[Mesa-dev] [PATCH v5 14/34] nvir/nir: implement CFG handling

Karol Herbst kherbst at redhat.com
Tue Feb 20 21:02:32 UTC 2018


Signed-off-by: Karol Herbst <kherbst at redhat.com>
---
 .../drivers/nouveau/codegen/nv50_ir_from_nir.cpp   | 255 ++++++++++++++++++++-
 1 file changed, 253 insertions(+), 2 deletions(-)

diff --git a/src/gallium/drivers/nouveau/codegen/nv50_ir_from_nir.cpp b/src/gallium/drivers/nouveau/codegen/nv50_ir_from_nir.cpp
index fd63642192..a6481b1380 100644
--- a/src/gallium/drivers/nouveau/codegen/nv50_ir_from_nir.cpp
+++ b/src/gallium/drivers/nouveau/codegen/nv50_ir_from_nir.cpp
@@ -49,10 +49,12 @@ public:
    typedef decltype(nir_ssa_def().index) NirSSADefIdx;
    typedef decltype(nir_ssa_def().bit_size) NirSSADefBitSize;
    typedef std::unordered_map<NirSSADefIdx, LValues> NirDefMap;
+   typedef std::unordered_map<decltype(nir_block().index), BasicBlock*> NirBlockMap;
 
    Converter(Program *, nir_shader *, nv50_ir_prog_info *);
 
    LValues& convert(nir_alu_dest *);
+   BasicBlock* convert(nir_block *);
    LValues& convert(nir_dest *);
    LValues& convert(nir_register *);
    LValues& convert(nir_ssa_def *);
@@ -76,6 +78,14 @@ public:
    Instruction *loadFrom(DataFile, uint8_t, DataType, Value *def, uint32_t base, uint8_t c, Value *indirect0 = nullptr, Value *indirect1 = nullptr, bool patch = false);
    void storeTo(nir_intrinsic_instr *, DataFile, operation, DataType, Value *src, uint8_t idx, uint8_t c, Value *indirect0 = nullptr, Value *indirect1 = nullptr);
 
+   bool visit(nir_block *);
+   bool visit(nir_cf_node *);
+   bool visit(nir_function *);
+   bool visit(nir_if *);
+   bool visit(nir_instr *);
+   bool visit(nir_jump_instr *);
+   bool visit(nir_loop *);
+
    bool run();
 
    bool isFloatType(nir_alu_type);
@@ -93,11 +103,34 @@ private:
 
    NirDefMap ssaDefs;
    NirDefMap regDefs;
+   NirBlockMap blocks;
+   unsigned int curLoopDepth;
+
+   BasicBlock *exit;
+
+   union {
+      struct {
+         Value *position;
+      } fp;
+   };
 };
 
 Converter::Converter(Program *prog, nir_shader *nir, nv50_ir_prog_info *info)
    : ConverterCommon(prog, info),
-     nir(nir) {}
+     nir(nir),
+     curLoopDepth(0) {}
+
+BasicBlock *
+Converter::convert(nir_block *block)
+{
+   NirBlockMap::iterator it = blocks.find(block->index);
+   if (it != blocks.end())
+      return (*it).second;
+
+   BasicBlock *bb = new BasicBlock(func);
+   blocks[block->index] = bb;
+   return bb;
+}
 
 bool
 Converter::isFloatType(nir_alu_type type)
@@ -1003,6 +1036,219 @@ Converter::parseNIR()
    return true;
 }
 
