From a0e1fcc093e0bb1265d8a6977f96dff51d9f5318 Mon Sep 17 00:00:00 2001 From: Sergei Barannikov Date: Thu, 26 Dec 2024 08:40:47 +0300 Subject: [PATCH] [TableGen][GISel] Refactor node renderers emission (#121071) Split importExplicitUseRenderer into several smaller functions and add a bunch of TODOs and FIXMEs. This is an NFCI change to simplify review of future functional changes. Pull Request: https://github.com/llvm/llvm-project/pull/121071 --- llvm/utils/TableGen/GlobalISelEmitter.cpp | 331 +++++++++++++--------- 1 file changed, 195 insertions(+), 136 deletions(-) diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp index 9c945edfafe3..0b910096b052 100644 --- a/llvm/utils/TableGen/GlobalISelEmitter.cpp +++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -413,10 +413,24 @@ class GlobalISelEmitter final : public GlobalISelMatchTableExecutorEmitter { importExplicitUseRenderers(action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder, const TreePatternNode &Dst) const; - Expected - importExplicitUseRenderer(action_iterator InsertPt, RuleMatcher &Rule, - BuildMIAction &DstMIBuilder, - const TreePatternNode &Dst) const; + + Error importNamedNodeRenderer(RuleMatcher &M, BuildMIAction &MIBuilder, + const TreePatternNode &N) const; + + Error importLeafNodeRenderer(RuleMatcher &M, BuildMIAction &MIBuilder, + const TreePatternNode &N) const; + + Error importXFormNodeRenderer(RuleMatcher &M, BuildMIAction &MIBuilder, + const TreePatternNode &N) const; + + Error importInstructionNodeRenderer(RuleMatcher &M, BuildMIAction &MIBuilder, + const TreePatternNode &N, + action_iterator &InsertPt) const; + + Error importNodeRenderer(RuleMatcher &M, BuildMIAction &MIBuilder, + const TreePatternNode &N, + action_iterator &InsertPt) const; + Error importDefaultOperandRenderers(action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder, const DAGDefaultOperand &DefaultOp) const; @@ -1190,159 +1204,207 @@ Error GlobalISelEmitter::importChildMatcher( return failedImport("Src pattern child is an unsupported kind"); } -Expected GlobalISelEmitter::importExplicitUseRenderer( - action_iterator InsertPt, RuleMatcher &Rule, BuildMIAction &DstMIBuilder, - const TreePatternNode &Dst) const { +// Equivalent of MatcherGen::EmitResultOfNamedOperand. +Error GlobalISelEmitter::importNamedNodeRenderer( + RuleMatcher &M, BuildMIAction &MIBuilder, const TreePatternNode &N) const { + StringRef NodeName = N.getName(); - const auto &SubOperand = Rule.getComplexSubOperand(Dst.getName()); - if (SubOperand) { - DstMIBuilder.addRenderer( - *std::get<0>(*SubOperand), Dst.getName(), std::get<1>(*SubOperand), - std::get<2>(*SubOperand)); - return InsertPt; + if (auto SubOperand = M.getComplexSubOperand(NodeName)) { + auto [ComplexPatternRec, RendererID, SubOperandIdx] = *SubOperand; + MIBuilder.addRenderer( + *ComplexPatternRec, NodeName, RendererID, SubOperandIdx); + return Error::success(); } - if (!Dst.isLeaf()) { - if (Dst.getOperator()->isSubClassOf("SDNodeXForm")) { - auto &Child = Dst.getChild(0); - auto I = SDNodeXFormEquivs.find(Dst.getOperator()); - if (I != SDNodeXFormEquivs.end()) { - const Record *XFormOpc = Dst.getOperator()->getValueAsDef("Opcode"); - if (XFormOpc->getName() == "timm") { - // If this is a TargetConstant, there won't be a corresponding - // instruction to transform. Instead, this will refer directly to an - // operand in an instruction's operand list. - DstMIBuilder.addRenderer(*I->second, - Child.getName()); - } else { - DstMIBuilder.addRenderer(*I->second, Child.getName()); - } - - return InsertPt; - } - return failedImport("SDNodeXForm " + Child.getName() + - " has no custom renderer"); - } + if (!N.isLeaf()) { + StringRef OperatorName = N.getOperator()->getName(); - // We accept 'bb' here. It's an operator because BasicBlockSDNode isn't - // inline, but in MI it's just another operand. - if (Dst.getOperator()->getName() == "bb") { - DstMIBuilder.addRenderer(Dst.getName()); - return InsertPt; + if (OperatorName == "imm") { + MIBuilder.addRenderer(NodeName); + return Error::success(); } - // Similarly, imm is an operator in TreePatternNode's view but must be - // rendered as operands. - // FIXME: The target should be able to choose sign-extended when appropriate - // (e.g. on Mips). - if (Dst.getOperator()->getName() == "timm") { - DstMIBuilder.addRenderer(Dst.getName()); - return InsertPt; - } - if (Dst.getOperator()->getName() == "tframeindex") { - DstMIBuilder.addRenderer(Dst.getName()); - return InsertPt; - } - if (Dst.getOperator()->getName() == "imm") { - DstMIBuilder.addRenderer(Dst.getName()); - return InsertPt; - } - if (Dst.getOperator()->getName() == "fpimm") { - DstMIBuilder.addRenderer(Dst.getName()); - return InsertPt; + if (OperatorName == "fpimm") { + MIBuilder.addRenderer(NodeName); + return Error::success(); } - if (Dst.getOperator()->isSubClassOf("Instruction")) { - auto OpTy = getInstResultType(Dst, Target); - if (!OpTy) - return OpTy.takeError(); - - unsigned TempRegID = Rule.allocateTempRegID(); - InsertPt = - Rule.insertAction(InsertPt, *OpTy, TempRegID); - DstMIBuilder.addRenderer(TempRegID); - - auto InsertPtOrError = createAndImportSubInstructionRenderer( - ++InsertPt, Rule, Dst, TempRegID); - if (auto Error = InsertPtOrError.takeError()) - return std::move(Error); - return InsertPtOrError.get(); + // TODO: 'imm' and 'fpimm' are the only nodes that need special treatment. + // Remove this check and add CopyRenderer unconditionally for other nodes. + if (OperatorName == "bb" || OperatorName == "timm" || + OperatorName == "tframeindex") { + MIBuilder.addRenderer(NodeName); + return Error::success(); } - return failedImport("Dst pattern child isn't a leaf node or an MBB" + - llvm::to_string(Dst)); - } - - // It could be a specific immediate in which case we should just check for - // that immediate. - if (const IntInit *ChildIntInit = dyn_cast(Dst.getLeafValue())) { - DstMIBuilder.addRenderer(ChildIntInit->getValue()); - return InsertPt; + return failedImport("node has unsupported operator " + to_string(N)); } - // Otherwise, we're looking for a bog-standard RegisterClass operand. - if (auto *ChildDefInit = dyn_cast(Dst.getLeafValue())) { - auto *ChildRec = ChildDefInit->getDef(); + if (const auto *DI = dyn_cast(N.getLeafValue())) { + const Record *R = DI->getDef(); - ArrayRef ChildTypes = Dst.getExtTypes(); - if (ChildTypes.size() != 1) - return failedImport("Dst pattern child has multiple results"); + if (N.getNumResults() != 1) + return failedImport("node does not have one result " + to_string(N)); std::optional OpTyOrNone; + ArrayRef ChildTypes = N.getExtTypes(); if (ChildTypes.front().isMachineValueType()) OpTyOrNone = MVTToLLT(ChildTypes.front().getMachineValueType().SimpleTy); + + // TODO: Remove this check. Types in the destination DAG should not matter. if (!OpTyOrNone) - return failedImport("Dst operand has an unsupported type"); + return failedImport("node has unsupported type " + to_string(N)); - if (ChildRec->isSubClassOf("Register")) { - DstMIBuilder.addRenderer(Target, ChildRec); - return InsertPt; - } + if (R->isSubClassOf("ComplexPattern")) { + auto I = ComplexPatternEquivs.find(R); + if (I == ComplexPatternEquivs.end()) + return failedImport("ComplexPattern " + R->getName() + + " does not have GISel equivalent"); - if (ChildRec->isSubClassOf("RegisterClass") || - ChildRec->isSubClassOf("RegisterOperand") || - ChildRec->isSubClassOf("ValueType")) { - if (ChildRec->isSubClassOf("RegisterOperand") && - !ChildRec->isValueUnset("GIZeroRegister")) { - DstMIBuilder.addRenderer( - Dst.getName(), ChildRec->getValueAsDef("GIZeroRegister")); - return InsertPt; - } + const OperandMatcher &OM = M.getOperandMatcher(NodeName); + MIBuilder.addRenderer( + *I->second, NodeName, OM.getAllocatedTemporariesBaseID()); + return Error::success(); + } - DstMIBuilder.addRenderer(Dst.getName()); - return InsertPt; + if (R->isSubClassOf("RegisterOperand") && + !R->isValueUnset("GIZeroRegister")) { + MIBuilder.addRenderer( + NodeName, R->getValueAsDef("GIZeroRegister")); + return Error::success(); } - if (ChildRec->isSubClassOf("SubRegIndex")) { - CodeGenSubRegIndex *SubIdx = CGRegs.getSubRegIdx(ChildRec); - DstMIBuilder.addRenderer(SubIdx->EnumValue); - return InsertPt; + // TODO: All special cases are handled above. Remove this check and add + // CopyRenderer unconditionally. + if (R->isSubClassOf("RegisterClass") || + R->isSubClassOf("RegisterOperand") || R->isSubClassOf("ValueType")) { + MIBuilder.addRenderer(NodeName); + return Error::success(); } + } - if (ChildRec->isSubClassOf("ComplexPattern")) { - const auto &ComplexPattern = ComplexPatternEquivs.find(ChildRec); - if (ComplexPattern == ComplexPatternEquivs.end()) - return failedImport( - "SelectionDAG ComplexPattern not mapped to GlobalISel"); + // TODO: Change this to assert and move to the beginning of the function. + if (!M.hasOperand(NodeName)) + return failedImport("could not find node $" + NodeName + + " in the source DAG"); - const OperandMatcher &OM = Rule.getOperandMatcher(Dst.getName()); - DstMIBuilder.addRenderer( - *ComplexPattern->second, Dst.getName(), - OM.getAllocatedTemporariesBaseID()); - return InsertPt; + // TODO: Remove this check and add CopyRenderer unconditionally. + // TODO: Handle nodes with multiple results (provided they can reach here). + if (isa(N.getLeafValue())) { + MIBuilder.addRenderer(NodeName); + return Error::success(); + } + + return failedImport("unsupported node " + to_string(N)); +} + +// Equivalent of MatcherGen::EmitResultLeafAsOperand. +Error GlobalISelEmitter::importLeafNodeRenderer( + RuleMatcher &M, BuildMIAction &MIBuilder, const TreePatternNode &N) const { + if (const auto *II = dyn_cast(N.getLeafValue())) { + MIBuilder.addRenderer(II->getValue()); + return Error::success(); + } + + if (const auto *DI = dyn_cast(N.getLeafValue())) { + const Record *R = DI->getDef(); + + if (R->isSubClassOf("Register")) { + MIBuilder.addRenderer(Target, R); + return Error::success(); } - return failedImport( - "Dst pattern child def is an unsupported tablegen class"); + if (R->isSubClassOf("SubRegIndex")) { + const CodeGenSubRegIndex *SubRegIndex = CGRegs.getSubRegIdx(R); + MIBuilder.addRenderer(SubRegIndex->EnumValue); + return Error::success(); + } + + // There are also RegisterClass / RegisterOperand operands of REG_SEQUENCE / + // COPY_TO_REGCLASS, but these instructions are currently handled elsewhere. } - // Handle the case where the MVT/register class is omitted in the dest pattern - // but MVT exists in the source pattern. - if (isa(Dst.getLeafValue()) && Rule.hasOperand(Dst.getName())) { - DstMIBuilder.addRenderer(Dst.getName()); - return InsertPt; + return failedImport("unrecognized node " + to_string(N)); +} + +// Equivalent of MatcherGen::EmitResultSDNodeXFormAsOperand. +Error GlobalISelEmitter::importXFormNodeRenderer( + RuleMatcher &M, BuildMIAction &MIBuilder, const TreePatternNode &N) const { + const Record *XFormRec = N.getOperator(); + auto I = SDNodeXFormEquivs.find(XFormRec); + if (I == SDNodeXFormEquivs.end()) + return failedImport("SDNodeXForm " + XFormRec->getName() + + " does not have GISel equivalent"); + + // TODO: Fail to import if GISDNodeXForm does not have RendererFn. + // This currently results in a fatal error in emitRenderOpcodes. + const Record *XFormEquivRec = I->second; + + // The node to apply the transformation function to. + // FIXME: The node may not have a name and may be a leaf. It should be + // rendered first, like any other nodes. This may or may not require + // introducing a temporary register, and we can't tell that without + // inspecting the node (possibly recursively). This is a general drawback + // of appending renderers directly to BuildMIAction. + const TreePatternNode &Node = N.getChild(0); + StringRef NodeName = Node.getName(); + + const Record *XFormOpc = CGP.getSDNodeTransform(XFormRec).first; + if (XFormOpc->getName() == "timm") { + // If this is a TargetConstant, there won't be a corresponding + // instruction to transform. Instead, this will refer directly to an + // operand in an instruction's operand list. + MIBuilder.addRenderer(*XFormEquivRec, NodeName); + } else { + MIBuilder.addRenderer(*XFormEquivRec, NodeName); } - return failedImport("Dst pattern child is an unsupported kind"); + + return Error::success(); +} + +// Equivalent of MatcherGen::EmitResultInstructionAsOperand. +Error GlobalISelEmitter::importInstructionNodeRenderer( + RuleMatcher &M, BuildMIAction &MIBuilder, const TreePatternNode &N, + action_iterator &InsertPt) const { + Expected OpTy = getInstResultType(N, Target); + if (!OpTy) + return OpTy.takeError(); + + // TODO: See the comment in importXFormNodeRenderer. We rely on the node + // requiring a temporary register, which prevents us from using this + // function on the root of the destination DAG. + unsigned TempRegID = M.allocateTempRegID(); + InsertPt = M.insertAction(InsertPt, *OpTy, TempRegID); + MIBuilder.addRenderer(TempRegID); + + auto InsertPtOrError = + createAndImportSubInstructionRenderer(++InsertPt, M, N, TempRegID); + if (!InsertPtOrError) + return InsertPtOrError.takeError(); + + InsertPt = *InsertPtOrError; + return Error::success(); +} + +// Equivalent of MatcherGen::EmitResultOperand. +Error GlobalISelEmitter::importNodeRenderer(RuleMatcher &M, + BuildMIAction &MIBuilder, + const TreePatternNode &N, + action_iterator &InsertPt) const { + if (N.hasName()) + return importNamedNodeRenderer(M, MIBuilder, N); + + if (N.isLeaf()) + return importLeafNodeRenderer(M, MIBuilder, N); + + if (N.getOperator()->isSubClassOf("SDNodeXForm")) + return importXFormNodeRenderer(M, MIBuilder, N); + + if (N.getOperator()->isSubClassOf("Instruction")) + return importInstructionNodeRenderer(M, MIBuilder, N, InsertPt); + + // Should not reach here. + return failedImport("unrecognized node " + llvm::to_string(N)); } /// Generates code that builds the resulting instruction(s) from the destination @@ -1597,11 +1659,9 @@ Expected GlobalISelEmitter::importExplicitUseRenderers( dyn_cast(SubRegChild.getLeafValue())) { CodeGenSubRegIndex *SubIdx = CGRegs.getSubRegIdx(SubRegInit->getDef()); - auto InsertPtOrError = - importExplicitUseRenderer(InsertPt, M, DstMIBuilder, ValChild); - if (auto Error = InsertPtOrError.takeError()) - return std::move(Error); - InsertPt = InsertPtOrError.get(); + if (Error Err = importNodeRenderer(M, DstMIBuilder, ValChild, InsertPt)) + return Err; + DstMIBuilder.addRenderer(SubIdx); } } @@ -1666,11 +1726,10 @@ Expected GlobalISelEmitter::importExplicitUseRenderers( continue; } - auto InsertPtOrError = importExplicitUseRenderer(InsertPt, M, DstMIBuilder, - Dst.getChild(Child)); - if (auto Error = InsertPtOrError.takeError()) - return std::move(Error); - InsertPt = InsertPtOrError.get(); + if (Error Err = + importNodeRenderer(M, DstMIBuilder, Dst.getChild(Child), InsertPt)) + return Err; + ++Child; }