[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