Skip to content

Commit 74bae94

Browse files
committed
TableGen: Support target specialized pseudoinstructions
Allow a target to steal the definition of a generic pseudoinstruction and remap the operands. This works by defining a new instruction, which will simply swap out the emitted entry in the InstrInfo table. This is intended to eliminate the C++ half of the implementation of PointerLikeRegClass. With RegClassByHwMode, the remaining usecase for PointerLikeRegClass are the common codegen pseudoinstructions. Every target maintains its own copy of the generic pseudo operand definitions anyway, so we can stub out the register operands with an appropriate class instead of waiting for runtime resolution. In the future we could probably take this a bit further. For example, there is a similar problem for ADJCALLSTACKUP/DOWN since they depend on target register definitions for the stack pointer register.
1 parent c1ac2e0 commit 74bae94

File tree

4 files changed

+242
-1
lines changed

4 files changed

+242
-1
lines changed

llvm/include/llvm/Target/Target.td

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1574,6 +1574,99 @@ def CONVERGENCECTRL_GLUE : StandardPseudoInstruction {
15741574
}
15751575
}
15761576

1577+
/// Allow a target to replace the instruction definition of a
1578+
/// StandardPseudoInstruction. A target should only define one
1579+
/// instance of this per instruction.
1580+
///
1581+
/// This is intended to allow targets to specify the register class
1582+
/// used for pointers. It should not be used to change the fundamental
1583+
/// operand structure (e.g., this should not add or remove operands,
1584+
/// or change the operand types).
1585+
class TargetSpecializedStandardPseudoInstruction<
1586+
StandardPseudoInstruction base_inst> : Instruction {
1587+
1588+
StandardPseudoInstruction Instruction = base_inst;
1589+
let OutOperandList = base_inst.OutOperandList;
1590+
let InOperandList = base_inst.InOperandList;
1591+
1592+
// TODO: Copy everything
1593+
let usesCustomInserter = base_inst.usesCustomInserter;
1594+
let hasSideEffects = base_inst.hasSideEffects;
1595+
let mayLoad = base_inst.mayLoad;
1596+
let mayStore = base_inst.mayStore;
1597+
let isTerminator = base_inst.isTerminator;
1598+
let isBranch = base_inst.isBranch;
1599+
let isIndirectBranch = base_inst.isIndirectBranch;
1600+
let isEHScopeReturn = base_inst.isEHScopeReturn;
1601+
let isReturn = base_inst.isReturn;
1602+
let isCall = base_inst.isCall;
1603+
let hasCtrlDep = base_inst.hasCtrlDep;
1604+
let isReMaterializable = base_inst.isReMaterializable;
1605+
let isMeta = base_inst.isMeta;
1606+
let Size = base_inst.Size;
1607+
let isAsCheapAsAMove = base_inst.isAsCheapAsAMove;
1608+
let isPseudo = true;
1609+
let hasNoSchedulingInfo = true;
1610+
let isNotDuplicable = base_inst.isNotDuplicable;
1611+
let isConvergent = base_inst.isConvergent;
1612+
let hasExtraSrcRegAllocReq = base_inst.hasExtraSrcRegAllocReq;
1613+
let hasExtraDefRegAllocReq = base_inst.hasExtraDefRegAllocReq;
1614+
}
1615+
1616+
// All pseudo instructions which need a pointer register class, which
1617+
// should be specialized by a target.
1618+
defvar PseudosWithPtrOps = [
1619+
LOAD_STACK_GUARD,
1620+
PREALLOCATED_ARG,
1621+
PATCHABLE_EVENT_CALL,
1622+
PATCHABLE_TYPED_EVENT_CALL
1623+
];
1624+
1625+
1626+
/// Replace PointerLikeRegClass operands in OperandList with new_rc.
1627+
class RemapPointerOperandList<dag OperandList, RegisterClassLike new_rc> {
1628+
// Collect the set of names so we can query and rewrite them.
1629+
list<string> op_names = !foreach(i, !range(!size(OperandList)),
1630+
!getdagname(OperandList, i));
1631+
1632+
// Beautiful language. This would be a lot easier if !getdagarg
1633+
// didn't require a specific type. We can't just collect a list of
1634+
// the operand values and reconstruct the dag, since there isn't a
1635+
// common base class for all the field kinds used in
1636+
// pseudoinstruction definitions; therefore everything must be
1637+
// maintained as a dag, so use a foldl. Additionally, ? doesn't
1638+
// evaluate as false so we get even more noise.
1639+
dag ret =
1640+
!foldl(OperandList, op_names, acc, name,
1641+
!cond(
1642+
!initialized(!getdagarg<PointerLikeRegClass>(OperandList, name))
1643+
: !setdagarg(acc, name, new_rc),
1644+
!initialized(!getdagarg<unknown_class>(OperandList, name)) : acc,
1645+
!initialized(!getdagarg<DAGOperand>(OperandList, name)) : acc
1646+
)
1647+
);
1648+
}
1649+
1650+
/// Define an override for a pseudoinstruction which uses a pointer
1651+
/// register class, specialized to the target's pointer type.
1652+
class RemapPointerOperands<StandardPseudoInstruction inst,
1653+
RegisterClassLike new_rc> :
1654+
TargetSpecializedStandardPseudoInstruction<inst> {
1655+
let OutOperandList =
1656+
RemapPointerOperandList<inst.OutOperandList, new_rc>.ret;
1657+
let InOperandList =
1658+
RemapPointerOperandList<inst.InOperandList, new_rc>.ret;
1659+
}
1660+
1661+
/// Helper to replace all pseudoinstructions using pointers to a
1662+
/// target register class. Most targets should use this
1663+
multiclass RemapAllTargetPseudoPointerOperands<
1664+
RegisterClassLike default_ptr_rc> {
1665+
foreach inst = PseudosWithPtrOps in {
1666+
def : RemapPointerOperands<inst, default_ptr_rc>;
1667+
}
1668+
}
1669+
15771670
// Generic opcodes used in GlobalISel.
15781671
include "llvm/Target/GenericOpcodes.td"
15791672

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// RUN: llvm-tblgen -gen-instr-info -I %p/../../include %s -DONECASE -o - | FileCheck -check-prefixes=CHECK,ONECASE %s
2+
// RUN: llvm-tblgen -gen-instr-info -I %p/../../include %s -DALLCASES -o - | FileCheck -check-prefixes=CHECK,ALLCASES %s
3+
// RUN: not llvm-tblgen -gen-instr-info -I %p/../../include %s -DERROR -o /dev/null 2>&1 | FileCheck -check-prefix=ERROR %s
4+
5+
// CHECK: namespace llvm::MyTarget {
6+
// CHECK: enum {
7+
// CHECK: LOAD_STACK_GUARD = [[LOAD_STACK_GUARD_OPCODE:[0-9]+]],
8+
// CHECK: PREALLOCATED_ARG = [[PREALLOCATED_ARG_OPCODE:[0-9]+]],
9+
// CHECK: PATCHABLE_EVENT_CALL = [[PATCHABLE_EVENT_CALL_OPCODE:[0-9]+]],
10+
// CHECK: PATCHABLE_TYPED_EVENT_CALL = [[PATCHABLE_TYPED_EVENT_CALL_OPCODE:[0-9]+]],
11+
12+
// Make sure no enum entry is emitted for MY_LOAD_STACK_GUARD
13+
// CHECK: G_UBFX = [[G_UBFX_OPCODE:[0-9]+]],
14+
// CHECK-NEXT: MY_MOV = [[MY_MOV_OPCODE:[0-9]+]],
15+
// CHECK-NEXT: INSTRUCTION_LIST_END = [[INSTR_LIST_END_OPCODE:[0-9]+]]
16+
17+
18+
// CHECK: extern const MyTargetInstrTable MyTargetDescs = {
19+
// CHECK-NEXT: {
20+
// CHECK-NEXT: { [[MY_MOV_OPCODE]], 2, 1, 2, 0, 0, 0, {{[0-9]+}}, MyTargetImpOpBase + 0, 0|(1ULL<<MCID::ExtraSrcRegAllocReq)|(1ULL<<MCID::ExtraDefRegAllocReq), 0x0ULL }, // MY_MOV
21+
// CHECK-NEXT: { [[G_UBFX_OPCODE]], 4, 1, 0, 0, 0, 0, {{[0-9]+}}, MyTargetImpOpBase + 0, 0|(1ULL<<MCID::PreISelOpcode)|(1ULL<<MCID::Pseudo)|(1ULL<<MCID::ExtraSrcRegAllocReq)|(1ULL<<MCID::ExtraDefRegAllocReq), 0x0ULL }, // G_UBFX
22+
23+
// ONECASE: { [[LOAD_STACK_GUARD_OPCODE]], 1, 1, 0, 0, 0, 0, [[LOAD_STACK_GUARD_OP_ENTRY:[0-9]+]], MyTargetImpOpBase + 0, 0|(1ULL<<MCID::Pseudo)|(1ULL<<MCID::MayLoad)|(1ULL<<MCID::Rematerializable)|(1ULL<<MCID::ExtraSrcRegAllocReq)|(1ULL<<MCID::ExtraDefRegAllocReq), 0x0ULL }, // MY_LOAD_STACK_GUARD
24+
25+
// ALLCASES: { [[PATCHABLE_TYPED_EVENT_CALL_OPCODE]], 3, 0, 0, 0, 0, 0, [[PATCHABLE_TYPED_EVENT_CALL_OP_ENTRY:[0-9]+]], MyTargetImpOpBase + 0, 0|(1ULL<<MCID::Pseudo)|(1ULL<<MCID::Call)|(1ULL<<MCID::MayLoad)|(1ULL<<MCID::MayStore)|(1ULL<<MCID::UsesCustomInserter)|(1ULL<<MCID::UnmodeledSideEffects)|(1ULL<<MCID::ExtraSrcRegAllocReq)|(1ULL<<MCID::ExtraDefRegAllocReq), 0x0ULL }, // anonymous_
26+
// ALLCASES: { [[PATCHABLE_EVENT_CALL_OPCODE]], 2, 0, 0, 0, 0, 0, [[PATCHABLE_EVENT_CALL_OP_ENTRY:[0-9]+]], MyTargetImpOpBase + 0, 0|(1ULL<<MCID::Pseudo)|(1ULL<<MCID::Call)|(1ULL<<MCID::MayLoad)|(1ULL<<MCID::MayStore)|(1ULL<<MCID::UsesCustomInserter)|(1ULL<<MCID::UnmodeledSideEffects)|(1ULL<<MCID::ExtraSrcRegAllocReq)|(1ULL<<MCID::ExtraDefRegAllocReq), 0x0ULL }, // anonymous_
27+
// ALLCASES: { [[PREALLOCATED_ARG_OPCODE]], 3, 1, 0, 0, 0, 0, [[PREALLOCATED_ARG_OP_ENTRY:[0-9]+]], MyTargetImpOpBase + 0, 0|(1ULL<<MCID::Pseudo)|(1ULL<<MCID::UsesCustomInserter)|(1ULL<<MCID::UnmodeledSideEffects)|(1ULL<<MCID::ExtraSrcRegAllocReq)|(1ULL<<MCID::ExtraDefRegAllocReq), 0x0ULL }, // anonymous_
28+
// ALLCASES: { [[LOAD_STACK_GUARD_OPCODE]], 1, 1, 0, 0, 0, 0, [[LOAD_STACK_GUARD_OP_ENTRY:[0-9]+]], MyTargetImpOpBase + 0, 0|(1ULL<<MCID::Pseudo)|(1ULL<<MCID::MayLoad)|(1ULL<<MCID::Rematerializable)|(1ULL<<MCID::ExtraSrcRegAllocReq)|(1ULL<<MCID::ExtraDefRegAllocReq), 0x0ULL }, // anonymous_
29+
30+
// CHECK: /* 0 */ { -1, 0, MCOI::OPERAND_UNKNOWN, 0 },
31+
32+
// ONECASE: /* [[LOAD_STACK_GUARD_OP_ENTRY]] */ { MyTarget::XRegsRegClassID, 0, MCOI::OPERAND_REGISTER, 0 },
33+
34+
// ALLCASES: /* [[LOAD_STACK_GUARD_OP_ENTRY]] */ { MyTarget::XRegsRegClassID, 0, MCOI::OPERAND_REGISTER, 0 },
35+
// ALLCASES: /* [[PREALLOCATED_ARG_OP_ENTRY]] */ { MyTarget::XRegsRegClassID, 0, MCOI::OPERAND_REGISTER, 0 }, { -1, 0, MCOI::OPERAND_IMMEDIATE, 0 }, { -1, 0, MCOI::OPERAND_IMMEDIATE, 0 },
36+
// ALLCASES: /* [[PATCHABLE_EVENT_CALL_OP_ENTRY]] */ { MyTarget::XRegsRegClassID, 0, MCOI::OPERAND_REGISTER, 0 }, { -1, 0, MCOI::OPERAND_UNKNOWN, 0 },
37+
// ALLCASES: /* [[PATCHABLE_TYPED_EVENT_CALL_OP_ENTRY]] */ { -1, 0, MCOI::OPERAND_UNKNOWN, 0 }, { MyTarget::XRegsRegClassID, 0, MCOI::OPERAND_REGISTER, 0 }, { -1, 0, MCOI::OPERAND_UNKNOWN, 0 },
38+
39+
40+
// CHECK: const char MyTargetInstrNameData[] = {
41+
// CHECK: /* {{[0-9]+}} */ "LOAD_STACK_GUARD\000"
42+
43+
include "llvm/Target/Target.td"
44+
45+
class MyReg<string n>
46+
: Register<n> {
47+
let Namespace = "MyTarget";
48+
}
49+
50+
class MyClass<int size, list<ValueType> types, dag registers>
51+
: RegisterClass<"MyTarget", types, size, registers> {
52+
let Size = size;
53+
}
54+
55+
def X0 : MyReg<"x0">;
56+
def X1 : MyReg<"x1">;
57+
def XRegs : RegisterClass<"MyTarget", [i64], 64, (add X0, X1)>;
58+
59+
60+
class TestInstruction : Instruction {
61+
let Size = 2;
62+
let Namespace = "MyTarget";
63+
let hasSideEffects = false;
64+
}
65+
66+
#ifdef ONECASE
67+
68+
// Example setting the pointer register class manually
69+
def MY_LOAD_STACK_GUARD :
70+
TargetSpecializedStandardPseudoInstruction<LOAD_STACK_GUARD> {
71+
let Namespace = "MyTarget";
72+
let OutOperandList = (outs XRegs:$dst);
73+
}
74+
75+
#endif
76+
77+
#ifdef ALLCASES
78+
79+
defm my_remaps : RemapAllTargetPseudoPointerOperands<XRegs>;
80+
81+
#endif
82+
83+
84+
#ifdef ERROR
85+
86+
def MY_LOAD_STACK_GUARD_0 : TargetSpecializedStandardPseudoInstruction<LOAD_STACK_GUARD>;
87+
88+
// ERROR: :[[@LINE+1]]:5: error: multiple overrides of 'LOAD_STACK_GUARD' defined
89+
def MY_LOAD_STACK_GUARD_1 : TargetSpecializedStandardPseudoInstruction<LOAD_STACK_GUARD>;
90+
91+
#endif
92+
93+
def MY_MOV : TestInstruction {
94+
let OutOperandList = (outs XRegs:$dst);
95+
let InOperandList = (ins XRegs:$src);
96+
let AsmString = "my_mov $dst, $src";
97+
}
98+
99+
100+
def MyTargetISA : InstrInfo;
101+
def MyTarget : Target { let InstructionSet = MyTargetISA; }

