Skip to content

Commit 5d3e267

Browse files
committed
[TableGen][GISel] Learn to import patterns with physreg defs
1 parent 6e8a1a4 commit 5d3e267

File tree

4 files changed

+122
-44
lines changed

4 files changed

+122
-44
lines changed

llvm/lib/Target/X86/X86InstrArithmetic.td

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -64,25 +64,21 @@ class MulDivOpM<bits<8> o, Format f, string m, X86TypeInfo t,
6464
sched.ReadAfterFold, sched.ReadAfterFold];
6565
}
6666

67-
multiclass Mul<bits<8> o, string m, Format RegMRM, Format MemMRM, SDPatternOperator node> {
67+
multiclass Mul<bits<8> o, string m, Format RegMRM, Format MemMRM> {
6868
// AL is really implied by AX, but the registers in Defs must match the
6969
// SDNode results (i8, i32).
7070
//
7171
// FIXME: Used for 8-bit mul, ignore result upper 8 bits.
72-
// This probably ought to be moved to a def : Pat<> if the
73-
// syntax can be accepted.
7472
let Defs = [AL, EFLAGS, AX], Uses = [AL] in
75-
def 8r : MulDivOpR<o, RegMRM, m, Xi8, WriteIMul8,
76-
[(set AL, EFLAGS, (node AL, GR8:$src1))]>;
73+
def 8r : MulDivOpR<o, RegMRM, m, Xi8, WriteIMul8, []>;
7774
let Defs = [AX, DX, EFLAGS], Uses = [AX] in
7875
def 16r : MulDivOpR<o, RegMRM, m, Xi16, WriteIMul16, []>, OpSize16;
7976
let Defs = [EAX, EDX, EFLAGS], Uses = [EAX] in
8077
def 32r : MulDivOpR<o, RegMRM, m, Xi32, WriteIMul32, []>, OpSize32;
8178
let Defs = [RAX, RDX, EFLAGS], Uses = [RAX] in
8279
def 64r : MulDivOpR<o, RegMRM, m, Xi64, WriteIMul64, []>;
8380
let Defs = [AL, EFLAGS, AX], Uses = [AL] in
84-
def 8m : MulDivOpM<o, MemMRM, m, Xi8, WriteIMul8,
85-
[(set AL, EFLAGS, (node AL, (loadi8 addr:$src1)))]>;
81+
def 8m : MulDivOpM<o, MemMRM, m, Xi8, WriteIMul8, []>;
8682
let Defs = [AX, DX, EFLAGS], Uses = [AX] in
8783
def 16m : MulDivOpM<o, MemMRM, m, Xi16, WriteIMul16, []>, OpSize16;
8884
let Defs = [EAX, EDX, EFLAGS], Uses = [EAX] in
@@ -127,8 +123,14 @@ multiclass Mul<bits<8> o, string m, Format RegMRM, Format MemMRM, SDPatternOpera
127123
}
128124
}
129125

130-
defm MUL : Mul<0xF7, "mul", MRM4r, MRM4m, mul>;
131-
defm IMUL : Mul<0xF7, "imul", MRM5r, MRM5m, null_frag>;
126+
defm MUL : Mul<0xF7, "mul", MRM4r, MRM4m>;
127+
defm IMUL : Mul<0xF7, "imul", MRM5r, MRM5m>;
128+
129+
// These nodes are selected by custom C++ code.
130+
let GISelShouldIgnore = true in {
131+
def : Pat<(mul AL, i8:$src1), (MUL8r $src1)>;
132+
def : Pat<(mul AL, (loadi8 addr:$src1)), (MUL8m addr:$src1)>;
133+
}
132134

