lima/gpir: Support branch instructions
authorConnor Abbott <cwabbott0@gmail.com>
Wed, 28 Aug 2019 08:57:35 +0000 (10:57 +0200)
committerConnor Abbott <cwabbott0@gmail.com>
Tue, 24 Sep 2019 06:35:47 +0000 (08:35 +0200)
Because branch conditions have to be in the pass slot, there is no
unconditional branch, and realistically the pass slot has to contain a
move when branching (there's nothing it does that would be useful for
operating on booleans, so we can't use it for anything when computing
the branch condition), we put the branch instruction in the pass slot
and at codegen time turn it into a move of the branch condition. This
means that it doesn't have to be special-cased like store instructions
are in the scheduler. Because of this decision we can remove the
half-implemented BRANCH codegen slot. Finally, we (ab)use the existing
schedule_first mechanism to make sure that branches are always last in
the basic block.

Reviewed-by: Vasily Khoruzhick <anarsoul@gmail.com>
src/gallium/drivers/lima/ir/gp/codegen.c
src/gallium/drivers/lima/ir/gp/gpir.h
src/gallium/drivers/lima/ir/gp/instr.c
src/gallium/drivers/lima/ir/gp/lower.c
src/gallium/drivers/lima/ir/gp/nir.c
src/gallium/drivers/lima/ir/gp/node.c

index 76e360b4fb1235afe44fc8d40b9c5a569b2982c1..113a8a0c0f34ca8458aeb4213d434220b2385767 100644 (file)
@@ -45,8 +45,6 @@ static gpir_codegen_src gpir_get_alu_input(gpir_node *parent, gpir_node *child)
          gpir_codegen_src_unused, gpir_codegen_src_p1_complex, gpir_codegen_src_unused },
       [GPIR_INSTR_SLOT_PASS] = {
          gpir_codegen_src_unused, gpir_codegen_src_p1_pass, gpir_codegen_src_p2_pass },
-      [GPIR_INSTR_SLOT_BRANCH] = {
-         gpir_codegen_src_unused, gpir_codegen_src_unused, gpir_codegen_src_unused },
 
       [GPIR_INSTR_SLOT_REG0_LOAD0] = {
          gpir_codegen_src_attrib_x, gpir_codegen_src_p1_attrib_x, gpir_codegen_src_unused },
@@ -418,6 +416,22 @@ static void gpir_codegen_pass_slot(gpir_codegen_instr *code, gpir_instr *instr)
       return;
    }
 
+   if (node->op == gpir_op_branch_cond) {
+      gpir_branch_node *branch = gpir_node_to_branch(node);
+
+      code->pass_op = gpir_codegen_pass_op_pass;
+      code->pass_src = gpir_get_alu_input(node, branch->cond);
+
+      /* Fill out branch information */
+      unsigned offset = branch->dest->instr_offset;
+      assert(offset < 0x200);
+      code->branch = true;
+      code->branch_target = offset & 0xff;
+      code->branch_target_lo = !(offset >> 8);
+      code->unknown_1 = 13;
+      return;
+   }
+
    gpir_alu_node *alu = gpir_node_to_alu(node);
    code->pass_src = gpir_get_alu_input(node, alu->children[0]);
 
@@ -434,16 +448,7 @@ static void gpir_codegen_pass_slot(gpir_codegen_instr *code, gpir_instr *instr)
    default:
       assert(0);
    }
-}
-
-static void gpir_codegen_branch_slot(gpir_codegen_instr *code, gpir_instr *instr)
-{
-   gpir_node *node = instr->slots[GPIR_INSTR_SLOT_BRANCH];
-
-   if (!node)
-      return;
 
-   assert(0);
 }
 
 static void gpir_codegen_reg0_slot(gpir_codegen_instr *code, gpir_instr *instr)
@@ -483,7 +488,7 @@ static gpir_codegen_store_src gpir_get_store_input(gpir_node *node)
       [GPIR_INSTR_SLOT_ADD1] = gpir_codegen_store_src_acc_1,
       [GPIR_INSTR_SLOT_COMPLEX] = gpir_codegen_store_src_complex,
       [GPIR_INSTR_SLOT_PASS] = gpir_codegen_store_src_pass,
