Skip to content

Commit 54a0395

Browse files
committed
[TableGen][GISel] Learn to import patterns with physreg defs
1 parent b2d7312 commit 54a0395

File tree

4 files changed

+123
-48
lines changed

4 files changed

+123
-48
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 < %s | 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 | FileCheck %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: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -404,9 +404,11 @@ class GlobalISelEmitter final : public GlobalISelMatchTableExecutorEmitter {
404404
createInstructionRenderer(action_iterator InsertPt, RuleMatcher &M,
405405
const TreePatternNode &Dst) const;
406406

407-
Expected<action_iterator> importExplicitDefRenderers(
408-
action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder,
409-
const TreePatternNode &Dst, unsigned Start = 0) const;
407+
Expected<action_iterator> importDefRenderers(action_iterator InsertPt,
408+
RuleMatcher &M,
409+
BuildMIAction &DstMIBuilder,
410+
const TreePatternNode &Dst,
411+
bool IsRoot) const;
410412

411413
Expected<action_iterator>
412414
importExplicitUseRenderers(action_iterator InsertPt, RuleMatcher &M,
@@ -419,8 +421,6 @@ class GlobalISelEmitter final : public GlobalISelMatchTableExecutorEmitter {
419421
Error importDefaultOperandRenderers(action_iterator InsertPt, RuleMatcher &M,
420422
BuildMIAction &DstMIBuilder,
421423
const DAGDefaultOperand &DefaultOp) const;
422-
Error importImplicitDefRenderers(BuildMIAction &DstMIBuilder,
423-
ArrayRef<const Record *> ImplicitDefs) const;
424424

425425
/// Analyze pattern \p P, returning a matcher for it if possible.
426426
/// Otherwise, return an Error explaining why we don't support it.
@@ -1379,8 +1379,9 @@ Expected<BuildMIAction &> GlobalISelEmitter::createAndImportInstructionRenderer(
13791379
CopyToPhysRegMIBuilder.addRenderer<CopyPhysRegRenderer>(PhysInput.first);
13801380
}
13811381

1382-
if (auto Error = importExplicitDefRenderers(InsertPt, M, DstMIBuilder, Dst)
1383-
.takeError())
1382+
if (auto Error =
1383+
importDefRenderers(InsertPt, M, DstMIBuilder, Dst, /*IsRoot=*/true)
1384+
.takeError())
13841385
return std::move(Error);
13851386

13861387
if (auto Error = importExplicitUseRenderers(InsertPt, M, DstMIBuilder, Dst)
@@ -1408,8 +1409,8 @@ GlobalISelEmitter::createAndImportSubInstructionRenderer(
14081409
DstMIBuilder.addRenderer<TempRegRenderer>(TempRegID, true);
14091410

14101411
// Handle additional (ignored) results.
1411-
InsertPtOrError = importExplicitDefRenderers(std::prev(*InsertPtOrError), M,
1412-
DstMIBuilder, Dst, /*Start=*/1);
1412+
InsertPtOrError = importDefRenderers(std::prev(*InsertPtOrError), M,
1413+
DstMIBuilder, Dst, /*IsRoot=*/false);
14131414
if (auto Error = InsertPtOrError.takeError())
14141415
return std::move(Error);
14151416

@@ -1448,13 +1449,13 @@ GlobalISelEmitter::createInstructionRenderer(action_iterator InsertPt,
14481449
DstI);
14491450
}
14501451

1451-
Expected<action_iterator> GlobalISelEmitter::importExplicitDefRenderers(
1452+
Expected<action_iterator> GlobalISelEmitter::importDefRenderers(
14521453
action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder,
1453-
const TreePatternNode &Dst, unsigned Start) const {
1454+
const TreePatternNode &Dst, bool IsRoot) const {
14541455
const CodeGenInstruction *DstI = DstMIBuilder.getCGI();
14551456

14561457
// Process explicit defs. The caller may have already handled the first def.
1457-
for (unsigned I = Start, E = DstI->Operands.NumDefs; I != E; ++I) {
1458+
for (unsigned I = IsRoot ? 0 : 1, E = DstI->Operands.NumDefs; I != E; ++I) {
14581459
const CGIOperandList::OperandInfo &OpInfo = DstI->Operands[I];
14591460
std::string OpName = getMangledRootDefName(OpInfo.Name);
14601461

@@ -1506,11 +1507,21 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitDefRenderers(
15061507
TempRegID, /*IsDef=*/true, /*SubReg=*/nullptr, /*IsDead=*/true);
15071508
}
15081509

1509-
// Implicit defs are not currently supported, mark all of them as dead.
1510+
// Process implicit defs.
15101511
for (const Record *Reg : DstI->ImplicitDefs) {
15111512
std::string OpName = getMangledRootDefName(Reg->getName());
1512-
assert(!M.hasOperand(OpName) && "The pattern should've been rejected");
1513-
DstMIBuilder.setDeadImplicitDef(Reg);
1513+
1514+
if (!IsRoot || !M.hasOperand(OpName)) {
1515+
DstMIBuilder.setDeadImplicitDef(Reg);
1516+
continue;
1517+
}
1518+
1519+
BuildMIAction &CopyBuilder = M.addAction<BuildMIAction>(
1520+
M.allocateOutputInsnID(), &Target.getInstruction(RK.getDef("COPY")));
1521+
1522+
StringRef PermanentRef = M.getOperandMatcher(OpName).getSymbolicName();
1523+
CopyBuilder.addRenderer<CopyRenderer>(PermanentRef);
1524+
CopyBuilder.addRenderer<AddRegisterRenderer>(Target, Reg);
15141525
}
15151526

15161527
return InsertPt;
@@ -1733,13 +1744,6 @@ Error GlobalISelEmitter::importDefaultOperandRenderers(
17331744
return Error::success();
17341745
}
17351746

1736-
Error GlobalISelEmitter::importImplicitDefRenderers(
1737-
BuildMIAction &DstMIBuilder, ArrayRef<const Record *> ImplicitDefs) const {
1738-
if (!ImplicitDefs.empty())
1739-
return failedImport("Pattern defines a physical register");
1740-
return Error::success();
1741-
}
1742-
17431747
Error GlobalISelEmitter::constrainOperands(action_iterator InsertPt,
17441748
RuleMatcher &M, unsigned InsnID,
17451749
const TreePatternNode &Dst) const {
@@ -2116,13 +2120,14 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
21162120
unsigned DstExpDefs = DstI.Operands.NumDefs,
21172121
DstNumDefs = DstI.ImplicitDefs.size() + DstExpDefs,
21182122
SrcNumDefs = Src.getExtTypes().size();
2123+
2124+
bool FoundNoUsePred = false;
21192125
if (DstNumDefs < SrcNumDefs) {
21202126
if (DstNumDefs != 0)
21212127
return failedImport("Src pattern result has more defs than dst MI (" +
21222128
to_string(SrcNumDefs) + " def(s) vs " +
21232129
to_string(DstNumDefs) + " def(s))");
21242130

2125-
bool FoundNoUsePred = false;
21262131
for (const auto &Pred : InsnMatcher.predicates()) {
21272132
if ((FoundNoUsePred = isa<NoUsePredicateMatcher>(Pred.get())))
21282133
break;
@@ -2135,15 +2140,24 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
21352140

21362141
// The root of the match also has constraints on the register bank so that it
21372142
// matches the result instruction.
2138-
unsigned N = std::min(DstExpDefs, SrcNumDefs);
2139-
for (unsigned I = 0; I < N; ++I) {
2140-
const auto &DstIOperand = DstI.Operands[I];
2143+
for (unsigned I = 0; I < SrcNumDefs; ++I) {
2144+
if (FoundNoUsePred)
2145+
continue;
21412146

21422147
OperandMatcher &OM = InsnMatcher.getOperand(I);
2148+
2149+
if (I >= DstExpDefs) {
2150+
const Record *Reg = DstI.ImplicitDefs[I - DstExpDefs];
2151+
OM.setSymbolicName(getMangledRootDefName(Reg->getName()));
2152+
M.defineOperand(OM.getSymbolicName(), OM);
2153+
continue;
2154+
}
2155+
21432156
// The operand names declared in the DstI instruction are unrelated to
21442157
// those used in pattern's source and destination DAGs, so mangle the
21452158
// former to prevent implicitly adding unexpected
21462159
// GIM_CheckIsSameOperand predicates by the defineOperand method.
2160+
const CGIOperandList::OperandInfo &DstIOperand = DstI.Operands[I];
21472161
OM.setSymbolicName(getMangledRootDefName(DstIOperand.Name));
21482162
M.defineOperand(OM.getSymbolicName(), OM);
21492163

@@ -2161,11 +2175,6 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
21612175
return std::move(Error);
21622176
BuildMIAction &DstMIBuilder = DstMIBuilderOrError.get();
21632177

2164-
// Render the implicit defs.
2165-
// These are only added to the root of the result.
2166-
if (auto Error = importImplicitDefRenderers(DstMIBuilder, P.getDstRegs()))
2167-
return std::move(Error);
2168-
21692178
DstMIBuilder.chooseInsnToMutate(M);
21702179

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

0 commit comments

Comments
 (0)