[llvm] [WIP][TableGen][GISel] Learn to import patterns with optional/physreg defs (PR #120343)
Sergei Barannikov via llvm-commits
llvm-commits at lists.llvm.org
Wed Dec 18 12:02:37 PST 2024
https://github.com/s-barannikov updated https://github.com/llvm/llvm-project/pull/120343
>From 94a13d9b0e90b966137670cc430b6a23b463102e Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Wed, 18 Dec 2024 16:47:10 +0300
Subject: [PATCH 1/3] [TableGen][GISel] Improve dead register handling
A dead implicit def wasn't marked as dead if it is also an implicit use.
The new approach should also be more straightforward and simplifies
future changes for supporting optional defs and physical register defs.
---
.../select-intrinsic-x86-flags-read-u32.mir | 2 +-
.../GlobalISelEmitter-nested-subregs.td | 2 +-
.../TableGen/GlobalISelEmitterRegSequence.td | 2 +-
llvm/test/TableGen/GlobalISelEmitterSubreg.td | 8 +-
llvm/utils/TableGen/GlobalISelEmitter.cpp | 77 +++++++------------
5 files changed, 35 insertions(+), 56 deletions(-)
diff --git a/llvm/test/CodeGen/X86/GlobalISel/select-intrinsic-x86-flags-read-u32.mir b/llvm/test/CodeGen/X86/GlobalISel/select-intrinsic-x86-flags-read-u32.mir
index 332ec2240c5b60..3d1857a274b4b2 100644
--- a/llvm/test/CodeGen/X86/GlobalISel/select-intrinsic-x86-flags-read-u32.mir
+++ b/llvm/test/CodeGen/X86/GlobalISel/select-intrinsic-x86-flags-read-u32.mir
@@ -9,7 +9,7 @@
define void @read_flags() { ret void }
; CHECK-LABEL: name: read_flags
; CHECK: bb.0:
- ; CHECK: [[RDFLAGS32_:%[0-9]+]]:gr32 = RDFLAGS32 implicit-def $esp, implicit $esp
+ ; CHECK: [[RDFLAGS32_:%[0-9]+]]:gr32 = RDFLAGS32 implicit-def dead $esp, implicit $esp
; CHECK: $eax = COPY [[RDFLAGS32_]]
...
diff --git a/llvm/test/TableGen/GlobalISelEmitter-nested-subregs.td b/llvm/test/TableGen/GlobalISelEmitter-nested-subregs.td
index 1fdb973c1f1ec7..79e55ef2e8b8ce 100644
--- a/llvm/test/TableGen/GlobalISelEmitter-nested-subregs.td
+++ b/llvm/test/TableGen/GlobalISelEmitter-nested-subregs.td
@@ -38,11 +38,11 @@ def A0 : RegisterClass<"MyTarget", [i32], 32, (add a0)>;
// CHECK-NEXT: // MIs[0] src
// CHECK-NEXT: GIM_RootCheckType, /*Op*/1, /*Type*/GILLT_s8,
// CHECK-NEXT: // (anyext:{ *:[i16] } i8:{ *:[i8] }:$src) => (EXTRACT_SUBREG:{ *:[i16] } (INSERT_SUBREG:{ *:[i32] } (IMPLICIT_DEF:{ *:[i32] }), A0b:{ *:[i8] }:$src, lo8:{ *:[i32] }), lo16:{ *:[i32] })
-// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32,
// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/1, /*TypeID*/GILLT_s32,
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/2, /*Opcode*/GIMT_Encode2(TargetOpcode::IMPLICIT_DEF),
// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/2, /*TempRegID*/1, /*TempRegFlags*/GIMT_Encode2(RegState::Define),
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/2,
+// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32,
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(TargetOpcode::INSERT_SUBREG),
// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/GIMT_Encode2(RegState::Define),
// CHECK-NEXT: GIR_AddSimpleTempRegister, /*InsnID*/1, /*TempRegID*/1,
diff --git a/llvm/test/TableGen/GlobalISelEmitterRegSequence.td b/llvm/test/TableGen/GlobalISelEmitterRegSequence.td
index 3829070b28efeb..69f82eac49c161 100644
--- a/llvm/test/TableGen/GlobalISelEmitterRegSequence.td
+++ b/llvm/test/TableGen/GlobalISelEmitterRegSequence.td
@@ -39,12 +39,12 @@ def SUBSOME_INSN : I<(outs SRegs:$dst), (ins SOP:$src), []>;
// CHECK-NEXT: GIM_RootCheckType, /*Op*/1, /*Type*/GILLT_s16,
// CHECK-NEXT: GIM_RootCheckRegBankForClass, /*Op*/1, /*RC*/GIMT_Encode2(Test::SRegsRegClassID),
// CHECK-NEXT: // (sext:{ *:[i32] } SOP:{ *:[i16] }:$src) => (REG_SEQUENCE:{ *:[i32] } DRegs:{ *:[i32] }, (SUBSOME_INSN:{ *:[i16] } SOP:{ *:[i16] }:$src), sub0:{ *:[i32] }, (SUBSOME_INSN:{ *:[i16] } SOP:{ *:[i16] }:$src), sub1:{ *:[i32] })
-// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s16,
// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/1, /*TypeID*/GILLT_s16,
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/2, /*Opcode*/GIMT_Encode2(MyTarget::SUBSOME_INSN),
// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/2, /*TempRegID*/1, /*TempRegFlags*/GIMT_Encode2(RegState::Define),
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/2, /*OldInsnID*/0, /*OpIdx*/1, // src
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/2,
+// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s16,
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(MyTarget::SUBSOME_INSN),
// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/GIMT_Encode2(RegState::Define),
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/0, /*OpIdx*/1, // src
diff --git a/llvm/test/TableGen/GlobalISelEmitterSubreg.td b/llvm/test/TableGen/GlobalISelEmitterSubreg.td
index 8df3238f6cc21e..08e690f3e894de 100644
--- a/llvm/test/TableGen/GlobalISelEmitterSubreg.td
+++ b/llvm/test/TableGen/GlobalISelEmitterSubreg.td
@@ -59,13 +59,13 @@ def : Pat<(sub (complex DOP:$src1, DOP:$src2), 77),
(SOME_INSN2 (EXTRACT_SUBREG DOP:$src1, sub0),
(EXTRACT_SUBREG DOP:$src2, sub1))>;
// CHECK-LABEL: // (sub:{ *:[i32] } (complex:{ *:[i32] } DOP:{ *:[i32] }:$src1, DOP:{ *:[i32] }:$src2), 77:{ *:[i32] }) => (SOME_INSN2:{ *:[i32] } (EXTRACT_SUBREG:{ *:[i32] } DOP:{ *:[i32] }:$src1, sub0:{ *:[i32] }), (EXTRACT_SUBREG:{ *:[i32] } DOP:{ *:[i32] }:$src2, sub1:{ *:[i32] }))
-// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32,
// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/1, /*TypeID*/GILLT_s32,
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/2, /*Opcode*/GIMT_Encode2(TargetOpcode::COPY),
// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/2, /*TempRegID*/1, /*TempRegFlags*/GIMT_Encode2(RegState::Define),
// CHECK-NEXT: GIR_ComplexSubOperandSubRegRenderer, /*InsnID*/2, /*RendererID*/GIMT_Encode2(0), /*SubOperand*/1, /*SubRegIdx*/GIMT_Encode2(2), // src2
// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/2, /*Op*/0, GIMT_Encode2(Test::SRegsRegClassID),
// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/2, /*Op*/1, GIMT_Encode2(Test::DRegsRegClassID),
+// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32,
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(TargetOpcode::COPY),
// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/GIMT_Encode2(RegState::Define),
// CHECK-NEXT: GIR_ComplexSubOperandSubRegRenderer, /*InsnID*/1, /*RendererID*/GIMT_Encode2(0), /*SubOperand*/0, /*SubRegIdx*/GIMT_Encode2(1), // src1
@@ -103,11 +103,11 @@ def : Pat<(i32 (anyext i16:$src)), (INSERT_SUBREG (i32 (IMPLICIT_DEF)), SOP:$src
// instruction.
def : Pat<(i32 (anyext i16:$src)), (SOME_INSN (INSERT_SUBREG (i32 (IMPLICIT_DEF)), SOP:$src, sub0))>;
// CHECK-LABEL: (anyext:{ *:[i32] } i16:{ *:[i16] }:$src) => (SOME_INSN:{ *:[i32] } (INSERT_SUBREG:{ *:[i32] } (IMPLICIT_DEF:{ *:[i32] }), SOP:{ *:[i16] }:$src, sub0:{ *:[i32] }))
-// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32,
// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/1, /*TypeID*/GILLT_s32,
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/2, /*Opcode*/GIMT_Encode2(TargetOpcode::IMPLICIT_DEF),
// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/2, /*TempRegID*/1, /*TempRegFlags*/GIMT_Encode2(RegState::Define),
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/2,
+// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32,
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(TargetOpcode::INSERT_SUBREG),
// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/GIMT_Encode2(RegState::Define),
// CHECK-NEXT: GIR_AddSimpleTempRegister, /*InsnID*/1, /*TempRegID*/1,
@@ -138,12 +138,12 @@ def : Pat<(i32 (anyext i16:$src)), (INSERT_SUBREG (i32 (COPY_TO_REGCLASS SOP:$sr
// by a subinstruction.
def : Pat<(i32 (anyext i16:$src)), (INSERT_SUBREG (i32 (IMPLICIT_DEF)), (SUBSOME_INSN SOP:$src), sub0)>;
// CHECK-LABEL: (anyext:{ *:[i32] } i16:{ *:[i16] }:$src) => (INSERT_SUBREG:{ *:[i32] } (IMPLICIT_DEF:{ *:[i32] }), (SUBSOME_INSN:{ *:[i16] } SOP:{ *:[i16] }:$src), sub0:{ *:[i32] })
-// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32,
// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/1, /*TypeID*/GILLT_s16,
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/2, /*Opcode*/GIMT_Encode2(MyTarget::SUBSOME_INSN),
// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/2, /*TempRegID*/1, /*TempRegFlags*/GIMT_Encode2(RegState::Define),
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/2, /*OldInsnID*/0, /*OpIdx*/1, // src
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/2,
+// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32,
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(TargetOpcode::IMPLICIT_DEF),
// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/GIMT_Encode2(RegState::Define),
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/1,
@@ -200,12 +200,12 @@ def : Pat<(i16 (trunc (bitreverse DOP:$src))),
// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/1, /*Op*/1, /*RC*/GIMT_Encode2(Test::DRegsRegClassID),
// CHECK-NEXT: GIM_CheckIsSafeToFold, /*NumInsns*/1,
// CHECK-NEXT: // (trunc:{ *:[i16] } (ctpop:{ *:[i32] } DOP:{ *:[i32] }:$src)) => (SUBSOME_INSN2:{ *:[i16] } (EXTRACT_SUBREG:{ *:[i16] } (SOME_INSN:{ *:[i32] } DOP:{ *:[i32] }:$src), sub0:{ *:[i32] }))
-// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s16,
// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/1, /*TypeID*/GILLT_s32,
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/2, /*Opcode*/GIMT_Encode2(MyTarget::SOME_INSN),
// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/2, /*TempRegID*/1, /*TempRegFlags*/GIMT_Encode2(RegState::Define),
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/2, /*OldInsnID*/1, /*OpIdx*/1, // src
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/2,
+// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s16,
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(TargetOpcode::COPY),
// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/1, /*TempRegID*/0, /*TempRegFlags*/GIMT_Encode2(RegState::Define),
// CHECK-NEXT: GIR_AddTempSubRegister, /*InsnID*/1, /*TempRegID*/1, /*TempRegFlags*/GIMT_Encode2(0), GIMT_Encode2(sub0),
diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp
index 84f23985b64213..715c29c4636aee 100644
--- a/llvm/utils/TableGen/GlobalISelEmitter.cpp
+++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp
@@ -324,8 +324,6 @@ class GlobalISelEmitter final : public GlobalISelMatchTableExecutorEmitter {
void emitTestSimplePredicate(raw_ostream &OS) override;
void emitRunCustomAction(raw_ostream &OS) override;
- void postProcessRule(RuleMatcher &M);
-
const CodeGenTarget &getTarget() const override { return Target; }
StringRef getClassName() const override { return ClassName; }
@@ -1410,12 +1408,10 @@ GlobalISelEmitter::createAndImportSubInstructionRenderer(
DstMIBuilder.addRenderer<TempRegRenderer>(TempRegID, true);
// Handle additional (ignored) results.
- if (DstMIBuilder.getCGI()->Operands.NumDefs > 1) {
- InsertPtOrError = importExplicitDefRenderers(
- std::prev(*InsertPtOrError), M, DstMIBuilder, Src, Dst, /*Start=*/1);
- if (auto Error = InsertPtOrError.takeError())
- return std::move(Error);
- }
+ InsertPtOrError = importExplicitDefRenderers(
+ std::prev(*InsertPtOrError), M, DstMIBuilder, Src, Dst, /*Start=*/1);
+ if (auto Error = InsertPtOrError.takeError())
+ return std::move(Error);
InsertPtOrError = importExplicitUseRenderers(InsertPtOrError.get(), M,
DstMIBuilder, Dst, Src);
@@ -1454,25 +1450,26 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitDefRenderers(
action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder,
const TreePatternNode &Src, const TreePatternNode &Dst, unsigned Start) {
const CodeGenInstruction *DstI = DstMIBuilder.getCGI();
- const unsigned SrcNumDefs = Src.getExtTypes().size();
- const unsigned DstNumDefs = DstI->Operands.NumDefs;
- if (DstNumDefs == 0)
- return InsertPt;
-
- for (unsigned I = Start; I < SrcNumDefs; ++I) {
- std::string OpName = getMangledRootDefName(DstI->Operands[I].Name);
- // CopyRenderer saves a StringRef, so cannot pass OpName itself -
- // let's use a string with an appropriate lifetime.
- StringRef PermanentRef = M.getOperandMatcher(OpName).getSymbolicName();
- DstMIBuilder.addRenderer<CopyRenderer>(PermanentRef);
- }
// Some instructions have multiple defs, but are missing a type entry
// (e.g. s_cc_out operands).
- if (Dst.getExtTypes().size() < DstNumDefs)
+ if (Dst.getExtTypes().size() < DstI->Operands.NumDefs)
return failedImport("unhandled discarded def");
- for (unsigned I = SrcNumDefs; I < DstNumDefs; ++I) {
+ // Process explicit defs. The caller may have already handled the first def.
+ for (unsigned I = Start, E = DstI->Operands.NumDefs; I != E; ++I) {
+ std::string OpName = getMangledRootDefName(DstI->Operands[I].Name);
+
+ // If the def is used in the source DAG, forward it.
+ if (M.hasOperand(OpName)) {
+ // CopyRenderer saves a StringRef, so cannot pass OpName itself -
+ // let's use a string with an appropriate lifetime.
+ StringRef PermanentRef = M.getOperandMatcher(OpName).getSymbolicName();
+ DstMIBuilder.addRenderer<CopyRenderer>(PermanentRef);
+ continue;
+ }
+
+ // The def is discarded, create a dead virtual register for it.
const TypeSetByHwMode &ExtTy = Dst.getExtType(I);
if (!ExtTy.isMachineValueType())
return failedImport("unsupported typeset");
@@ -1484,7 +1481,15 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitDefRenderers(
unsigned TempRegID = M.allocateTempRegID();
InsertPt =
M.insertAction<MakeTempRegisterAction>(InsertPt, *OpTy, TempRegID);
- DstMIBuilder.addRenderer<TempRegRenderer>(TempRegID, true, nullptr, true);
+ DstMIBuilder.addRenderer<TempRegRenderer>(
+ TempRegID, /*IsDef=*/true, /*SubReg=*/nullptr, /*IsDead=*/true);
+ }
+
+ // Implicit defs are not currently supported, mark all of them as dead.
+ for (const Record *Reg : DstI->ImplicitDefs) {
+ std::string OpName = getMangledRootDefName(Reg->getName());
+ assert(!M.hasOperand(OpName) && "The pattern should've been rejected");
+ DstMIBuilder.setDeadImplicitDef(Reg);
}
return InsertPt;
@@ -2299,31 +2304,6 @@ void GlobalISelEmitter::emitRunCustomAction(raw_ostream &OS) {
<< "}\n";
}
-void GlobalISelEmitter::postProcessRule(RuleMatcher &M) {
- SmallPtrSet<const Record *, 16> UsedRegs;
-
- // TODO: deal with subregs?
- for (auto &A : M.actions()) {
- auto *MI = dyn_cast<BuildMIAction>(A.get());
- if (!MI)
- continue;
-
- for (auto *Use : MI->getCGI()->ImplicitUses)
- UsedRegs.insert(Use);
- }
-
- for (auto &A : M.actions()) {
- auto *MI = dyn_cast<BuildMIAction>(A.get());
- if (!MI)
- continue;
-
- for (auto *Def : MI->getCGI()->ImplicitDefs) {
- if (!UsedRegs.contains(Def))
- MI->setDeadImplicitDef(Def);
- }
- }
-}
-
void GlobalISelEmitter::run(raw_ostream &OS) {
if (!UseCoverageFile.empty()) {
RuleCoverage = CodeGenCoverage();
@@ -2383,7 +2363,6 @@ void GlobalISelEmitter::run(raw_ostream &OS) {
"Pattern is not covered by a test");
}
Rules.push_back(std::move(MatcherOrErr.get()));
- postProcessRule(Rules.back());
}
// Comparison function to order records by name.
>From 86e4f094d98867d5c3e0225efda0ef18a7ef3790 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Wed, 18 Dec 2024 17:15:45 +0300
Subject: [PATCH 2/3] [TableGen][GISel] Delete unused `Src` arguments
The last uses were removed in #120332 and #120426.
When emitting renderers, we shouldn't look at the source DAG at all.
The required information is provided by the destination DAG and by
the instructions referenced in that DAG. Sometimes, we do want to know
if a leaf was referenced in the source DAG; this can be checked by
calling `RuleMatcher::hasOperand`. Any other use of the source DAG when
emitting renderers is likely an error.
---
llvm/utils/TableGen/GlobalISelEmitter.cpp | 128 +++++++++++-----------
1 file changed, 62 insertions(+), 66 deletions(-)
diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp
index 715c29c4636aee..a33411ebf7dd83 100644
--- a/llvm/utils/TableGen/GlobalISelEmitter.cpp
+++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp
@@ -393,12 +393,13 @@ class GlobalISelEmitter final : public GlobalISelMatchTableExecutorEmitter {
bool OperandIsAPointer, bool OperandIsImmArg,
unsigned OpIdx, unsigned &TempOpIdx);
- Expected<BuildMIAction &> createAndImportInstructionRenderer(
- RuleMatcher &M, InstructionMatcher &InsnMatcher,
- const TreePatternNode &Src, const TreePatternNode &Dst);
+ Expected<BuildMIAction &>
+ createAndImportInstructionRenderer(RuleMatcher &M,
+ InstructionMatcher &InsnMatcher,
+ const TreePatternNode &Dst);
Expected<action_iterator> createAndImportSubInstructionRenderer(
action_iterator InsertPt, RuleMatcher &M, const TreePatternNode &Dst,
- const TreePatternNode &Src, unsigned TempReg);
+ unsigned TempReg);
Expected<action_iterator>
createInstructionRenderer(action_iterator InsertPt, RuleMatcher &M,
const TreePatternNode &Dst);
@@ -406,15 +407,16 @@ class GlobalISelEmitter final : public GlobalISelMatchTableExecutorEmitter {
Expected<action_iterator>
importExplicitDefRenderers(action_iterator InsertPt, RuleMatcher &M,
BuildMIAction &DstMIBuilder,
- const TreePatternNode &Src,
const TreePatternNode &Dst, unsigned Start = 0);
- Expected<action_iterator> importExplicitUseRenderers(
- action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder,
- const llvm::TreePatternNode &Dst, const TreePatternNode &Src);
- Expected<action_iterator> importExplicitUseRenderer(
- action_iterator InsertPt, RuleMatcher &Rule, BuildMIAction &DstMIBuilder,
- const TreePatternNode &DstChild, const TreePatternNode &Src);
+ Expected<action_iterator>
+ importExplicitUseRenderers(action_iterator InsertPt, RuleMatcher &M,
+ BuildMIAction &DstMIBuilder,
+ const TreePatternNode &Dst);
+ Expected<action_iterator>
+ importExplicitUseRenderer(action_iterator InsertPt, RuleMatcher &Rule,
+ BuildMIAction &DstMIBuilder,
+ const TreePatternNode &Dst);
Error importDefaultOperandRenderers(action_iterator InsertPt, RuleMatcher &M,
BuildMIAction &DstMIBuilder,
const DAGDefaultOperand &DefaultOp) const;
@@ -1196,23 +1198,22 @@ Error GlobalISelEmitter::importChildMatcher(
Expected<action_iterator> GlobalISelEmitter::importExplicitUseRenderer(
action_iterator InsertPt, RuleMatcher &Rule, BuildMIAction &DstMIBuilder,
- const TreePatternNode &DstChild, const TreePatternNode &Src) {
+ const TreePatternNode &Dst) {
- const auto &SubOperand = Rule.getComplexSubOperand(DstChild.getName());
+ const auto &SubOperand = Rule.getComplexSubOperand(Dst.getName());
if (SubOperand) {
DstMIBuilder.addRenderer<RenderComplexPatternOperand>(
- *std::get<0>(*SubOperand), DstChild.getName(), std::get<1>(*SubOperand),
+ *std::get<0>(*SubOperand), Dst.getName(), std::get<1>(*SubOperand),
std::get<2>(*SubOperand));
return InsertPt;
}
- if (!DstChild.isLeaf()) {
- if (DstChild.getOperator()->isSubClassOf("SDNodeXForm")) {
- auto &Child = DstChild.getChild(0);
- auto I = SDNodeXFormEquivs.find(DstChild.getOperator());
+ 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 =
- DstChild.getOperator()->getValueAsDef("Opcode");
+ 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
@@ -1231,10 +1232,10 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitUseRenderer(
// We accept 'bb' here. It's an operator because BasicBlockSDNode isn't
// inline, but in MI it's just another operand.
- if (DstChild.getOperator()->isSubClassOf("SDNode")) {
- auto &ChildSDNI = CGP.getSDNodeInfo(DstChild.getOperator());
+ if (Dst.getOperator()->isSubClassOf("SDNode")) {
+ auto &ChildSDNI = CGP.getSDNodeInfo(Dst.getOperator());
if (ChildSDNI.getSDClassName() == "BasicBlockSDNode") {
- DstMIBuilder.addRenderer<CopyRenderer>(DstChild.getName());
+ DstMIBuilder.addRenderer<CopyRenderer>(Dst.getName());
return InsertPt;
}
}
@@ -1243,26 +1244,25 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitUseRenderer(
// rendered as operands.
// FIXME: The target should be able to choose sign-extended when appropriate
// (e.g. on Mips).
- if (DstChild.getOperator()->getName() == "timm") {
- DstMIBuilder.addRenderer<CopyRenderer>(DstChild.getName());
+ if (Dst.getOperator()->getName() == "timm") {
+ DstMIBuilder.addRenderer<CopyRenderer>(Dst.getName());
return InsertPt;
}
- if (DstChild.getOperator()->getName() == "tframeindex") {
- DstMIBuilder.addRenderer<CopyRenderer>(DstChild.getName());
+ if (Dst.getOperator()->getName() == "tframeindex") {
+ DstMIBuilder.addRenderer<CopyRenderer>(Dst.getName());
return InsertPt;
}
- if (DstChild.getOperator()->getName() == "imm") {
- DstMIBuilder.addRenderer<CopyConstantAsImmRenderer>(DstChild.getName());
+ if (Dst.getOperator()->getName() == "imm") {
+ DstMIBuilder.addRenderer<CopyConstantAsImmRenderer>(Dst.getName());
return InsertPt;
}
- if (DstChild.getOperator()->getName() == "fpimm") {
- DstMIBuilder.addRenderer<CopyFConstantAsFPImmRenderer>(
- DstChild.getName());
+ if (Dst.getOperator()->getName() == "fpimm") {
+ DstMIBuilder.addRenderer<CopyFConstantAsFPImmRenderer>(Dst.getName());
return InsertPt;
}
- if (DstChild.getOperator()->isSubClassOf("Instruction")) {
- auto OpTy = getInstResultType(DstChild, Target);
+ if (Dst.getOperator()->isSubClassOf("Instruction")) {
+ auto OpTy = getInstResultType(Dst, Target);
if (!OpTy)
return OpTy.takeError();
@@ -1272,29 +1272,28 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitUseRenderer(
DstMIBuilder.addRenderer<TempRegRenderer>(TempRegID);
auto InsertPtOrError = createAndImportSubInstructionRenderer(
- ++InsertPt, Rule, DstChild, Src, TempRegID);
+ ++InsertPt, Rule, Dst, TempRegID);
if (auto Error = InsertPtOrError.takeError())
return std::move(Error);
return InsertPtOrError.get();
}
return failedImport("Dst pattern child isn't a leaf node or an MBB" +
- llvm::to_string(DstChild));
+ 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<IntInit>(DstChild.getLeafValue())) {
+ if (const IntInit *ChildIntInit = dyn_cast<IntInit>(Dst.getLeafValue())) {
DstMIBuilder.addRenderer<ImmRenderer>(ChildIntInit->getValue());
return InsertPt;
}
// Otherwise, we're looking for a bog-standard RegisterClass operand.
- if (auto *ChildDefInit = dyn_cast<DefInit>(DstChild.getLeafValue())) {
+ if (auto *ChildDefInit = dyn_cast<DefInit>(Dst.getLeafValue())) {
auto *ChildRec = ChildDefInit->getDef();
- ArrayRef<TypeSetByHwMode> ChildTypes = DstChild.getExtTypes();
+ ArrayRef<TypeSetByHwMode> ChildTypes = Dst.getExtTypes();
if (ChildTypes.size() != 1)
return failedImport("Dst pattern child has multiple results");
@@ -1315,11 +1314,11 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitUseRenderer(
if (ChildRec->isSubClassOf("RegisterOperand") &&
!ChildRec->isValueUnset("GIZeroRegister")) {
DstMIBuilder.addRenderer<CopyOrAddZeroRegRenderer>(
- DstChild.getName(), ChildRec->getValueAsDef("GIZeroRegister"));
+ Dst.getName(), ChildRec->getValueAsDef("GIZeroRegister"));
return InsertPt;
}
- DstMIBuilder.addRenderer<CopyRenderer>(DstChild.getName());
+ DstMIBuilder.addRenderer<CopyRenderer>(Dst.getName());
return InsertPt;
}
@@ -1335,9 +1334,9 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitUseRenderer(
return failedImport(
"SelectionDAG ComplexPattern not mapped to GlobalISel");
- const OperandMatcher &OM = Rule.getOperandMatcher(DstChild.getName());
+ const OperandMatcher &OM = Rule.getOperandMatcher(Dst.getName());
DstMIBuilder.addRenderer<RenderComplexPatternOperand>(
- *ComplexPattern->second, DstChild.getName(),
+ *ComplexPattern->second, Dst.getName(),
OM.getAllocatedTemporariesBaseID());
return InsertPt;
}
@@ -1348,16 +1347,15 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitUseRenderer(
// Handle the case where the MVT/register class is omitted in the dest pattern
// but MVT exists in the source pattern.
- if (isa<UnsetInit>(DstChild.getLeafValue()) &&
- Rule.hasOperand(DstChild.getName())) {
- DstMIBuilder.addRenderer<CopyRenderer>(DstChild.getName());
+ if (isa<UnsetInit>(Dst.getLeafValue()) && Rule.hasOperand(Dst.getName())) {
+ DstMIBuilder.addRenderer<CopyRenderer>(Dst.getName());
return InsertPt;
}
return failedImport("Dst pattern child is an unsupported kind");
}
Expected<BuildMIAction &> GlobalISelEmitter::createAndImportInstructionRenderer(
- RuleMatcher &M, InstructionMatcher &InsnMatcher, const TreePatternNode &Src,
+ RuleMatcher &M, InstructionMatcher &InsnMatcher,
const TreePatternNode &Dst) {
auto InsertPtOrError = createInstructionRenderer(M.actions_end(), M, Dst);
if (auto Error = InsertPtOrError.takeError())
@@ -1377,14 +1375,12 @@ Expected<BuildMIAction &> GlobalISelEmitter::createAndImportInstructionRenderer(
CopyToPhysRegMIBuilder.addRenderer<CopyPhysRegRenderer>(PhysInput.first);
}
- if (auto Error =
- importExplicitDefRenderers(InsertPt, M, DstMIBuilder, Src, Dst)
- .takeError())
+ if (auto Error = importExplicitDefRenderers(InsertPt, M, DstMIBuilder, Dst)
+ .takeError())
return std::move(Error);
- if (auto Error =
- importExplicitUseRenderers(InsertPt, M, DstMIBuilder, Dst, Src)
- .takeError())
+ if (auto Error = importExplicitUseRenderers(InsertPt, M, DstMIBuilder, Dst)
+ .takeError())
return std::move(Error);
return DstMIBuilder;
@@ -1392,8 +1388,8 @@ Expected<BuildMIAction &> GlobalISelEmitter::createAndImportInstructionRenderer(
Expected<action_iterator>
GlobalISelEmitter::createAndImportSubInstructionRenderer(
- const action_iterator InsertPt, RuleMatcher &M, const TreePatternNode &Dst,
- const TreePatternNode &Src, unsigned TempRegID) {
+ action_iterator InsertPt, RuleMatcher &M, const TreePatternNode &Dst,
+ unsigned TempRegID) {
auto InsertPtOrError = createInstructionRenderer(InsertPt, M, Dst);
// TODO: Assert there's exactly one result.
@@ -1408,13 +1404,13 @@ GlobalISelEmitter::createAndImportSubInstructionRenderer(
DstMIBuilder.addRenderer<TempRegRenderer>(TempRegID, true);
// Handle additional (ignored) results.
- InsertPtOrError = importExplicitDefRenderers(
- std::prev(*InsertPtOrError), M, DstMIBuilder, Src, Dst, /*Start=*/1);
+ InsertPtOrError = importExplicitDefRenderers(std::prev(*InsertPtOrError), M,
+ DstMIBuilder, Dst, /*Start=*/1);
if (auto Error = InsertPtOrError.takeError())
return std::move(Error);
- InsertPtOrError = importExplicitUseRenderers(InsertPtOrError.get(), M,
- DstMIBuilder, Dst, Src);
+ InsertPtOrError =
+ importExplicitUseRenderers(InsertPtOrError.get(), M, DstMIBuilder, Dst);
if (auto Error = InsertPtOrError.takeError())
return std::move(Error);
@@ -1448,7 +1444,7 @@ Expected<action_iterator> GlobalISelEmitter::createInstructionRenderer(
Expected<action_iterator> GlobalISelEmitter::importExplicitDefRenderers(
action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder,
- const TreePatternNode &Src, const TreePatternNode &Dst, unsigned Start) {
+ const TreePatternNode &Dst, unsigned Start) {
const CodeGenInstruction *DstI = DstMIBuilder.getCGI();
// Some instructions have multiple defs, but are missing a type entry
@@ -1497,7 +1493,7 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitDefRenderers(
Expected<action_iterator> GlobalISelEmitter::importExplicitUseRenderers(
action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder,
- const llvm::TreePatternNode &Dst, const llvm::TreePatternNode &Src) {
+ const TreePatternNode &Dst) {
const CodeGenInstruction *DstI = DstMIBuilder.getCGI();
CodeGenInstruction *OrigDstI = &Target.getInstruction(Dst.getOperator());
@@ -1527,7 +1523,7 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitUseRenderers(
TempRegID);
auto InsertPtOrError = createAndImportSubInstructionRenderer(
- ++InsertPt, M, ValChild, Src, TempRegID);
+ ++InsertPt, M, ValChild, TempRegID);
if (auto Error = InsertPtOrError.takeError())
return std::move(Error);
@@ -1585,7 +1581,7 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitUseRenderers(
CodeGenSubRegIndex *SubIdx = CGRegs.getSubRegIdx(SubRegInit->getDef());
auto InsertPtOrError =
- importExplicitUseRenderer(InsertPt, M, DstMIBuilder, ValChild, Src);
+ importExplicitUseRenderer(InsertPt, M, DstMIBuilder, ValChild);
if (auto Error = InsertPtOrError.takeError())
return std::move(Error);
InsertPt = InsertPtOrError.get();
@@ -1654,7 +1650,7 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitUseRenderers(
}
auto InsertPtOrError = importExplicitUseRenderer(InsertPt, M, DstMIBuilder,
- Dst.getChild(Child), Src);
+ Dst.getChild(Child));
if (auto Error = InsertPtOrError.takeError())
return std::move(Error);
InsertPt = InsertPtOrError.get();
@@ -2135,7 +2131,7 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
}
auto DstMIBuilderOrError =
- createAndImportInstructionRenderer(M, InsnMatcher, Src, Dst);
+ createAndImportInstructionRenderer(M, InsnMatcher, Dst);
if (auto Error = DstMIBuilderOrError.takeError())
return std::move(Error);
BuildMIAction &DstMIBuilder = DstMIBuilderOrError.get();
>From 16c07ccbb1316498a807babe79c731cc9cb90593 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Tue, 17 Dec 2024 19:39:15 +0300
Subject: [PATCH 3/3] [TableGen][GISel] Learn to import patterns with
optional/physreg defs
---
.../Target/GlobalISel/SelectionDAGCompat.td | 2 +
.../test/CodeGen/X86/GlobalISel/mul-scalar.ll | 9 +-
.../Common/GlobalISelEmitterCommon.td | 3 +-
.../GlobalISelEmitter-implicit-defs.td | 62 ++++++++++--
.../GlobalISel/GlobalISelMatchTable.cpp | 9 +-
.../Common/GlobalISel/GlobalISelMatchTable.h | 6 +-
llvm/utils/TableGen/GlobalISelEmitter.cpp | 96 ++++++++++++-------
7 files changed, 133 insertions(+), 54 deletions(-)
diff --git a/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td b/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td
index 2148f5be4c41aa..c8c0eeb57099a2 100644
--- a/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td
+++ b/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td
@@ -70,6 +70,8 @@ def : GINodeEquiv<G_SDIV, sdiv>;
def : GINodeEquiv<G_UDIV, udiv>;
def : GINodeEquiv<G_SREM, srem>;
def : GINodeEquiv<G_UREM, urem>;
+def : GINodeEquiv<G_SDIVREM, sdivrem>;
+def : GINodeEquiv<G_UDIVREM, udivrem>;
def : GINodeEquiv<G_AND, and>;
def : GINodeEquiv<G_OR, or>;
def : GINodeEquiv<G_XOR, xor>;
diff --git a/llvm/test/CodeGen/X86/GlobalISel/mul-scalar.ll b/llvm/test/CodeGen/X86/GlobalISel/mul-scalar.ll
index f401f45a06f6a7..3196668c70d8ec 100644
--- a/llvm/test/CodeGen/X86/GlobalISel/mul-scalar.ll
+++ b/llvm/test/CodeGen/X86/GlobalISel/mul-scalar.ll
@@ -5,16 +5,17 @@
define i8 @test_mul_i8(i8 %arg1, i8 %arg2) nounwind {
; X64-LABEL: test_mul_i8:
; X64: # %bb.0:
-; X64-NEXT: movsbl %dil, %eax
-; X64-NEXT: imulb %sil
+; X64-NEXT: movl %edi, %eax
+; X64-NEXT: # kill: def $al killed $al killed $eax
+; X64-NEXT: mulb %sil
; X64-NEXT: retq
;
; X86-LABEL: test_mul_i8:
; X86: # %bb.0:
; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
-; X86-NEXT: cbtw
-; X86-NEXT: imulb %cl
+; X86-NEXT: # kill: def $al killed $al killed $eax
+; X86-NEXT: mulb %cl
; X86-NEXT: retl
%ret = mul i8 %arg1, %arg2
ret i8 %ret
diff --git a/llvm/test/TableGen/Common/GlobalISelEmitterCommon.td b/llvm/test/TableGen/Common/GlobalISelEmitterCommon.td
index 8f11fee3751844..cfcaf3c76bbf8a 100644
--- a/llvm/test/TableGen/Common/GlobalISelEmitterCommon.td
+++ b/llvm/test/TableGen/Common/GlobalISelEmitterCommon.td
@@ -7,7 +7,8 @@ class MyTargetGenericInstruction : GenericInstruction {
}
def R0 : Register<"r0"> { let Namespace = "MyTarget"; }
-def GPR32 : RegisterClass<"MyTarget", [i32], 32, (add R0)>;
+def R1 : Register<"r0"> { let Namespace = "MyTarget"; }
+def GPR32 : RegisterClass<"MyTarget", [i32], 32, (add R0, R1)>;
def GPR32Op : RegisterOperand<GPR32>;
def F0 : Register<"f0"> { let Namespace = "MyTarget"; }
def FPR32 : RegisterClass<"MyTarget", [f32], 32, (add F0)>;
diff --git a/llvm/test/TableGen/GlobalISelEmitter-implicit-defs.td b/llvm/test/TableGen/GlobalISelEmitter-implicit-defs.td
index 79af1a336f2890..ebf290a27b13ed 100644
--- a/llvm/test/TableGen/GlobalISelEmitter-implicit-defs.td
+++ b/llvm/test/TableGen/GlobalISelEmitter-implicit-defs.td
@@ -1,12 +1,60 @@
-// RUN: llvm-tblgen -gen-global-isel -warn-on-skipped-patterns -I %p/../../include -I %p/Common %s -o /dev/null 2>&1 < %s | FileCheck %s --implicit-check-not="Skipped pattern"
+// RUN: llvm-tblgen -gen-global-isel -I %p/../../include -I %p/Common %s | FileCheck %s
include "llvm/Target/Target.td"
include "GlobalISelEmitterCommon.td"
-// CHECK: Skipped pattern: Pattern defines a physical register
-let Uses = [B0], Defs = [B0] in
-def tst1 : I<(outs), (ins), [(set B0, (add B0, 1))]>;
+let Defs = [R0, R1] in
+def tst1 : I<(outs), (ins), [(set R0, (get_fpenv))]>;
-// CHECK: Skipped pattern: Src pattern result has 1 def(s) without the HasNoUse predicate set to true but Dst MI has no def
-let Uses = [B0] in
-def tst2 : I<(outs), (ins), [(set B0, (add B0, 1))]>;
+let Defs = [R0, R1] in
+def tst2 : I<(outs GPR32:$rd), (ins GPR32:$rs1, GPR32:$rs2),
+ [(set GPR32:$rd, R0, (udivrem i32:$rs1, i32:$rs2))]>;
+
+def : Pat<(sdiv i32:$rs1, i32:$rs2), (tst2 $rs1, $rs2)>;
+def : Pat<(sdivrem i32:$rs1, i32:$rs2), (tst2 $rs1, $rs2)>;
+
+// CHECK-LABEL: // (sdiv:{ *:[i32] } i32:{ *:[i32] }:$rs1, i32:{ *:[i32] }:$rs2) => (tst2:{ *:[i32] }:{ *:[i32] } ?:{ *:[i32] }:$rs1, ?:{ *:[i32] }:$rs2)
+// CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/GIMT_Encode2(MyTarget::tst2),
+// CHECK-NEXT: GIR_AddImplicitDef, /*InsnID*/0, GIMT_Encode2(MyTarget::R0), GIMT_Encode2(RegState::Dead),
+// CHECK-NEXT: GIR_AddImplicitDef, /*InsnID*/0, GIMT_Encode2(MyTarget::R1), GIMT_Encode2(RegState::Dead),
+// CHECK-NEXT: GIR_RootConstrainSelectedInstOperands,
+// CHECK-NEXT: // GIR_Coverage, 2,
+
+// CHECK-LABEL: // (sdivrem:{ *:[i32] }:{ *:[i32] } i32:{ *:[i32] }:$rs1, i32:{ *:[i32] }:$rs2) => (tst2:{ *:[i32] }:{ *:[i32] } ?:{ *:[i32] }:$rs1, ?:{ *:[i32] }:$rs2)
+// CHECK-NEXT: GIR_BuildRootMI, /*Opcode*/GIMT_Encode2(MyTarget::tst2),
+// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/0, // DstI[rd]
+// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/2, // rs1
+// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/3, // rs2
+// CHECK-NEXT: GIR_SetImplicitDefDead, /*InsnID*/0, /*OpIdx for MyTarget::R1*/1,
+// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(TargetOpcode::COPY),
+// CHECK-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/0, /*OpIdx*/1, // DstI[R0]
+// CHECK-NEXT: GIR_AddRegister, /*InsnID*/1, GIMT_Encode2(MyTarget::R0), /*AddRegisterRegFlags*/GIMT_Encode2(0),
+// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/1, /*Op*/0, GIMT_Encode2(MyTarget::GPR32RegClassID),
+// CHECK-NEXT: GIR_RootConstrainSelectedInstOperands,
+// CHECK-NEXT: // GIR_Coverage, 3,
+// CHECK-NEXT: GIR_EraseRootFromParent_Done,
+
+// CHECK-LABEL: // (udivrem:{ *:[i32] }:{ *:[i32] } i32:{ *:[i32] }:$rs1, i32:{ *:[i32] }:$rs2) => (tst2:{ *:[i32] }:{ *:[i32] } i32:{ *:[i32] }:$rs1, i32:{ *:[i32] }:$rs2)
+// CHECK-NEXT: GIR_BuildRootMI, /*Opcode*/GIMT_Encode2(MyTarget::tst2),
+// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/0, // DstI[rd]
+// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/2, // rs1
+// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/3, // rs2
+// CHECK-NEXT: GIR_SetImplicitDefDead, /*InsnID*/0, /*OpIdx for MyTarget::R1*/1,
+// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(TargetOpcode::COPY),
+// CHECK-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/0, /*OpIdx*/1, // DstI[R0]
+// CHECK-NEXT: GIR_AddRegister, /*InsnID*/1, GIMT_Encode2(MyTarget::R0), /*AddRegisterRegFlags*/GIMT_Encode2(0),
+// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/1, /*Op*/0, GIMT_Encode2(MyTarget::GPR32RegClassID),
+// CHECK-NEXT: GIR_RootConstrainSelectedInstOperands,
+// CHECK-NEXT: // GIR_Coverage, 1,
+// CHECK-NEXT: GIR_EraseRootFromParent_Done,
+
+// CHECK-LABEL: // (get_fpenv:{ *:[i32] }) => (tst1:{ *:[i32] })
+// CHECK-NEXT: GIR_BuildRootMI, /*Opcode*/GIMT_Encode2(MyTarget::tst1),
+// CHECK-NEXT: GIR_SetImplicitDefDead, /*InsnID*/0, /*OpIdx for MyTarget::R1*/1,
+// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(TargetOpcode::COPY),
+// CHECK-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/0, /*OpIdx*/0, // DstI[R0]
+// CHECK-NEXT: GIR_AddRegister, /*InsnID*/1, GIMT_Encode2(MyTarget::R0), /*AddRegisterRegFlags*/GIMT_Encode2(0),
+// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/1, /*Op*/0, GIMT_Encode2(MyTarget::GPR32RegClassID),
+// CHECK-NEXT: GIR_RootConstrainSelectedInstOperands,
+// CHECK-NEXT: // GIR_Coverage, 0,
+// CHECK-NEXT: GIR_EraseRootFromParent_Done,
diff --git a/llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.cpp b/llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.cpp
index 15ec7e17130de4..619e7a4790c88b 100644
--- a/llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.cpp
+++ b/llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.cpp
@@ -1993,10 +1993,13 @@ void AddRegisterRenderer::emitRenderOpcodes(MatchTable &Table,
// TODO: This is encoded as a 64-bit element, but only 16 or 32-bits are
// really needed for a physical register reference. We can pack the
// register and flags in a single field.
- if (IsDef)
- Table << MatchTable::NamedValue(2, "RegState::Define");
- else
+ if (IsDef) {
+ Table << MatchTable::NamedValue(
+ 2, IsDead ? "RegState::Define | RegState::Dead" : "RegState::Define");
+ } else {
+ assert(!IsDead && "A use cannot be dead");
Table << MatchTable::IntValue(2, 0);
+ }
Table << MatchTable::LineBreak;
}
diff --git a/llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.h b/llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.h
index 00fe073057c5c9..48ce71be677c08 100644
--- a/llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.h
+++ b/llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.h
@@ -2091,13 +2091,15 @@ class AddRegisterRenderer : public OperandRenderer {
unsigned InsnID;
const Record *RegisterDef;
bool IsDef;
+ bool IsDead;
const CodeGenTarget &Target;
public:
AddRegisterRenderer(unsigned InsnID, const CodeGenTarget &Target,
- const Record *RegisterDef, bool IsDef = false)
+ const Record *RegisterDef, bool IsDef = false,
+ bool IsDead = false)
: OperandRenderer(OR_Register), InsnID(InsnID), RegisterDef(RegisterDef),
- IsDef(IsDef), Target(Target) {}
+ IsDef(IsDef), IsDead(IsDead), Target(Target) {}
static bool classof(const OperandRenderer *R) {
return R->getKind() == OR_Register;
diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp
index a33411ebf7dd83..18caf7a2f7233d 100644
--- a/llvm/utils/TableGen/GlobalISelEmitter.cpp
+++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp
@@ -404,10 +404,11 @@ class GlobalISelEmitter final : public GlobalISelMatchTableExecutorEmitter {
createInstructionRenderer(action_iterator InsertPt, RuleMatcher &M,
const TreePatternNode &Dst);
- Expected<action_iterator>
- importExplicitDefRenderers(action_iterator InsertPt, RuleMatcher &M,
- BuildMIAction &DstMIBuilder,
- const TreePatternNode &Dst, unsigned Start = 0);
+ Expected<action_iterator> importDefRenderers(action_iterator InsertPt,
+ RuleMatcher &M,
+ BuildMIAction &DstMIBuilder,
+ const TreePatternNode &Dst,
+ bool IsRoot);
Expected<action_iterator>
importExplicitUseRenderers(action_iterator InsertPt, RuleMatcher &M,
@@ -420,8 +421,6 @@ class GlobalISelEmitter final : public GlobalISelMatchTableExecutorEmitter {
Error importDefaultOperandRenderers(action_iterator InsertPt, RuleMatcher &M,
BuildMIAction &DstMIBuilder,
const DAGDefaultOperand &DefaultOp) const;
- Error importImplicitDefRenderers(BuildMIAction &DstMIBuilder,
- ArrayRef<const Record *> ImplicitDefs) const;
/// Analyze pattern \p P, returning a matcher for it if possible.
/// Otherwise, return an Error explaining why we don't support it.
@@ -1375,8 +1374,9 @@ Expected<BuildMIAction &> GlobalISelEmitter::createAndImportInstructionRenderer(
CopyToPhysRegMIBuilder.addRenderer<CopyPhysRegRenderer>(PhysInput.first);
}
- if (auto Error = importExplicitDefRenderers(InsertPt, M, DstMIBuilder, Dst)
- .takeError())
+ if (auto Error =
+ importDefRenderers(InsertPt, M, DstMIBuilder, Dst, /*IsRoot=*/true)
+ .takeError())
return std::move(Error);
if (auto Error = importExplicitUseRenderers(InsertPt, M, DstMIBuilder, Dst)
@@ -1404,8 +1404,8 @@ GlobalISelEmitter::createAndImportSubInstructionRenderer(
DstMIBuilder.addRenderer<TempRegRenderer>(TempRegID, true);
// Handle additional (ignored) results.
- InsertPtOrError = importExplicitDefRenderers(std::prev(*InsertPtOrError), M,
- DstMIBuilder, Dst, /*Start=*/1);
+ InsertPtOrError = importDefRenderers(std::prev(*InsertPtOrError), M,
+ DstMIBuilder, Dst, /*IsRoot=*/false);
if (auto Error = InsertPtOrError.takeError())
return std::move(Error);
@@ -1442,19 +1442,15 @@ Expected<action_iterator> GlobalISelEmitter::createInstructionRenderer(
DstI);
}
-Expected<action_iterator> GlobalISelEmitter::importExplicitDefRenderers(
+Expected<action_iterator> GlobalISelEmitter::importDefRenderers(
action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder,
- const TreePatternNode &Dst, unsigned Start) {
+ const TreePatternNode &Dst, bool IsRoot) {
const CodeGenInstruction *DstI = DstMIBuilder.getCGI();
- // Some instructions have multiple defs, but are missing a type entry
- // (e.g. s_cc_out operands).
- if (Dst.getExtTypes().size() < DstI->Operands.NumDefs)
- return failedImport("unhandled discarded def");
-
// Process explicit defs. The caller may have already handled the first def.
- for (unsigned I = Start, E = DstI->Operands.NumDefs; I != E; ++I) {
- std::string OpName = getMangledRootDefName(DstI->Operands[I].Name);
+ for (unsigned I = IsRoot ? 0 : 1, E = DstI->Operands.NumDefs; I != E; ++I) {
+ const CGIOperandList::OperandInfo &OpInfo = DstI->Operands[I];
+ std::string OpName = getMangledRootDefName(OpInfo.Name);
// If the def is used in the source DAG, forward it.
if (M.hasOperand(OpName)) {
@@ -1465,6 +1461,19 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitDefRenderers(
continue;
}
+ if (OpInfo.Rec->isSubClassOf("OptionalDefOperand")) {
+ const DAGDefaultOperand &ComplexOp = CGP.getDefaultOperand(OpInfo.Rec);
+ for (const TreePatternNode &SubOp :
+ make_pointee_range(ComplexOp.DefaultOps)) {
+ const Record *Reg = cast<DefInit>(SubOp.getLeafValue())->getDef();
+ assert(Reg->isSubClassOf("Register") &&
+ "Optional def can only be a register");
+ DstMIBuilder.addRenderer<AddRegisterRenderer>(
+ Target, Reg, /*IsDef=*/true, /*IsDead=*/true);
+ }
+ continue;
+ }
+
// The def is discarded, create a dead virtual register for it.
const TypeSetByHwMode &ExtTy = Dst.getExtType(I);
if (!ExtTy.isMachineValueType())
@@ -1484,8 +1493,23 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitDefRenderers(
// Implicit defs are not currently supported, mark all of them as dead.
for (const Record *Reg : DstI->ImplicitDefs) {
std::string OpName = getMangledRootDefName(Reg->getName());
- assert(!M.hasOperand(OpName) && "The pattern should've been rejected");
- DstMIBuilder.setDeadImplicitDef(Reg);
+
+ if (!IsRoot || !M.hasOperand(OpName)) {
+ DstMIBuilder.setDeadImplicitDef(Reg);
+ continue;
+ }
+
+ BuildMIAction &CopyBuilder = M.addAction<BuildMIAction>(
+ M.allocateOutputInsnID(), &Target.getInstruction(RK.getDef("COPY")));
+
+ StringRef PermanentRef = M.getOperandMatcher(OpName).getSymbolicName();
+ CopyBuilder.addRenderer<CopyRenderer>(PermanentRef);
+ CopyBuilder.addRenderer<AddRegisterRenderer>(Target, Reg);
+
+ const CodeGenRegisterClass *RC = CGRegs.getRegClassForRegister(Reg);
+ assert(RC);
+ M.addAction<ConstrainOperandToRegClassAction>(CopyBuilder.getInsnID(),
+ /*OpIdx=*/0, *RC);
}
return InsertPt;
@@ -1708,13 +1732,6 @@ Error GlobalISelEmitter::importDefaultOperandRenderers(
return Error::success();
}
-Error GlobalISelEmitter::importImplicitDefRenderers(
- BuildMIAction &DstMIBuilder, ArrayRef<const Record *> ImplicitDefs) const {
- if (!ImplicitDefs.empty())
- return failedImport("Pattern defines a physical register");
- return Error::success();
-}
-
Error GlobalISelEmitter::constrainOperands(action_iterator InsertPt,
RuleMatcher &M, unsigned InsnID,
const TreePatternNode &Dst) {
@@ -2091,13 +2108,14 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
unsigned DstExpDefs = DstI.Operands.NumDefs,
DstNumDefs = DstI.ImplicitDefs.size() + DstExpDefs,
SrcNumDefs = Src.getExtTypes().size();
+
+ bool FoundNoUsePred = false;
if (DstNumDefs < SrcNumDefs) {
if (DstNumDefs != 0)
return failedImport("Src pattern result has more defs than dst MI (" +
to_string(SrcNumDefs) + " def(s) vs " +
to_string(DstNumDefs) + " def(s))");
- bool FoundNoUsePred = false;
for (const auto &Pred : InsnMatcher.predicates()) {
if ((FoundNoUsePred = isa<NoUsePredicateMatcher>(Pred.get())))
break;
@@ -2110,15 +2128,24 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
// The root of the match also has constraints on the register bank so that it
// matches the result instruction.
- unsigned N = std::min(DstExpDefs, SrcNumDefs);
- for (unsigned I = 0; I < N; ++I) {
- const auto &DstIOperand = DstI.Operands[I];
+ for (unsigned I = 0; I < SrcNumDefs; ++I) {
+ if (FoundNoUsePred)
+ continue;
OperandMatcher &OM = InsnMatcher.getOperand(I);
+
+ if (I >= DstExpDefs) {
+ const Record *Reg = DstI.ImplicitDefs[I - DstExpDefs];
+ OM.setSymbolicName(getMangledRootDefName(Reg->getName()));
+ M.defineOperand(OM.getSymbolicName(), OM);
+ continue;
+ }
+
// The operand names declared in the DstI instruction are unrelated to
// those used in pattern's source and destination DAGs, so mangle the
// former to prevent implicitly adding unexpected
// GIM_CheckIsSameOperand predicates by the defineOperand method.
+ const CGIOperandList::OperandInfo &DstIOperand = DstI.Operands[I];
OM.setSymbolicName(getMangledRootDefName(DstIOperand.Name));
M.defineOperand(OM.getSymbolicName(), OM);
@@ -2136,11 +2163,6 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
return std::move(Error);
BuildMIAction &DstMIBuilder = DstMIBuilderOrError.get();
- // Render the implicit defs.
- // These are only added to the root of the result.
- if (auto Error = importImplicitDefRenderers(DstMIBuilder, P.getDstRegs()))
- return std::move(Error);
-
DstMIBuilder.chooseInsnToMutate(M);
// Constrain the registers to classes. This is normally derived from the
More information about the llvm-commits
mailing list