+bool
+Converter::visit(nir_function *function)
+{
+   // we only support emiting the main function for now
+   assert(!strcmp(function->name, "main"));
+   assert(function->impl);
+
+   // usually the blocks will set everything up, but main is special
+   BasicBlock *entry = new BasicBlock(prog->main);
+   exit = new BasicBlock(prog->main);
+   blocks[nir_start_block(function->impl)->index] = entry;
+   prog->main->setEntry(entry);
+   prog->main->setExit(exit);
+
+   setPosition(entry, true);
+
+   switch (prog->getType()) {
+   case Program::TYPE_TESSELLATION_CONTROL:
+      outBase = mkOp2v(
+         OP_SUB, TYPE_U32, getSSA(),
+         mkOp1v(OP_RDSV, TYPE_U32, getSSA(), mkSysVal(SV_LANEID, 0)),
+         mkOp1v(OP_RDSV, TYPE_U32, getSSA(), mkSysVal(SV_INVOCATION_ID, 0)));
+      break;
+   case Program::TYPE_FRAGMENT: {
+      Symbol *sv = mkSysVal(SV_POSITION, 3);
+      fragCoord[3] = mkOp1v(OP_RDSV, TYPE_F32, getSSA(), sv);
+      fp.position = mkOp1v(OP_RCP, TYPE_F32, fragCoord[3], fragCoord[3]);
+      break;
+   }
+   default:
+      break;
+   }
+
+   nir_index_ssa_defs(function->impl);
+   foreach_list_typed(nir_cf_node, node, node, &function->impl->body) {
+      if (!visit(node))
+         return false;
+   }
+
+   bb->cfg.attach(&exit->cfg, Graph::Edge::TREE);
+   setPosition(exit, true);
+
+   // TODO: for non main function this needs to be a OP_RETURN
+   mkOp(OP_EXIT, TYPE_NONE, NULL)->terminator = 1;
+   return true;
+}
+
+bool
+Converter::visit(nir_cf_node *node)
+{
+   switch (node->type) {
+   case nir_cf_node_block:
+      if (!visit(nir_cf_node_as_block(node)))
+         return false;
+      break;
+   case nir_cf_node_if:
+      if (!visit(nir_cf_node_as_if(node)))
+         return false;
+      break;
+   case nir_cf_node_loop:
+      if (!visit(nir_cf_node_as_loop(node)))
+         return false;
+      break;
+   default:
+      ERROR("unknown nir_cf_node type %u\n", node->type);
+      return false;
+   }
+   return true;
+}
+
+bool
+Converter::visit(nir_block *block)
+{
+   BasicBlock *bb = convert(block);
+
+   setPosition(bb, true);
+   nir_foreach_instr(insn, block) {
+      if (!visit(insn))
+         return false;
+   }
+   return true;
+}
+
+bool
+Converter::visit(nir_if *nif)
+{
+   DataType sType = getSType(nif->condition, false, false);
+   Value *src = getSrc(&nif->condition, 0);
+
+   nir_block *lastThen = nir_if_last_then_block(nif);
+   nir_block *lastElse = nir_if_last_else_block(nif);
+
+   assert(!lastThen->successors[1]);
+   assert(!lastElse->successors[1]);
+
+   BasicBlock *ifBB = convert(nir_if_first_then_block(nif));
+   BasicBlock *elseBB = convert(nir_if_first_else_block(nif));
+
+   bb->cfg.attach(&ifBB->cfg, Graph::Edge::TREE);
+   bb->cfg.attach(&elseBB->cfg, Graph::Edge::TREE);
+
+   // we only insert joinats, if both nodes end up at the end of the if again.
+   // the reason for this to not happens are breaks/continues/ret/... which
+   // have their own handling
+   if (lastThen->successors[0] == lastElse->successors[0])
+      bb->joinAt = mkFlow(OP_JOINAT, convert(lastThen->successors[0]), CC_ALWAYS, nullptr);
+
+   mkFlow(OP_BRA, elseBB, CC_EQ, src)->setType(sType);
+
+   foreach_list_typed(nir_cf_node, node, node, &nif->then_list) {
+      if (!visit(node))
+         return false;
+   }
+   setPosition(convert(lastThen), true);
+   if (!bb->getExit() || !bb->getExit()->asFlow() || bb->getExit()->asFlow()->op == OP_JOIN) {
+      BasicBlock *tailBB = convert(lastThen->successors[0]);
+      mkFlow(OP_BRA, tailBB, CC_ALWAYS, nullptr);
+      bb->cfg.attach(&tailBB->cfg, Graph::Edge::FORWARD);
+   }
+
+   foreach_list_typed(nir_cf_node, node, node, &nif->else_list) {
+      if (!visit(node))
+         return false;
+   }
+   setPosition(convert(lastElse), true);
+   if (!bb->getExit() || !bb->getExit()->asFlow() || bb->getExit()->asFlow()->op == OP_JOIN) {
+      BasicBlock *tailBB = convert(lastElse->successors[0]);
+      mkFlow(OP_BRA, tailBB, CC_ALWAYS, nullptr);
+      bb->cfg.attach(&tailBB->cfg, Graph::Edge::FORWARD);
+   }
+
+   if (lastThen->successors[0] == lastElse->successors[0]) {
+      setPosition(convert(lastThen->successors[0]), true);
+      mkFlow(OP_JOIN, nullptr, CC_ALWAYS, nullptr)->fixed = 1;
+   }
+
+   return true;
+}
+
+bool
+Converter::visit(nir_loop *loop)
+{
+   curLoopDepth += 1;
+   func->loopNestingBound = std::max(func->loopNestingBound, curLoopDepth);
+
+   BasicBlock *loopBB = convert(nir_loop_first_block(loop));
+   BasicBlock *tailBB = convert(nir_cf_node_as_block(nir_cf_node_next(&loop->cf_node)));
+   bb->cfg.attach(&loopBB->cfg, Graph::Edge::TREE);
+
+   mkFlow(OP_PREBREAK, tailBB, CC_ALWAYS, NULL);
+   setPosition(loopBB, false);
+   mkFlow(OP_PRECONT, loopBB, CC_ALWAYS, NULL);
+
+   foreach_list_typed(nir_cf_node, node, node, &loop->body) {
+      if (!visit(node))
+         return false;
+   }
+   Instruction *insn = bb->getExit();
+   if (!insn || !insn->asFlow()) {
+      mkFlow(OP_CONT, loopBB, CC_ALWAYS, nullptr);
+      bb->cfg.attach(&loopBB->cfg, Graph::Edge::BACK);
+   } else if (insn && insn->op == OP_BRA && !insn->getPredicate() && tailBB->cfg.incidentCount() == 0) {
+      // RA doesn't like having blocks around with no incident edge, so we create a fake one to make it happy
+      bb->cfg.attach(&tailBB->cfg, Graph::Edge::TREE);
+   }
+
+   curLoopDepth -= 1;
+
+   return true;
+}
+
+bool
+Converter::visit(nir_instr *insn)
+{
+   switch (insn->type) {
+   case nir_instr_type_jump:
+      if (!visit(nir_instr_as_jump(insn)))
+         return false;
+      break;
+   default:
+      ERROR("unknown nir_instr type %u\n", insn->type);
+      return false;
+   }
+   return true;
+}
+
+bool
+Converter::visit(nir_jump_instr *insn)
+{
+   switch (insn->type) {
+   case nir_jump_return:
+      // TODO: this only works in the main function
+      mkFlow(OP_BRA, exit, CC_ALWAYS, NULL);
+      bb->cfg.attach(&exit->cfg, Graph::Edge::CROSS);
+      break;
+   case nir_jump_break:
+   case nir_jump_continue: {
+      bool isBreak = insn->type == nir_jump_break;
+      nir_block *block = insn->instr.block;
+      assert(!block->successors[1]);
+      BasicBlock *target = convert(block->successors[0]);
+      mkFlow(isBreak ? OP_BREAK : OP_CONT, target, CC_ALWAYS, NULL);
+      bb->cfg.attach(&target->cfg, isBreak ? Graph::Edge::CROSS : Graph::Edge::BACK);
+      break;
+   }
+   default:
+      ERROR("unknown nir_jump_type %u\n", insn->type);
+      return false;
+   }
+
+   return true;
+}
+
 bool
 Converter::run()
 {
@@ -1050,7 +1296,12 @@ Converter::run()
       return false;
    }
 
-   return false;
+   nir_foreach_function(function, nir) {
+      if (!visit(function))
+         return false;
+   }
+
+   return true;
 }
 
 } // unnamed namespace
-- 
2.14.3



More information about the mesa-dev mailing list