diff --git a/llvm/include/llvm/Target/Target.td b/llvm/include/llvm/Target/Target.td index 13175177edd3e..4a759a99d1d25 100644 --- a/llvm/include/llvm/Target/Target.td +++ b/llvm/include/llvm/Target/Target.td @@ -1574,6 +1574,99 @@ def CONVERGENCECTRL_GLUE : StandardPseudoInstruction { } } +/// Allow a target to replace the instruction definition of a +/// StandardPseudoInstruction. A target should only define one +/// instance of this per instruction. +/// +/// This is intended to allow targets to specify the register class +/// used for pointers. It should not be used to change the fundamental +/// operand structure (e.g., this should not add or remove operands, +/// or change the operand types). +class TargetSpecializedStandardPseudoInstruction< + StandardPseudoInstruction base_inst> : Instruction { + + StandardPseudoInstruction Instruction = base_inst; + let OutOperandList = base_inst.OutOperandList; + let InOperandList = base_inst.InOperandList; + + // TODO: Copy everything + let usesCustomInserter = base_inst.usesCustomInserter; + let hasSideEffects = base_inst.hasSideEffects; + let mayLoad = base_inst.mayLoad; + let mayStore = base_inst.mayStore; + let isTerminator = base_inst.isTerminator; + let isBranch = base_inst.isBranch; + let isIndirectBranch = base_inst.isIndirectBranch; + let isEHScopeReturn = base_inst.isEHScopeReturn; + let isReturn = base_inst.isReturn; + let isCall = base_inst.isCall; + let hasCtrlDep = base_inst.hasCtrlDep; + let isReMaterializable = base_inst.isReMaterializable; + let isMeta = base_inst.isMeta; + let Size = base_inst.Size; + let isAsCheapAsAMove = base_inst.isAsCheapAsAMove; + let isPseudo = true; + let hasNoSchedulingInfo = true; + let isNotDuplicable = base_inst.isNotDuplicable; + let isConvergent = base_inst.isConvergent; + let hasExtraSrcRegAllocReq = base_inst.hasExtraSrcRegAllocReq; + let hasExtraDefRegAllocReq = base_inst.hasExtraDefRegAllocReq; +} + +// All pseudo instructions which need a pointer register class, which +// should be specialized by a target. +defvar PseudosWithPtrOps = [ + LOAD_STACK_GUARD, + PREALLOCATED_ARG, + PATCHABLE_EVENT_CALL, + PATCHABLE_TYPED_EVENT_CALL +]; + + +/// Replace PointerLikeRegClass operands in OperandList with new_rc. +class RemapPointerOperandList { + // Collect the set of names so we can query and rewrite them. + list op_names = !foreach(i, !range(!size(OperandList)), + !getdagname(OperandList, i)); + + // Beautiful language. This would be a lot easier if !getdagarg + // didn't require a specific type. We can't just collect a list of + // the operand values and reconstruct the dag, since there isn't a + // common base class for all the field kinds used in + // pseudoinstruction definitions; therefore everything must be + // maintained as a dag, so use a foldl. Additionally, ? doesn't + // evaluate as false so we get even more noise. + dag ret = + !foldl(OperandList, op_names, acc, name, + !cond( + !initialized(!getdagarg(OperandList, name)) + : !setdagarg(acc, name, new_rc), + !initialized(!getdagarg(OperandList, name)) : acc, + !initialized(!getdagarg(OperandList, name)) : acc + ) + ); +} + +/// Define an override for a pseudoinstruction which uses a pointer +/// register class, specialized to the target's pointer type. +class RemapPointerOperands : + TargetSpecializedStandardPseudoInstruction { + let OutOperandList = + RemapPointerOperandList.ret; + let InOperandList = + RemapPointerOperandList.ret; +} + +/// Helper to replace all pseudoinstructions using pointers to a +/// target register class. Most targets should use this +multiclass RemapAllTargetPseudoPointerOperands< + RegisterClassLike default_ptr_rc> { + foreach inst = PseudosWithPtrOps in { + def : RemapPointerOperands; + } +} + // Generic opcodes used in GlobalISel. include "llvm/Target/GenericOpcodes.td" diff --git a/llvm/test/TableGen/target-specialized-pseudos.td b/llvm/test/TableGen/target-specialized-pseudos.td new file mode 100644 index 0000000000000..99c63f3ec29d9 --- /dev/null +++ b/llvm/test/TableGen/target-specialized-pseudos.td @@ -0,0 +1,101 @@ +// RUN: llvm-tblgen -gen-instr-info -I %p/../../include %s -DONECASE -o - | FileCheck -check-prefixes=CHECK,ONECASE %s +// RUN: llvm-tblgen -gen-instr-info -I %p/../../include %s -DALLCASES -o - | FileCheck -check-prefixes=CHECK,ALLCASES %s +// RUN: not llvm-tblgen -gen-instr-info -I %p/../../include %s -DERROR -o /dev/null 2>&1 | FileCheck -check-prefix=ERROR %s + +// CHECK: namespace llvm::MyTarget { +// CHECK: enum { +// CHECK: LOAD_STACK_GUARD = [[LOAD_STACK_GUARD_OPCODE:[0-9]+]], +// CHECK: PREALLOCATED_ARG = [[PREALLOCATED_ARG_OPCODE:[0-9]+]], +// CHECK: PATCHABLE_EVENT_CALL = [[PATCHABLE_EVENT_CALL_OPCODE:[0-9]+]], +// CHECK: PATCHABLE_TYPED_EVENT_CALL = [[PATCHABLE_TYPED_EVENT_CALL_OPCODE:[0-9]+]], + +// Make sure no enum entry is emitted for MY_LOAD_STACK_GUARD +// CHECK: G_UBFX = [[G_UBFX_OPCODE:[0-9]+]], +// CHECK-NEXT: MY_MOV = [[MY_MOV_OPCODE:[0-9]+]], +// CHECK-NEXT: INSTRUCTION_LIST_END = [[INSTR_LIST_END_OPCODE:[0-9]+]] + + +// CHECK: extern const MyTargetInstrTable MyTargetDescs = { +// CHECK-NEXT: { +// CHECK-NEXT: { [[MY_MOV_OPCODE]], 2, 1, 2, 0, 0, 0, {{[0-9]+}}, MyTargetImpOpBase + 0, 0|(1ULL< + : Register { + let Namespace = "MyTarget"; +} + +class MyClass types, dag registers> + : RegisterClass<"MyTarget", types, size, registers> { + let Size = size; +} + +def X0 : MyReg<"x0">; +def X1 : MyReg<"x1">; +def XRegs : RegisterClass<"MyTarget", [i64], 64, (add X0, X1)>; + + +class TestInstruction : Instruction { + let Size = 2; + let Namespace = "MyTarget"; + let hasSideEffects = false; +} + +#ifdef ONECASE + +// Example setting the pointer register class manually +def MY_LOAD_STACK_GUARD : + TargetSpecializedStandardPseudoInstruction { + let Namespace = "MyTarget"; + let OutOperandList = (outs XRegs:$dst); +} + +#endif + +#ifdef ALLCASES + +defm my_remaps : RemapAllTargetPseudoPointerOperands; + +#endif + + +#ifdef ERROR + +def MY_LOAD_STACK_GUARD_0 : TargetSpecializedStandardPseudoInstruction; + +// ERROR: :[[@LINE+1]]:5: error: multiple overrides of 'LOAD_STACK_GUARD' defined +def MY_LOAD_STACK_GUARD_1 : TargetSpecializedStandardPseudoInstruction; + +#endif + +def MY_MOV : TestInstruction { + let OutOperandList = (outs XRegs:$dst); + let InOperandList = (ins XRegs:$src); + let AsmString = "my_mov $dst, $src"; +} + + +def MyTargetISA : InstrInfo; +def MyTarget : Target { let InstructionSet = MyTargetISA; } diff --git a/llvm/utils/TableGen/Common/CodeGenTarget.cpp b/llvm/utils/TableGen/Common/CodeGenTarget.cpp index 3db0d07eec88f..3493c21f0ab68 100644 --- a/llvm/utils/TableGen/Common/CodeGenTarget.cpp +++ b/llvm/utils/TableGen/Common/CodeGenTarget.cpp @@ -284,15 +284,25 @@ void CodeGenTarget::ComputeInstrsByEnum() const { assert(EndOfPredefines == getNumFixedInstructions() && "Missing generic opcode"); + unsigned SkippedInsts = 0; + for (const auto &[_, CGIUp] : InstMap) { const CodeGenInstruction *CGI = CGIUp.get(); if (CGI->Namespace != "TargetOpcode") { + + if (CGI->TheDef->isSubClassOf( + "TargetSpecializedStandardPseudoInstruction")) { + ++SkippedInsts; + continue; + } + InstrsByEnum.push_back(CGI); NumPseudoInstructions += CGI->TheDef->getValueAsBit("isPseudo"); } } - assert(InstrsByEnum.size() == InstMap.size() && "Missing predefined instr"); + assert(InstrsByEnum.size() + SkippedInsts == InstMap.size() && + "Missing predefined instr"); // All of the instructions are now in random order based on the map iteration. llvm::sort( diff --git a/llvm/utils/TableGen/InstrInfoEmitter.cpp b/llvm/utils/TableGen/InstrInfoEmitter.cpp index 176e4b250b82a..ed32f537d4b5d 100644 --- a/llvm/utils/TableGen/InstrInfoEmitter.cpp +++ b/llvm/utils/TableGen/InstrInfoEmitter.cpp @@ -71,6 +71,13 @@ class InstrInfoEmitter { typedef std::vector OperandInfoListTy; typedef std::map OperandInfoMapTy; + DenseMap + TargetSpecializedPseudoInsts; + + /// Compute mapping of opcodes which should have their definitions overridden + /// by a target version. + void buildTargetSpecializedPseudoInstsMap(); + /// Generate member functions in the target-specific GenInstrInfo class. /// /// This method is used to custom expand TIIPredicate definitions. @@ -215,6 +222,10 @@ InstrInfoEmitter::CollectOperandInfo(OperandInfoListTy &OperandInfoList, const CodeGenTarget &Target = CDP.getTargetInfo(); unsigned Offset = 0; for (const CodeGenInstruction *Inst : Target.getInstructions()) { + auto OverrideEntry = TargetSpecializedPseudoInsts.find(Inst); + if (OverrideEntry != TargetSpecializedPseudoInsts.end()) + Inst = OverrideEntry->second; + OperandInfoTy OperandInfo = GetOperandInfo(*Inst); if (OperandInfoMap.try_emplace(OperandInfo, Offset).second) { OperandInfoList.push_back(OperandInfo); @@ -882,6 +893,25 @@ void InstrInfoEmitter::emitTIIHelperMethods(raw_ostream &OS, } } +void InstrInfoEmitter::buildTargetSpecializedPseudoInstsMap() { + ArrayRef SpecializedInsts = Records.getAllDerivedDefinitions( + "TargetSpecializedStandardPseudoInstruction"); + const CodeGenTarget &Target = CDP.getTargetInfo(); + + for (const Record *SpecializedRec : SpecializedInsts) { + const CodeGenInstruction &SpecializedInst = + Target.getInstruction(SpecializedRec); + const Record *BaseInstRec = SpecializedRec->getValueAsDef("Instruction"); + + const CodeGenInstruction &BaseInst = Target.getInstruction(BaseInstRec); + + if (!TargetSpecializedPseudoInsts.insert({&BaseInst, &SpecializedInst}) + .second) + PrintFatalError(SpecializedRec, "multiple overrides of '" + + BaseInst.getName() + "' defined"); + } +} + //===----------------------------------------------------------------------===// // Main Output. //===----------------------------------------------------------------------===// @@ -904,6 +934,8 @@ void InstrInfoEmitter::run(raw_ostream &OS) { // Collect all of the operand info records. Timer.startTimer("Collect operand info"); + buildTargetSpecializedPseudoInstsMap(); + OperandInfoListTy OperandInfoList; OperandInfoMapTy OperandInfoMap; unsigned OperandInfoSize = @@ -970,6 +1002,11 @@ void InstrInfoEmitter::run(raw_ostream &OS) { for (const CodeGenInstruction *Inst : reverse(NumberedInstructions)) { // Keep a list of the instruction names. InstrNames.add(Inst->getName()); + + auto OverrideEntry = TargetSpecializedPseudoInsts.find(Inst); + if (OverrideEntry != TargetSpecializedPseudoInsts.end()) + Inst = OverrideEntry->second; + // Emit the record into the table. emitRecord(*Inst, --Num, InstrInfo, EmittedLists, OperandInfoMap, OS); }