Skip to content

Commit 8f8b945

Browse files
committed
[TableGen][GISel] Learn to import patterns with physreg defs
1 parent cbf931e commit 8f8b945

File tree

4 files changed

+106
-50
lines changed

4 files changed

+106
-50
lines changed

llvm/lib/Target/X86/X86InstrArithmetic.td

Lines changed: 10 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,13 @@ 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+
let GISelShouldIgnore = true in {
130+
def : Pat<(mul AL, i8:$src1), (MUL8r $src1)>;
131+
def : Pat<(mul AL, (loadi8 addr:$src1)), (MUL8m addr:$src1)>;
132+
}
132133

133134
multiclass Div<bits<8> o, string m, Format RegMRM, Format MemMRM> {
134135
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<"r0"> { 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: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,57 @@
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 -I %p/../../include -I %p/Common %s | FileCheck %s
22

33
include "llvm/Target/Target.td"
44
include "GlobalISelEmitterCommon.td"
55

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))]>;
6+
let Defs = [R0, R1] in
7+
def tst1 : I<(outs), (ins), [(set R0, (get_fpenv))]>;
98

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))]>;
9+
let Defs = [R0, R1] in
10+
def tst2 : I<(outs GPR32:$rd), (ins GPR32:$rs1, GPR32:$rs2),
11+
[(set GPR32:$rd, R0, (udivrem i32:$rs1, i32:$rs2))]>;
12+
13+
def : Pat<(sdiv i32:$rs1, i32:$rs2), (tst2 $rs1, $rs2)>;
14+
def : Pat<(sdivrem i32:$rs1, i32:$rs2), (tst2 $rs1, $rs2)>;
15+
16+
// CHECK-LABEL: // (sdiv:{ *:[i32] } i32:{ *:[i32] }:$rs1, i32:{ *:[i32] }:$rs2) => (tst2:{ *:[i32] }:{ *:[i32] } ?:{ *:[i32] }:$rs1, ?:{ *:[i32] }:$rs2)
17+
// CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/GIMT_Encode2(MyTarget::tst2),
18+
// CHECK-NEXT: GIR_AddImplicitDef, /*InsnID*/0, GIMT_Encode2(MyTarget::R0), GIMT_Encode2(RegState::Dead),
19+
// CHECK-NEXT: GIR_AddImplicitDef, /*InsnID*/0, GIMT_Encode2(MyTarget::R1), GIMT_Encode2(RegState::Dead),
20+
// CHECK-NEXT: GIR_RootConstrainSelectedInstOperands,
21+
// CHECK-NEXT: // GIR_Coverage, 2,
22+
23+
// CHECK-LABEL: // (sdivrem:{ *:[i32] }:{ *:[i32] } i32:{ *:[i32] }:$rs1, i32:{ *:[i32] }:$rs2) => (tst2:{ *:[i32] }:{ *:[i32] } ?:{ *:[i32] }:$rs1, ?:{ *:[i32] }:$rs2)
24+
// CHECK-NEXT: GIR_BuildRootMI, /*Opcode*/GIMT_Encode2(MyTarget::tst2),
25+
// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/0, // DstI[rd]
26+
// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/2, // rs1
27+
// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/3, // rs2
28+
// CHECK-NEXT: GIR_SetImplicitDefDead, /*InsnID*/0, /*OpIdx for MyTarget::R1*/1,
29+
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(TargetOpcode::COPY),
30+
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/0, /*OpIdx*/1, // DstI[R0]
31+
// CHECK-NEXT: GIR_AddRegister, /*InsnID*/1, GIMT_Encode2(MyTarget::R0), /*AddRegisterRegFlags*/GIMT_Encode2(0),
32+
// CHECK-NEXT: GIR_RootConstrainSelectedInstOperands,
33+
// CHECK-NEXT: // GIR_Coverage, 3,
34+
// CHECK-NEXT: GIR_EraseRootFromParent_Done,
35+
36+
// CHECK-LABEL: // (udivrem:{ *:[i32] }:{ *:[i32] } i32:{ *:[i32] }:$rs1, i32:{ *:[i32] }:$rs2) => (tst2:{ *:[i32] }:{ *:[i32] } i32:{ *:[i32] }:$rs1, i32:{ *:[i32] }:$rs2)
37+
// CHECK-NEXT: GIR_BuildRootMI, /*Opcode*/GIMT_Encode2(MyTarget::tst2),
38+
// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/0, // DstI[rd]
39+
// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/2, // rs1
40+
// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/3, // rs2
41+
// CHECK-NEXT: GIR_SetImplicitDefDead, /*InsnID*/0, /*OpIdx for MyTarget::R1*/1,
42+
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(TargetOpcode::COPY),
43+
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/0, /*OpIdx*/1, // DstI[R0]
44+
// CHECK-NEXT: GIR_AddRegister, /*InsnID*/1, GIMT_Encode2(MyTarget::R0), /*AddRegisterRegFlags*/GIMT_Encode2(0),
45+
// CHECK-NEXT: GIR_RootConstrainSelectedInstOperands,
46+
// CHECK-NEXT: // GIR_Coverage, 1,
47+
// CHECK-NEXT: GIR_EraseRootFromParent_Done,
48+
49+
// CHECK-LABEL: // (get_fpenv:{ *:[i32] }) => (tst1:{ *:[i32] })
50+
// CHECK-NEXT: GIR_BuildRootMI, /*Opcode*/GIMT_Encode2(MyTarget::tst1),
51+
// CHECK-NEXT: GIR_SetImplicitDefDead, /*InsnID*/0, /*OpIdx for MyTarget::R1*/1,
52+
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(TargetOpcode::COPY),
53+
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/0, /*OpIdx*/0, // DstI[R0]
54+
// CHECK-NEXT: GIR_AddRegister, /*InsnID*/1, GIMT_Encode2(MyTarget::R0), /*AddRegisterRegFlags*/GIMT_Encode2(0),
55+
// CHECK-NEXT: GIR_RootConstrainSelectedInstOperands,
56+
// CHECK-NEXT: // GIR_Coverage, 0,
57+
// CHECK-NEXT: GIR_EraseRootFromParent_Done,

