Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions llvm/include/llvm/Target/Target.td
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The unfortunate thing is that we have to duplicate all these members here.
But we don't need them all, right? Only those that are overridden by the instructions from the list below. E.g., we don't need isMeta, Size, isRematerializable etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isRematerializable is used. And I'd rather be safe than sorry and copy literally everything


// 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<dag OperandList, RegisterClassLike new_rc> {
// Collect the set of names so we can query and rewrite them.
list<string> 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<PointerLikeRegClass>(OperandList, name))
: !setdagarg(acc, name, new_rc),
!initialized(!getdagarg<unknown_class>(OperandList, name)) : acc,
!initialized(!getdagarg<DAGOperand>(OperandList, name)) : acc
)
);
}

/// Define an override for a pseudoinstruction which uses a pointer
/// register class, specialized to the target's pointer type.
class RemapPointerOperands<StandardPseudoInstruction inst,
RegisterClassLike new_rc> :
TargetSpecializedStandardPseudoInstruction<inst> {
let OutOperandList =
RemapPointerOperandList<inst.OutOperandList, new_rc>.ret;
let InOperandList =
RemapPointerOperandList<inst.InOperandList, new_rc>.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<inst, default_ptr_rc>;
}
}

// Generic opcodes used in GlobalISel.
include "llvm/Target/GenericOpcodes.td"

Expand Down
101 changes: 101 additions & 0 deletions llvm/test/TableGen/target-specialized-pseudos.td
Original file line number Diff line number Diff line change
@@ -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<<MCID::ExtraSrcRegAllocReq)|(1ULL<<MCID::ExtraDefRegAllocReq), 0x0ULL }, // MY_MOV
// 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

// 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

// 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_
// 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_
// 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_
// 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_

// CHECK: /* 0 */ { -1, 0, MCOI::OPERAND_UNKNOWN, 0 },

// ONECASE: /* [[LOAD_STACK_GUARD_OP_ENTRY]] */ { MyTarget::XRegsRegClassID, 0, MCOI::OPERAND_REGISTER, 0 },

// ALLCASES: /* [[LOAD_STACK_GUARD_OP_ENTRY]] */ { MyTarget::XRegsRegClassID, 0, MCOI::OPERAND_REGISTER, 0 },
// ALLCASES: /* [[PREALLOCATED_ARG_OP_ENTRY]] */ { MyTarget::XRegsRegClassID, 0, MCOI::OPERAND_REGISTER, 0 }, { -1, 0, MCOI::OPERAND_IMMEDIATE, 0 }, { -1, 0, MCOI::OPERAND_IMMEDIATE, 0 },
// ALLCASES: /* [[PATCHABLE_EVENT_CALL_OP_ENTRY]] */ { MyTarget::XRegsRegClassID, 0, MCOI::OPERAND_REGISTER, 0 }, { -1, 0, MCOI::OPERAND_UNKNOWN, 0 },
// 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 },


// CHECK: const char MyTargetInstrNameData[] = {
// CHECK: /* {{[0-9]+}} */ "LOAD_STACK_GUARD\000"

include "llvm/Target/Target.td"

class MyReg<string n>
: Register<n> {
let Namespace = "MyTarget";
}

class MyClass<int size, list<ValueType> 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<LOAD_STACK_GUARD> {
let Namespace = "MyTarget";
let OutOperandList = (outs XRegs:$dst);
}

#endif

#ifdef ALLCASES

defm my_remaps : RemapAllTargetPseudoPointerOperands<XRegs>;

#endif


#ifdef ERROR

def MY_LOAD_STACK_GUARD_0 : TargetSpecializedStandardPseudoInstruction<LOAD_STACK_GUARD>;

// ERROR: :[[@LINE+1]]:5: error: multiple overrides of 'LOAD_STACK_GUARD' defined
def MY_LOAD_STACK_GUARD_1 : TargetSpecializedStandardPseudoInstruction<LOAD_STACK_GUARD>;

#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; }
12 changes: 11 additions & 1 deletion llvm/utils/TableGen/Common/CodeGenTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
37 changes: 37 additions & 0 deletions llvm/utils/TableGen/InstrInfoEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ class InstrInfoEmitter {
typedef std::vector<OperandInfoTy> OperandInfoListTy;
typedef std::map<OperandInfoTy, unsigned> OperandInfoMapTy;

DenseMap<const CodeGenInstruction *, const CodeGenInstruction *>
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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -882,6 +893,25 @@ void InstrInfoEmitter::emitTIIHelperMethods(raw_ostream &OS,
}
}

void InstrInfoEmitter::buildTargetSpecializedPseudoInstsMap() {
ArrayRef<const Record *> 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.
//===----------------------------------------------------------------------===//
Expand All @@ -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 =
Expand Down Expand Up @@ -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);
}
Expand Down
Loading