llvm/utils/TableGen/Common/CodeGenTarget.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,15 +284,25 @@ void CodeGenTarget::ComputeInstrsByEnum() const {
284284
assert(EndOfPredefines == getNumFixedInstructions() &&
285285
"Missing generic opcode");
286286

287+
unsigned SkippedInsts = 0;
288+
287289
for (const auto &[_, CGIUp] : InstMap) {
288290
const CodeGenInstruction *CGI = CGIUp.get();
289291
if (CGI->Namespace != "TargetOpcode") {
292+
293+
if (CGI->TheDef->isSubClassOf(
294+
"TargetSpecializedStandardPseudoInstruction")) {
295+
++SkippedInsts;
296+
continue;
297+
}
298+
290299
InstrsByEnum.push_back(CGI);
291300
NumPseudoInstructions += CGI->TheDef->getValueAsBit("isPseudo");
292301
}
293302
}
294303

295-
assert(InstrsByEnum.size() == InstMap.size() && "Missing predefined instr");
304+
assert(InstrsByEnum.size() + SkippedInsts == InstMap.size() &&
305+
"Missing predefined instr");
296306

297307
// All of the instructions are now in random order based on the map iteration.
298308
llvm::sort(

llvm/utils/TableGen/InstrInfoEmitter.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@ class InstrInfoEmitter {
7171
typedef std::vector<OperandInfoTy> OperandInfoListTy;
7272
typedef std::map<OperandInfoTy, unsigned> OperandInfoMapTy;
7373

74+
DenseMap<const CodeGenInstruction *, const CodeGenInstruction *>
75+
TargetSpecializedPseudoInsts;
76+
77+
/// Compute mapping of opcodes which should have their definitions overridden
78+
/// by a target version.
79+
void buildTargetSpecializedPseudoInstsMap();
80+
7481
/// Generate member functions in the target-specific GenInstrInfo class.
7582
///
7683
/// This method is used to custom expand TIIPredicate definitions.
@@ -215,6 +222,10 @@ InstrInfoEmitter::CollectOperandInfo(OperandInfoListTy &OperandInfoList,
215222
const CodeGenTarget &Target = CDP.getTargetInfo();
216223
unsigned Offset = 0;
217224
for (const CodeGenInstruction *Inst : Target.getInstructions()) {
225+
auto OverrideEntry = TargetSpecializedPseudoInsts.find(Inst);
226+
if (OverrideEntry != TargetSpecializedPseudoInsts.end())
227+
Inst = OverrideEntry->second;
228+
218229
OperandInfoTy OperandInfo = GetOperandInfo(*Inst);
219230
if (OperandInfoMap.try_emplace(OperandInfo, Offset).second) {
220231
OperandInfoList.push_back(OperandInfo);
@@ -882,6 +893,25 @@ void InstrInfoEmitter::emitTIIHelperMethods(raw_ostream &OS,
882893
}
883894
}
884895

896+
void InstrInfoEmitter::buildTargetSpecializedPseudoInstsMap() {
897+
ArrayRef<const Record *> SpecializedInsts = Records.getAllDerivedDefinitions(
898+
"TargetSpecializedStandardPseudoInstruction");
899+
const CodeGenTarget &Target = CDP.getTargetInfo();
900+
901+
for (const Record *SpecializedRec : SpecializedInsts) {
902+
const CodeGenInstruction &SpecializedInst =
903+
Target.getInstruction(SpecializedRec);
904+
const Record *BaseInstRec = SpecializedRec->getValueAsDef("Instruction");
905+
906+
const CodeGenInstruction &BaseInst = Target.getInstruction(BaseInstRec);
907+
908+
if (!TargetSpecializedPseudoInsts.insert({&BaseInst, &SpecializedInst})
909+
.second)
910+
PrintFatalError(SpecializedRec, "multiple overrides of '" +
911+
BaseInst.getName() + "' defined");
912+
}
913+
}
914+
885915
//===----------------------------------------------------------------------===//
886916
// Main Output.
887917
//===----------------------------------------------------------------------===//
@@ -904,6 +934,8 @@ void InstrInfoEmitter::run(raw_ostream &OS) {
904934

905935
// Collect all of the operand info records.
906936
Timer.startTimer("Collect operand info");
937+
buildTargetSpecializedPseudoInstsMap();
938+
907939
OperandInfoListTy OperandInfoList;
908940
OperandInfoMapTy OperandInfoMap;
909941
unsigned OperandInfoSize =
@@ -970,6 +1002,11 @@ void InstrInfoEmitter::run(raw_ostream &OS) {
9701002
for (const CodeGenInstruction *Inst : reverse(NumberedInstructions)) {
9711003
// Keep a list of the instruction names.
9721004
InstrNames.add(Inst->getName());
1005+
1006+
auto OverrideEntry = TargetSpecializedPseudoInsts.find(Inst);
1007+
if (OverrideEntry != TargetSpecializedPseudoInsts.end())
1008+
Inst = OverrideEntry->second;
1009+
9731010
// Emit the record into the table.
9741011
emitRecord(*Inst, --Num, InstrInfo, EmittedLists, OperandInfoMap, OS);
9751012
}

0 commit comments

Comments
 (0)