llvm/utils/TableGen/GlobalISelEmitter.cpp

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

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

412413
Expected<action_iterator>
413414
importExplicitUseRenderers(action_iterator InsertPt, RuleMatcher &M,
@@ -420,8 +421,6 @@ class GlobalISelEmitter final : public GlobalISelMatchTableExecutorEmitter {
420421
Error importDefaultOperandRenderers(action_iterator InsertPt, RuleMatcher &M,
421422
BuildMIAction &DstMIBuilder,
422423
const DAGDefaultOperand &DefaultOp) const;
423-
Error importImplicitDefRenderers(BuildMIAction &DstMIBuilder,
424-
ArrayRef<const Record *> ImplicitDefs) const;
425424

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

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

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

14101410
// Handle additional (ignored) results.
1411-
InsertPtOrError = importExplicitDefRenderers(std::prev(*InsertPtOrError), M,
1412-
DstMIBuilder, Dst, /*Start=*/1);
1411+
InsertPtOrError = importDefRenderers(std::prev(*InsertPtOrError), M,
1412+
DstMIBuilder, Dst, /*IsRoot=*/false);
14131413
if (auto Error = InsertPtOrError.takeError())
14141414
return std::move(Error);
14151415

@@ -1446,13 +1446,14 @@ Expected<action_iterator> GlobalISelEmitter::createInstructionRenderer(
14461446
DstI);
14471447
}
14481448

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

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

@@ -1504,11 +1505,21 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitDefRenderers(
15041505
TempRegID, /*IsDef=*/true, /*SubReg=*/nullptr, /*IsDead=*/true);
15051506
}
15061507

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

15141525
return InsertPt;
@@ -1731,13 +1742,6 @@ Error GlobalISelEmitter::importDefaultOperandRenderers(
17311742
return Error::success();
17321743
}
17331744

1734-
Error GlobalISelEmitter::importImplicitDefRenderers(
1735-
BuildMIAction &DstMIBuilder, ArrayRef<const Record *> ImplicitDefs) const {
1736-
if (!ImplicitDefs.empty())
1737-
return failedImport("Pattern defines a physical register");
1738-
return Error::success();
1739-
}
1740-
17411745
Error GlobalISelEmitter::constrainOperands(action_iterator InsertPt,
17421746
RuleMatcher &M, unsigned InsnID,
17431747
const TreePatternNode &Dst) {
@@ -2114,13 +2118,14 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
21142118
unsigned DstExpDefs = DstI.Operands.NumDefs,
21152119
DstNumDefs = DstI.ImplicitDefs.size() + DstExpDefs,
21162120
SrcNumDefs = Src.getExtTypes().size();
2121+
2122+
bool FoundNoUsePred = false;
21172123
if (DstNumDefs < SrcNumDefs) {
21182124
if (DstNumDefs != 0)
21192125
return failedImport("Src pattern result has more defs than dst MI (" +
21202126
to_string(SrcNumDefs) + " def(s) vs " +
21212127
to_string(DstNumDefs) + " def(s))");
21222128

2123-
bool FoundNoUsePred = false;
21242129
for (const auto &Pred : InsnMatcher.predicates()) {
21252130
if ((FoundNoUsePred = isa<NoUsePredicateMatcher>(Pred.get())))
21262131
break;
@@ -2133,15 +2138,24 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
21332138

21342139
// The root of the match also has constraints on the register bank so that it
21352140
// matches the result instruction.
2136-
unsigned N = std::min(DstExpDefs, SrcNumDefs);
2137-
for (unsigned I = 0; I < N; ++I) {
2138-
const auto &DstIOperand = DstI.Operands[I];
2141+
for (unsigned I = 0; I < SrcNumDefs; ++I) {
2142+
if (FoundNoUsePred)
2143+
continue;
21392144

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

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

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

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

0 commit comments

Comments
 (0)