-      [GPIR_INSTR_SLOT_BRANCH...GPIR_INSTR_SLOT_STORE3] = gpir_codegen_store_src_none,
+      [GPIR_INSTR_SLOT_REG0_LOAD0...GPIR_INSTR_SLOT_STORE3] = gpir_codegen_store_src_none,
    };
 
    gpir_store_node *store = gpir_node_to_store(node);
@@ -546,7 +551,6 @@ static void gpir_codegen(gpir_codegen_instr *code, gpir_instr *instr)
 
    gpir_codegen_complex_slot(code, instr);
    gpir_codegen_pass_slot(code, instr);
-   gpir_codegen_branch_slot(code, instr);
 
    gpir_codegen_reg0_slot(code, instr);
    gpir_codegen_reg1_slot(code, instr);
@@ -574,6 +578,7 @@ bool gpir_codegen_prog(gpir_compiler *comp)
 {
    int num_instr = 0;
    list_for_each_entry(gpir_block, block, &comp->block_list, list) {
+      block->instr_offset = num_instr;
       num_instr += list_length(&block->instr_list);
    }
 
index 24924c92567f39fb5602451b4a03a47db0b87948..6cbd406032ef5f2fa3cfe3cfdfe08d8649a79cef 100644 (file)
@@ -246,7 +246,6 @@ enum gpir_instr_slot {
    GPIR_INSTR_SLOT_ADD1,
    GPIR_INSTR_SLOT_PASS,
    GPIR_INSTR_SLOT_COMPLEX,
-   GPIR_INSTR_SLOT_BRANCH,
    GPIR_INSTR_SLOT_REG0_LOAD0,
    GPIR_INSTR_SLOT_REG0_LOAD1,
    GPIR_INSTR_SLOT_REG0_LOAD2,
@@ -347,6 +346,13 @@ typedef struct gpir_block {
    struct list_head instr_list;
    struct gpir_compiler *comp;
 
+   struct gpir_block *successors[2];
+   struct list_head predecessors;
+   struct list_head predecessors_node;
+
+   /* For codegen, the offset in the final program. */
+   unsigned instr_offset;
+
    /* for scheduler */
    union {
       struct {
@@ -361,6 +367,7 @@ typedef struct gpir_block {
 typedef struct {
    gpir_node node;
    gpir_block *dest;
+   gpir_node *cond;
 } gpir_branch_node;
 
 struct lima_vs_shader_state;
@@ -376,6 +383,9 @@ typedef struct gpir_compiler {
    /* array for searching ssa node */
    gpir_node **var_nodes;
 
+   /* gpir block for NIR block. */
+   gpir_block **blocks;
+
    /* for physical reg */
    struct list_head reg_list;
    int cur_reg;
@@ -433,6 +443,7 @@ static inline bool gpir_node_is_leaf(gpir_node *node)
 #define gpir_node_to_const(node) ((gpir_const_node *)(node))
 #define gpir_node_to_load(node) ((gpir_load_node *)(node))
 #define gpir_node_to_store(node) ((gpir_store_node *)(node))
+#define gpir_node_to_branch(node) ((gpir_branch_node *)(node))
 
 gpir_instr *gpir_instr_create(gpir_block *block);
 bool gpir_instr_try_insert_node(gpir_instr *instr, gpir_node *node);
index 45e9d81714359c7e81afc43985d4334e57b56201..5cfb7e34a0274fddb1c63efdd1230ce1a0fa738a 100644 (file)
@@ -535,7 +535,6 @@ void gpir_instr_print_prog(gpir_compiler *comp)
       [GPIR_INSTR_SLOT_REG0_LOAD3] = { 15, "load0" },
       [GPIR_INSTR_SLOT_REG1_LOAD3] = { 15, "load1" },
       [GPIR_INSTR_SLOT_MEM_LOAD3] = { 15, "load2" },
-      [GPIR_INSTR_SLOT_BRANCH] = { 4, "bnch" },
       [GPIR_INSTR_SLOT_STORE3] = { 15, "store" },
       [GPIR_INSTR_SLOT_COMPLEX] = { 4, "cmpl" },
       [GPIR_INSTR_SLOT_PASS] = { 4, "pass" },
index 6d6aa7e80ff010c421a0a687932fd3ffe5a7e325..eaeeeb8f1eb2a2e4f82c52fdeb3ed967c9b14564 100644 (file)
@@ -413,6 +413,26 @@ static bool gpir_lower_not(gpir_block *block, gpir_node *node)
    return true;
 }
 
+/* There is no unconditional branch instruction, so we have to lower it to a
+ * conditional branch with a condition of 1.0.
+ */
+
+static bool gpir_lower_branch_uncond(gpir_block *block, gpir_node *node)
+{
+   gpir_branch_node *branch = gpir_node_to_branch(node);
+
+   gpir_node *node_const = gpir_node_create(block, gpir_op_const);
+   gpir_const_node *c = gpir_node_to_const(node_const);
+
+   list_addtail(&c->node.list, &node->list);
+   c->value.f = 1.0f;
+   gpir_node_add_dep(&branch->node, &c->node, GPIR_DEP_INPUT);
+
+   branch->node.op = gpir_op_branch_cond;
+   branch->cond = node_const;
+
+   return true;
+}
 
 static bool (*gpir_pre_rsched_lower_funcs[gpir_op_num])(gpir_block *, gpir_node *) = {
    [gpir_op_not] = gpir_lower_not,
@@ -424,6 +444,7 @@ static bool (*gpir_pre_rsched_lower_funcs[gpir_op_num])(gpir_block *, gpir_node
    [gpir_op_eq] = gpir_lower_eq_ne,
    [gpir_op_ne] = gpir_lower_eq_ne,
    [gpir_op_abs] = gpir_lower_abs,
+   [gpir_op_branch_uncond] = gpir_lower_branch_uncond,
 };
 
 bool gpir_pre_rsched_lower_prog(gpir_compiler *comp)
index 6dcb4d88f02827a8b0536208edc60f088177ec77..e2dc939f1a0cc77799931ed44fa9d4343869f4d2 100644 (file)
@@ -272,8 +272,8 @@ static bool gpir_emit_tex(gpir_block *block, nir_instr *ni)
 
 static bool gpir_emit_jump(gpir_block *block, nir_instr *ni)
 {
-   gpir_error("nir_jump_instr not support\n");
-   return false;
+   /* Jumps are emitted at the end of the basic block, so do nothing. */
+   return true;
 }
 
 static bool (*gpir_emit_instr[nir_instr_type_phi])(gpir_block *, nir_instr *) = {
@@ -285,79 +285,61 @@ static bool (*gpir_emit_instr[nir_instr_type_phi])(gpir_block *, nir_instr *) =
    [nir_instr_type_jump]       = gpir_emit_jump,
 };
 
-static gpir_block *gpir_block_create(gpir_compiler *comp)
+static bool gpir_emit_function(gpir_compiler *comp, nir_function_impl *impl)
 {
-   gpir_block *block = ralloc(comp, gpir_block);
-   if (!block)
-      return NULL;
+   nir_index_blocks(impl);
+   comp->blocks = ralloc_array(comp, gpir_block *, impl->num_blocks);
 
-   list_inithead(&block->node_list);
-   list_inithead(&block->instr_list);
+   nir_foreach_block(block_nir, impl) {
+      gpir_block *block = ralloc(comp, gpir_block);
+      if (!block)
+         return false;
 
-   return block;
-}
+      list_inithead(&block->node_list);
+      list_inithead(&block->instr_list);
 
-static bool gpir_emit_block(gpir_compiler *comp, nir_block *nblock)
-{
-   gpir_block *block = gpir_block_create(comp);
-   if (!block)
-      return false;
+      list_addtail(&block->list, &comp->block_list);
+      block->comp = comp;
+      comp->blocks[block_nir->index] = block;
+   }
 
-   list_addtail(&block->list, &comp->block_list);
-   block->comp = comp;
+   nir_foreach_block(block_nir, impl) {
+      gpir_block *block = comp->blocks[block_nir->index];
+      nir_foreach_instr(instr, block_nir) {
+         assert(instr->type < nir_instr_type_phi);
+         if (!gpir_emit_instr[instr->type](block, instr))
+            return false;
+      }
 
-   nir_foreach_instr(instr, nblock) {
-      assert(instr->type < nir_instr_type_phi);
-      if (!gpir_emit_instr[instr->type](block, instr))
-         return false;
-   }
+      if (block_nir->successors[0] == impl->end_block)
+         block->successors[0] = NULL;
+      else
+         block->successors[0] = comp->blocks[block_nir->successors[0]->index];
+      block->successors[1] = NULL;
 
-   return true;
-}
+      if (block_nir->successors[1] != NULL) {
+         nir_if *nif = nir_cf_node_as_if(nir_cf_node_next(&block_nir->cf_node));
+         gpir_alu_node *cond = gpir_node_create(block, gpir_op_not);
+         list_addtail(&cond->node.list, &block->node_list);
+         cond->children[0] = gpir_node_find(block, &cond->node, &nif->condition, 0);
+         gpir_node_add_dep(&cond->node, cond->children[0], GPIR_DEP_INPUT);
 
-static bool gpir_emit_if(gpir_compiler *comp, nir_if *nif)
-{
-   gpir_error("if nir_cf_node not support\n");
-   return false;
-}
+         gpir_branch_node *branch = gpir_node_create(block, gpir_op_branch_cond);
+         list_addtail(&branch->node.list, &block->node_list);
 
-static bool gpir_emit_loop(gpir_compiler *comp, nir_loop *nloop)
-{
-   gpir_error("loop nir_cf_node not support\n");
-   return false;
-}
+         branch->dest = comp->blocks[block_nir->successors[1]->index];
+         block->successors[1] = branch->dest;
 
-static bool gpir_emit_function(gpir_compiler *comp, nir_function_impl *nfunc)
-{
-   gpir_error("function nir_cf_node not support\n");
-   return false;
-}
+         branch->cond = &cond->node;
+         gpir_node_add_dep(&branch->node, &cond->node, GPIR_DEP_INPUT);
 
-static bool gpir_emit_cf_list(gpir_compiler *comp, struct exec_list *list)
-{
-   foreach_list_typed(nir_cf_node, node, node, list) {
-      bool ret;
+         assert(block_nir->successors[0]->index == block_nir->index + 1);
+      } else if (block_nir->successors[0]->index != block_nir->index + 1) {
+         gpir_branch_node *branch = gpir_node_create(block, gpir_op_branch_uncond);
+         list_addtail(&branch->node.list, &block->node_list);
 
-      switch (node->type) {
-      case nir_cf_node_block:
-         ret = gpir_emit_block(comp, nir_cf_node_as_block(node));
-         break;
-      case nir_cf_node_if:
-         ret = gpir_emit_if(comp, nir_cf_node_as_if(node));
-         break;
-      case nir_cf_node_loop:
-         ret = gpir_emit_loop(comp, nir_cf_node_as_loop(node));
-         break;
-      case nir_cf_node_function:
-         ret = gpir_emit_function(comp, nir_cf_node_as_function(node));
-         break;
-      default:
-         gpir_error("unknown NIR node type %d\n", node->type);
-         return false;
+         branch->dest = comp->blocks[block_nir->successors[0]->index];
       }
-
-      if (!ret)
-         return false;
    }
 
    return true;
@@ -430,7 +412,7 @@ bool gpir_compile_nir(struct lima_vs_shader_state *prog, struct nir_shader *nir,
    comp->constant_base = nir->num_uniforms;
    prog->uniform_pending_offset = nir->num_uniforms * 16;
 
-   if (!gpir_emit_cf_list(comp, &func->body))
+   if (!gpir_emit_function(comp, func))
       goto err_out0;
 
    gpir_node_print_prog_seq(comp);
index 1bf9d806c30fe3e341d4d9fe7dcc4d57c01b89f4..e62512890b386a3a38370cdc5360a0672297c2d2 100644 (file)
@@ -246,6 +246,8 @@ const gpir_op_info gpir_op_infos[] = {
    [gpir_op_branch_cond] = {
       .name = "branch_cond",
       .type = gpir_node_type_branch,
+      .schedule_first = true,
+      .slots = (int []) { GPIR_INSTR_SLOT_PASS, GPIR_INSTR_SLOT_END },
    },
    [gpir_op_const] = {
       .name = "const",
@@ -380,6 +382,10 @@ void gpir_node_replace_child(gpir_node *parent, gpir_node *old_child,
       gpir_store_node *store = gpir_node_to_store(parent);
       if (store->child == old_child)
          store->child = new_child;
+   } else if (parent->type == gpir_node_type_branch) {
+      gpir_branch_node *branch = gpir_node_to_branch(parent);
+      if (branch->cond == old_child)
+         branch->cond = new_child;
    }
 }