133135
multiclass Div<bits<8> o, string m, Format RegMRM, Format MemMRM> {
134136
defvar sched8 = !if(!eq(m, "div"), WriteDiv8, WriteIDiv8);

llvm/test/TableGen/Common/GlobalISelEmitterCommon.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ class MyTargetGenericInstruction : GenericInstruction {
77
}
88

99
def R0 : Register<"r0"> { let Namespace = "MyTarget"; }
10-
def GPR32 : RegisterClass<"MyTarget", [i32], 32, (add R0)>;
10+
def R1 : Register<"r1"> { let Namespace = "MyTarget"; }
11+
def GPR32 : RegisterClass<"MyTarget", [i32], 32, (add R0, R1)>;
1112
def GPR32Op : RegisterOperand<GPR32>;
1213
def F0 : Register<"f0"> { let Namespace = "MyTarget"; }
1314
def FPR32 : RegisterClass<"MyTarget", [f32], 32, (add F0)>;
Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,75 @@
1-
// RUN: llvm-tblgen -gen-global-isel -warn-on-skipped-patterns -I %p/../../../include -I %p/../Common %s -o /dev/null 2>&1 | FileCheck %s --implicit-check-not="Skipped pattern"
1+
// RUN: llvm-tblgen -gen-global-isel -optimize-match-table=false \
2+
// RUN: -I %p/../../../include -I %p/../Common %s
23

34
include "llvm/Target/Target.td"
45
include "GlobalISelEmitterCommon.td"
56

6-
// CHECK: Skipped pattern: Pattern defines a physical register
7-
let Uses = [B0], Defs = [B0] in
8-
def tst1 : I<(outs), (ins), [(set B0, (add B0, 1))]>;
7+
let Defs = [R0, B0] in
8+
def tst1 : I<(outs), (ins), [(set R0, (i32 42))]>;
99

10-
// CHECK: Skipped pattern: Src pattern result has 1 def(s) without the HasNoUse predicate set to true but Dst MI has no def
11-
let Uses = [B0] in
12-
def tst2 : I<(outs), (ins), [(set B0, (add B0, 1))]>;
10+
let Defs = [R0, R1] in
11+
def tst2 : I<(outs GPR32:$rd), (ins GPR32:$rs1, GPR32:$rs2),
12+
[(set GPR32:$rd, R1, (sdivrem i32:$rs1, i32:$rs2))]>;
13+
14+
let Defs = [R0, R1] in
15+
def tst3 : I<(outs), (ins GPR32:$rs1, GPR32:$rs2),
16+
[(set R1, R0, (udivrem i32:$rs1, i32:$rs2))]>;
17+
18+
let Defs = [R0] in
19+
def tst4 : I<(outs GPR32:$rd), (ins GPR32:$rs), []>;
20+
21+
def : Pat<(sdivrem i32:$rs, 42), (tst4 (tst4 $rs))>;
22+
23+
// CHECK-LABEL: // (sdivrem:{ *:[i32] }:{ *:[i32] } i32:{ *:[i32] }:$rs, 42:{ *:[i32] }) => (tst4:{ *:[i32] }:{ *:[i32] } (tst4:{ *:[i32] }:{ *:[i32] } ?:{ *:[i32] }:$rs))
24+
// CHECK-NEXT: GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/GILLT_s32,
25+
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/2, /*Opcode*/GIMT_Encode2(MyTarget::tst4),
26+
// CHECK-NEXT: GIR_AddTempRegister, /*InsnID*/2, /*TempRegID*/0, /*TempRegFlags*/GIMT_Encode2(RegState::Define),
27+
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/2, /*OldInsnID*/0, /*OpIdx*/2, // rs
28+
// CHECK-NEXT: GIR_SetImplicitDefDead, /*InsnID*/2, /*OpIdx for MyTarget::R0*/0,
29+
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/2,
30+
// CHECK-NEXT: GIR_BuildRootMI, /*Opcode*/GIMT_Encode2(MyTarget::tst4),
31+
// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/0, // DstI[rd]
32+
// CHECK-NEXT: GIR_AddSimpleTempRegister, /*InsnID*/0, /*TempRegID*/0,
33+
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(TargetOpcode::COPY),
34+
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/0, /*OpIdx*/1, // DstI[R0]
35+
// CHECK-NEXT: GIR_AddRegister, /*InsnID*/1, GIMT_Encode2(MyTarget::R0), /*AddRegisterRegFlags*/GIMT_Encode2(0),
36+
// CHECK-NEXT: GIR_RootConstrainSelectedInstOperands,
37+
// CHECK-NEXT: // GIR_Coverage, 3,
38+
// CHECK-NEXT: GIR_EraseRootFromParent_Done,
39+
40+
// CHECK-LABEL: // 42:{ *:[i32] } => (tst1:{ *:[i32] })
41+
// CHECK-NEXT: GIR_BuildRootMI, /*Opcode*/GIMT_Encode2(MyTarget::tst1),
42+
// CHECK-NEXT: GIR_SetImplicitDefDead, /*InsnID*/0, /*OpIdx for MyTarget::B0*/1,
43+
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(TargetOpcode::COPY),
44+
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/0, /*OpIdx*/0, // DstI[R0]
45+
// CHECK-NEXT: GIR_AddRegister, /*InsnID*/1, GIMT_Encode2(MyTarget::R0), /*AddRegisterRegFlags*/GIMT_Encode2(0),
46+
// CHECK-NEXT: GIR_RootConstrainSelectedInstOperands,
47+
// CHECK-NEXT: // GIR_Coverage, 0,
48+
// CHECK-NEXT: GIR_EraseRootFromParent_Done,
49+
50+
// CHECK-LABEL: // (sdivrem:{ *:[i32] }:{ *:[i32] } i32:{ *:[i32] }:$rs1, i32:{ *:[i32] }:$rs2) => (tst2:{ *:[i32] }:{ *:[i32] } i32:{ *:[i32] }:$rs1, i32:{ *:[i32] }:$rs2)
51+
// CHECK-NEXT: GIR_BuildRootMI, /*Opcode*/GIMT_Encode2(MyTarget::tst2),
52+
// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/0, // DstI[rd]
53+
// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/2, // rs1
54+
// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/3, // rs2
55+
// CHECK-NEXT: GIR_SetImplicitDefDead, /*InsnID*/0, /*OpIdx for MyTarget::R1*/1,
56+
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(TargetOpcode::COPY),
57+
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/0, /*OpIdx*/1, // DstI[R0]
58+
// CHECK-NEXT: GIR_AddRegister, /*InsnID*/1, GIMT_Encode2(MyTarget::R0), /*AddRegisterRegFlags*/GIMT_Encode2(0),
59+
// CHECK-NEXT: GIR_RootConstrainSelectedInstOperands,
60+
// CHECK-NEXT: // GIR_Coverage, 1,
61+
// CHECK-NEXT: GIR_EraseRootFromParent_Done,
62+
63+
// CHECK-LABEL: // (udivrem:{ *:[i32] }:{ *:[i32] } i32:{ *:[i32] }:$rs1, i32:{ *:[i32] }:$rs2) => (tst3:{ *:[i32] } i32:{ *:[i32] }:$rs1, i32:{ *:[i32] }:$rs2)
64+
// CHECK-NEXT: GIR_BuildRootMI, /*Opcode*/GIMT_Encode2(MyTarget::tst3),
65+
// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/2, // rs1
66+
// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/3, // rs2
67+
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(TargetOpcode::COPY),
68+
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/0, /*OpIdx*/0, // DstI[R0]
69+
// CHECK-NEXT: GIR_AddRegister, /*InsnID*/1, GIMT_Encode2(MyTarget::R0), /*AddRegisterRegFlags*/GIMT_Encode2(0),
70+
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/2, /*Opcode*/GIMT_Encode2(TargetOpcode::COPY),
71+
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/2, /*OldInsnID*/0, /*OpIdx*/1, // DstI[R1]
72+
// CHECK-NEXT: GIR_AddRegister, /*InsnID*/2, GIMT_Encode2(MyTarget::R1), /*AddRegisterRegFlags*/GIMT_Encode2(0),
73+
// CHECK-NEXT: GIR_RootConstrainSelectedInstOperands,
74+
// CHECK-NEXT: // GIR_Coverage, 2,
75+
// CHECK-NEXT: GIR_EraseRootFromParent_Done,

llvm/utils/TableGen/GlobalISelEmitter.cpp

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,12 @@ class GlobalISelEmitter final : public GlobalISelMatchTableExecutorEmitter {
404404
createInstructionRenderer(action_iterator InsertPt, RuleMatcher &M,
405405
const TreePatternNode &Dst) const;
406406

407+
Expected<action_iterator> importDefRenderers(action_iterator InsertPt,
408+
RuleMatcher &M,
409+
BuildMIAction &DstMIBuilder,
410+
const TreePatternNode &Dst,
411+
bool IsRoot) const;
412+
407413
Expected<action_iterator>
408414
importExplicitDefRenderers(action_iterator InsertPt, RuleMatcher &M,
409415
BuildMIAction &DstMIBuilder,
@@ -434,8 +440,6 @@ class GlobalISelEmitter final : public GlobalISelMatchTableExecutorEmitter {
434440
Error importDefaultOperandRenderers(action_iterator InsertPt, RuleMatcher &M,
435441
BuildMIAction &DstMIBuilder,
436442
const DAGDefaultOperand &DefaultOp) const;
437-
Error importImplicitDefRenderers(BuildMIAction &DstMIBuilder,
438-
ArrayRef<const Record *> ImplicitDefs) const;
439443

440444
/// Analyze pattern \p P, returning a matcher for it if possible.
441445
/// Otherwise, return an Error explaining why we don't support it.
@@ -1423,9 +1427,9 @@ Expected<BuildMIAction &> GlobalISelEmitter::createAndImportInstructionRenderer(
14231427
CopyToPhysRegMIBuilder.addRenderer<CopyPhysRegRenderer>(PhysInput.first);
14241428
}
14251429

1426-
if (auto Error = importExplicitDefRenderers(InsertPt, M, DstMIBuilder, Dst,
1427-
/*IsRoot=*/true)
1428-
.takeError())
1430+
if (auto Error =
1431+
importDefRenderers(InsertPt, M, DstMIBuilder, Dst, /*IsRoot=*/true)
1432+
.takeError())
14291433
return std::move(Error);
14301434

14311435
if (auto Error = importExplicitUseRenderers(InsertPt, M, DstMIBuilder, Dst)
@@ -1453,8 +1457,8 @@ GlobalISelEmitter::createAndImportSubInstructionRenderer(
14531457
DstMIBuilder.addRenderer<TempRegRenderer>(TempRegID, true);
14541458

14551459
// Handle additional (ignored) results.
1456-
InsertPtOrError = importExplicitDefRenderers(
1457-
std::prev(*InsertPtOrError), M, DstMIBuilder, Dst, /*IsRoot=*/false);
1460+
InsertPtOrError = importDefRenderers(std::prev(*InsertPtOrError), M,
1461+
DstMIBuilder, Dst, /*IsRoot=*/false);
14581462
if (auto Error = InsertPtOrError.takeError())
14591463
return std::move(Error);
14601464

@@ -1493,7 +1497,7 @@ GlobalISelEmitter::createInstructionRenderer(action_iterator InsertPt,
14931497
DstI);
14941498
}
14951499

1496-
Expected<action_iterator> GlobalISelEmitter::importExplicitDefRenderers(
1500+
Expected<action_iterator> GlobalISelEmitter::importDefRenderers(
14971501
action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder,
14981502
const TreePatternNode &Dst, bool IsRoot) const {
14991503
const CodeGenInstruction *DstI = DstMIBuilder.getCGI();
@@ -1551,11 +1555,21 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitDefRenderers(
15511555
TempRegID, /*IsDef=*/true, /*SubReg=*/nullptr, /*IsDead=*/true);
15521556
}
15531557

1554-
// Implicit defs are not currently supported, mark all of them as dead.
1558+
// Process implicit defs.
15551559
for (const Record *Reg : DstI->ImplicitDefs) {
15561560
std::string OpName = getMangledRootDefName(Reg->getName());
1557-
assert(!M.hasOperand(OpName) && "The pattern should've been rejected");
1558-
DstMIBuilder.setDeadImplicitDef(Reg);
1561+
1562+
if (!IsRoot || !M.hasOperand(OpName)) {
1563+
DstMIBuilder.setDeadImplicitDef(Reg);
1564+
continue;
1565+
}
1566+
1567+
BuildMIAction &CopyBuilder = M.addAction<BuildMIAction>(
1568+
M.allocateOutputInsnID(), &Target.getInstruction(RK.getDef("COPY")));
1569+
1570+
StringRef PermanentRef = M.getOperandMatcher(OpName).getSymbolicName();
1571+
CopyBuilder.addRenderer<CopyRenderer>(PermanentRef);
1572+
CopyBuilder.addRenderer<AddRegisterRenderer>(Target, Reg);
15591573
}
15601574

15611575
return InsertPt;
@@ -1775,13 +1789,6 @@ Error GlobalISelEmitter::importDefaultOperandRenderers(
17751789
return Error::success();
17761790
}
17771791

1778-
Error GlobalISelEmitter::importImplicitDefRenderers(
1779-
BuildMIAction &DstMIBuilder, ArrayRef<const Record *> ImplicitDefs) const {
1780-
if (!ImplicitDefs.empty())
1781-
return failedImport("Pattern defines a physical register");
1782-
return Error::success();
1783-
}
1784-
17851792
Error GlobalISelEmitter::constrainOperands(action_iterator InsertPt,
17861793
RuleMatcher &M, unsigned InsnID,
17871794
const TreePatternNode &Dst) const {
@@ -2158,13 +2165,14 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
21582165
unsigned DstExpDefs = DstI.Operands.NumDefs,
21592166
DstNumDefs = DstI.ImplicitDefs.size() + DstExpDefs,
21602167
SrcNumDefs = Src.getExtTypes().size();
2168+
2169+
bool FoundNoUsePred = false;
21612170
if (DstNumDefs < SrcNumDefs) {
21622171
if (DstNumDefs != 0)
21632172
return failedImport("Src pattern result has more defs than dst MI (" +
21642173
to_string(SrcNumDefs) + " def(s) vs " +
21652174
to_string(DstNumDefs) + " def(s))");
21662175

2167-
bool FoundNoUsePred = false;
21682176
for (const auto &Pred : InsnMatcher.predicates()) {
21692177
if ((FoundNoUsePred = isa<NoUsePredicateMatcher>(Pred.get())))
21702178
break;
@@ -2177,15 +2185,24 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
21772185

21782186
// The root of the match also has constraints on the register bank so that it
21792187
// matches the result instruction.
2180-
unsigned N = std::min(DstExpDefs, SrcNumDefs);
2181-
for (unsigned I = 0; I < N; ++I) {
2182-
const auto &DstIOperand = DstI.Operands[I];
2188+
for (unsigned I = 0; I < SrcNumDefs; ++I) {
2189+
if (FoundNoUsePred)
2190+
continue;
21832191

21842192
OperandMatcher &OM = InsnMatcher.getOperand(I);
2193+
2194+
if (I >= DstExpDefs) {
2195+
const Record *Reg = DstI.ImplicitDefs[I - DstExpDefs];
2196+
OM.setSymbolicName(getMangledRootDefName(Reg->getName()));
2197+
M.defineOperand(OM.getSymbolicName(), OM);
2198+
continue;
2199+
}
2200+
21852201
// The operand names declared in the DstI instruction are unrelated to
21862202
// those used in pattern's source and destination DAGs, so mangle the
21872203
// former to prevent implicitly adding unexpected
21882204
// GIM_CheckIsSameOperand predicates by the defineOperand method.
2205+
const CGIOperandList::OperandInfo &DstIOperand = DstI.Operands[I];
21892206
OM.setSymbolicName(getMangledRootDefName(DstIOperand.Name));
21902207
M.defineOperand(OM.getSymbolicName(), OM);
21912208

@@ -2203,11 +2220,6 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
22032220
return std::move(Error);
22042221
BuildMIAction &DstMIBuilder = DstMIBuilderOrError.get();
22052222

2206-
// Render the implicit defs.
2207-
// These are only added to the root of the result.
2208-
if (auto Error = importImplicitDefRenderers(DstMIBuilder, P.getDstRegs()))
2209-
return std::move(Error);
2210-
22112223
DstMIBuilder.chooseInsnToMutate(M);
22122224

22132225
// Constrain the registers to classes. This is normally derived from the

0 commit comments

Comments
 (0)