Skip to content

Commit 16c07cc

Browse files
committed
[TableGen][GISel] Learn to import patterns with optional/physreg defs
1 parent 86e4f09 commit 16c07cc

File tree

7 files changed

+133
-54
lines changed

7 files changed

+133
-54
lines changed

llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ def : GINodeEquiv<G_SDIV, sdiv>;
7070
def : GINodeEquiv<G_UDIV, udiv>;
7171
def : GINodeEquiv<G_SREM, srem>;
7272
def : GINodeEquiv<G_UREM, urem>;
73+
def : GINodeEquiv<G_SDIVREM, sdivrem>;
74+
def : GINodeEquiv<G_UDIVREM, udivrem>;
7375
def : GINodeEquiv<G_AND, and>;
7476
def : GINodeEquiv<G_OR, or>;
7577
def : GINodeEquiv<G_XOR, xor>;

llvm/test/CodeGen/X86/GlobalISel/mul-scalar.ll

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,17 @@
55
define i8 @test_mul_i8(i8 %arg1, i8 %arg2) nounwind {
66
; X64-LABEL: test_mul_i8:
77
; X64: # %bb.0:
8-
; X64-NEXT: movsbl %dil, %eax
9-
; X64-NEXT: imulb %sil
8+
; X64-NEXT: movl %edi, %eax
9+
; X64-NEXT: # kill: def $al killed $al killed $eax
10+
; X64-NEXT: mulb %sil
1011
; X64-NEXT: retq
1112
;
1213
; X86-LABEL: test_mul_i8:
1314
; X86: # %bb.0:
1415
; X86-NEXT: movl {{[0-9]+}}(%esp), %eax
1516
; X86-NEXT: movl {{[0-9]+}}(%esp), %ecx
16-
; X86-NEXT: cbtw
17-
; X86-NEXT: imulb %cl
17+
; X86-NEXT: # kill: def $al killed $al killed $eax
18+
; X86-NEXT: mulb %cl
1819
; X86-NEXT: retl
1920
%ret = mul i8 %arg1, %arg2
2021
ret i8 %ret

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: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,60 @@
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_ConstrainOperandRC, /*InsnID*/1, /*Op*/0, GIMT_Encode2(MyTarget::GPR32RegClassID),
33+
// CHECK-NEXT: GIR_RootConstrainSelectedInstOperands,
34+
// CHECK-NEXT: // GIR_Coverage, 3,
35+
// CHECK-NEXT: GIR_EraseRootFromParent_Done,
36+
37+
// CHECK-LABEL: // (udivrem:{ *:[i32] }:{ *:[i32] } i32:{ *:[i32] }:$rs1, i32:{ *:[i32] }:$rs2) => (tst2:{ *:[i32] }:{ *:[i32] } i32:{ *:[i32] }:$rs1, i32:{ *:[i32] }:$rs2)
38+
// CHECK-NEXT: GIR_BuildRootMI, /*Opcode*/GIMT_Encode2(MyTarget::tst2),
39+
// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/0, // DstI[rd]
40+
// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/2, // rs1
41+
// CHECK-NEXT: GIR_RootToRootCopy, /*OpIdx*/3, // rs2
42+
// CHECK-NEXT: GIR_SetImplicitDefDead, /*InsnID*/0, /*OpIdx for MyTarget::R1*/1,
43+
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(TargetOpcode::COPY),
44+
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/0, /*OpIdx*/1, // DstI[R0]
45+
// CHECK-NEXT: GIR_AddRegister, /*InsnID*/1, GIMT_Encode2(MyTarget::R0), /*AddRegisterRegFlags*/GIMT_Encode2(0),
46+
// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/1, /*Op*/0, GIMT_Encode2(MyTarget::GPR32RegClassID),
47+
// CHECK-NEXT: GIR_RootConstrainSelectedInstOperands,
48+
// CHECK-NEXT: // GIR_Coverage, 1,
49+
// CHECK-NEXT: GIR_EraseRootFromParent_Done,
50+
51+
// CHECK-LABEL: // (get_fpenv:{ *:[i32] }) => (tst1:{ *:[i32] })
52+
// CHECK-NEXT: GIR_BuildRootMI, /*Opcode*/GIMT_Encode2(MyTarget::tst1),
53+
// CHECK-NEXT: GIR_SetImplicitDefDead, /*InsnID*/0, /*OpIdx for MyTarget::R1*/1,
54+
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/GIMT_Encode2(TargetOpcode::COPY),
55+
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/0, /*OpIdx*/0, // DstI[R0]
56+
// CHECK-NEXT: GIR_AddRegister, /*InsnID*/1, GIMT_Encode2(MyTarget::R0), /*AddRegisterRegFlags*/GIMT_Encode2(0),
57+
// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/1, /*Op*/0, GIMT_Encode2(MyTarget::GPR32RegClassID),
58+
// CHECK-NEXT: GIR_RootConstrainSelectedInstOperands,
59+
// CHECK-NEXT: // GIR_Coverage, 0,
60+
// CHECK-NEXT: GIR_EraseRootFromParent_Done,

llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1993,10 +1993,13 @@ void AddRegisterRenderer::emitRenderOpcodes(MatchTable &Table,
19931993
// TODO: This is encoded as a 64-bit element, but only 16 or 32-bits are
19941994
// really needed for a physical register reference. We can pack the
19951995
// register and flags in a single field.
1996-
if (IsDef)
1997-
Table << MatchTable::NamedValue(2, "RegState::Define");
1998-
else
1996+
if (IsDef) {
1997+
Table << MatchTable::NamedValue(
1998+
2, IsDead ? "RegState::Define | RegState::Dead" : "RegState::Define");
1999+
} else {
2000+
assert(!IsDead && "A use cannot be dead");
19992001
Table << MatchTable::IntValue(2, 0);
2002+
}
20002003
Table << MatchTable::LineBreak;
20012004
}
20022005

llvm/utils/TableGen/Common/GlobalISel/GlobalISelMatchTable.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2091,13 +2091,15 @@ class AddRegisterRenderer : public OperandRenderer {
20912091
unsigned InsnID;
20922092
const Record *RegisterDef;
20932093
bool IsDef;
2094+
bool IsDead;
20942095
const CodeGenTarget &Target;
20952096

20962097
public:
20972098
AddRegisterRenderer(unsigned InsnID, const CodeGenTarget &Target,
2098-
const Record *RegisterDef, bool IsDef = false)
2099+
const Record *RegisterDef, bool IsDef = false,
2100+
bool IsDead = false)
20992101
: OperandRenderer(OR_Register), InsnID(InsnID), RegisterDef(RegisterDef),
2100-
IsDef(IsDef), Target(Target) {}
2102+
IsDef(IsDef), IsDead(IsDead), Target(Target) {}
21012103

21022104
static bool classof(const OperandRenderer *R) {
21032105
return R->getKind() == OR_Register;

llvm/utils/TableGen/GlobalISelEmitter.cpp

Lines changed: 59 additions & 37 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.
@@ -1375,8 +1374,9 @@ Expected<BuildMIAction &> GlobalISelEmitter::createAndImportInstructionRenderer(
13751374
CopyToPhysRegMIBuilder.addRenderer<CopyPhysRegRenderer>(PhysInput.first);
13761375
}
13771376

1378-
if (auto Error = importExplicitDefRenderers(InsertPt, M, DstMIBuilder, Dst)
1379-
.takeError())
1377+
if (auto Error =
1378+
importDefRenderers(InsertPt, M, DstMIBuilder, Dst, /*IsRoot=*/true)
1379+
.takeError())
13801380
return std::move(Error);
13811381

13821382
if (auto Error = importExplicitUseRenderers(InsertPt, M, DstMIBuilder, Dst)
@@ -1404,8 +1404,8 @@ GlobalISelEmitter::createAndImportSubInstructionRenderer(
14041404
DstMIBuilder.addRenderer<TempRegRenderer>(TempRegID, true);
14051405

14061406
// Handle additional (ignored) results.
1407-
InsertPtOrError = importExplicitDefRenderers(std::prev(*InsertPtOrError), M,
1408-
DstMIBuilder, Dst, /*Start=*/1);
1407+
InsertPtOrError = importDefRenderers(std::prev(*InsertPtOrError), M,
1408+
DstMIBuilder, Dst, /*IsRoot=*/false);
14091409
if (auto Error = InsertPtOrError.takeError())
14101410
return std::move(Error);
14111411

@@ -1442,19 +1442,15 @@ Expected<action_iterator> GlobalISelEmitter::createInstructionRenderer(
14421442
DstI);
14431443
}
14441444

1445-
Expected<action_iterator> GlobalISelEmitter::importExplicitDefRenderers(
1445+
Expected<action_iterator> GlobalISelEmitter::importDefRenderers(
14461446
action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder,
1447-
const TreePatternNode &Dst, unsigned Start) {
1447+
const TreePatternNode &Dst, bool IsRoot) {
14481448
const CodeGenInstruction *DstI = DstMIBuilder.getCGI();
14491449

1450-
// Some instructions have multiple defs, but are missing a type entry
1451-
// (e.g. s_cc_out operands).
1452-
if (Dst.getExtTypes().size() < DstI->Operands.NumDefs)
1453-
return failedImport("unhandled discarded def");
1454-
14551450
// Process explicit defs. The caller may have already handled the first def.
1456-
for (unsigned I = Start, E = DstI->Operands.NumDefs; I != E; ++I) {
1457-
std::string OpName = getMangledRootDefName(DstI->Operands[I].Name);
1451+
for (unsigned I = IsRoot ? 0 : 1, E = DstI->Operands.NumDefs; I != E; ++I) {
1452+
const CGIOperandList::OperandInfo &OpInfo = DstI->Operands[I];
1453+
std::string OpName = getMangledRootDefName(OpInfo.Name);
14581454

14591455
// If the def is used in the source DAG, forward it.
14601456
if (M.hasOperand(OpName)) {
@@ -1465,6 +1461,19 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitDefRenderers(
14651461
continue;
14661462
}
14671463

1464+
if (OpInfo.Rec->isSubClassOf("OptionalDefOperand")) {
1465+
const DAGDefaultOperand &ComplexOp = CGP.getDefaultOperand(OpInfo.Rec);
1466+
for (const TreePatternNode &SubOp :
1467+
make_pointee_range(ComplexOp.DefaultOps)) {
1468+
const Record *Reg = cast<DefInit>(SubOp.getLeafValue())->getDef();
1469+
assert(Reg->isSubClassOf("Register") &&
1470+
"Optional def can only be a register");
1471+
DstMIBuilder.addRenderer<AddRegisterRenderer>(
1472+
Target, Reg, /*IsDef=*/true, /*IsDead=*/true);
1473+
}
1474+
continue;
1475+
}
1476+
14681477
// The def is discarded, create a dead virtual register for it.
14691478
const TypeSetByHwMode &ExtTy = Dst.getExtType(I);
14701479
if (!ExtTy.isMachineValueType())
@@ -1484,8 +1493,23 @@ Expected<action_iterator> GlobalISelEmitter::importExplicitDefRenderers(
14841493
// Implicit defs are not currently supported, mark all of them as dead.
14851494
for (const Record *Reg : DstI->ImplicitDefs) {
14861495
std::string OpName = getMangledRootDefName(Reg->getName());
1487-
assert(!M.hasOperand(OpName) && "The pattern should've been rejected");
1488-
DstMIBuilder.setDeadImplicitDef(Reg);
1496+
1497+
if (!IsRoot || !M.hasOperand(OpName)) {
1498+
DstMIBuilder.setDeadImplicitDef(Reg);
1499+
continue;
1500+
}
1501+
1502+
BuildMIAction &CopyBuilder = M.addAction<BuildMIAction>(
1503+
M.allocateOutputInsnID(), &Target.getInstruction(RK.getDef("COPY")));
1504+
1505+
StringRef PermanentRef = M.getOperandMatcher(OpName).getSymbolicName();
1506+
CopyBuilder.addRenderer<CopyRenderer>(PermanentRef);
1507+
CopyBuilder.addRenderer<AddRegisterRenderer>(Target, Reg);
1508+
1509+
const CodeGenRegisterClass *RC = CGRegs.getRegClassForRegister(Reg);
1510+
assert(RC);
1511+
M.addAction<ConstrainOperandToRegClassAction>(CopyBuilder.getInsnID(),
1512+
/*OpIdx=*/0, *RC);
14891513
}
14901514

14911515
return InsertPt;
@@ -1708,13 +1732,6 @@ Error GlobalISelEmitter::importDefaultOperandRenderers(
17081732
return Error::success();
17091733
}
17101734

1711-
Error GlobalISelEmitter::importImplicitDefRenderers(
1712-
BuildMIAction &DstMIBuilder, ArrayRef<const Record *> ImplicitDefs) const {
1713-
if (!ImplicitDefs.empty())
1714-
return failedImport("Pattern defines a physical register");
1715-
return Error::success();
1716-
}
1717-
17181735
Error GlobalISelEmitter::constrainOperands(action_iterator InsertPt,
17191736
RuleMatcher &M, unsigned InsnID,
17201737
const TreePatternNode &Dst) {
@@ -2091,13 +2108,14 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
20912108
unsigned DstExpDefs = DstI.Operands.NumDefs,
20922109
DstNumDefs = DstI.ImplicitDefs.size() + DstExpDefs,
20932110
SrcNumDefs = Src.getExtTypes().size();
2111+
2112+
bool FoundNoUsePred = false;
20942113
if (DstNumDefs < SrcNumDefs) {
20952114
if (DstNumDefs != 0)
20962115
return failedImport("Src pattern result has more defs than dst MI (" +
20972116
to_string(SrcNumDefs) + " def(s) vs " +
20982117
to_string(DstNumDefs) + " def(s))");
20992118

2100-
bool FoundNoUsePred = false;
21012119
for (const auto &Pred : InsnMatcher.predicates()) {
21022120
if ((FoundNoUsePred = isa<NoUsePredicateMatcher>(Pred.get())))
21032121
break;
@@ -2110,15 +2128,24 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
21102128

21112129
// The root of the match also has constraints on the register bank so that it
21122130
// matches the result instruction.
2113-
unsigned N = std::min(DstExpDefs, SrcNumDefs);
2114-
for (unsigned I = 0; I < N; ++I) {
2115-
const auto &DstIOperand = DstI.Operands[I];
2131+
for (unsigned I = 0; I < SrcNumDefs; ++I) {
2132+
if (FoundNoUsePred)
2133+
continue;
21162134

21172135
OperandMatcher &OM = InsnMatcher.getOperand(I);
2136+
2137+
if (I >= DstExpDefs) {
2138+
const Record *Reg = DstI.ImplicitDefs[I - DstExpDefs];
2139+
OM.setSymbolicName(getMangledRootDefName(Reg->getName()));
2140+
M.defineOperand(OM.getSymbolicName(), OM);
2141+
continue;
2142+
}
2143+
21182144
// The operand names declared in the DstI instruction are unrelated to
21192145
// those used in pattern's source and destination DAGs, so mangle the
21202146
// former to prevent implicitly adding unexpected
21212147
// GIM_CheckIsSameOperand predicates by the defineOperand method.
2148+
const CGIOperandList::OperandInfo &DstIOperand = DstI.Operands[I];
21222149
OM.setSymbolicName(getMangledRootDefName(DstIOperand.Name));
21232150
M.defineOperand(OM.getSymbolicName(), OM);
21242151

@@ -2136,11 +2163,6 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
21362163
return std::move(Error);
21372164
BuildMIAction &DstMIBuilder = DstMIBuilderOrError.get();
21382165

2139-
// Render the implicit defs.
2140-
// These are only added to the root of the result.
2141-
if (auto Error = importImplicitDefRenderers(DstMIBuilder, P.getDstRegs()))
2142-
return std::move(Error);
2143-
21442166
DstMIBuilder.chooseInsnToMutate(M);
21452167

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

0 commit comments

Comments
 (0)