GlobalISel: Add target pre-isel instructions

Allows targets to introduce regbankselectable
pseudo-instructions. Currently the closet feature to this is an
intrinsic. However this requires creating a public intrinsic
declaration. This litters the public intrinsic namespace with
operations we don't necessarily want to expose to IR producers, and
would rather leave as private to the backend.

Use a new instruction bit. A previous attempt tried to keep using enum
value ranges, but it turned into a mess.

llvm-svn: 373937
This commit is contained in:
Matt Arsenault 2019-10-07 18:43:29 +00:00
parent cdbeaf548f
commit 27269054d2
15 changed files with 140 additions and 6 deletions

View File

@ -618,6 +618,12 @@ public:
return hasPropertyInBundle(1ULL << MCFlag, Type);
}
/// Return true if this is an instruction that should go through the usual
/// legalization steps.
bool isPreISelOpcode(QueryType Type = IgnoreBundle) const {
return hasProperty(MCID::PreISelOpcode, Type);
}
/// Return true if this instruction can have a variable number of operands.
/// In this case, the variable operands will be after the normal
/// operands but before the implicit definitions and uses (if any are

View File

@ -129,7 +129,8 @@ namespace MCID {
/// not use these directly. These all correspond to bitfields in the
/// MCInstrDesc::Flags field.
enum Flag {
Variadic = 0,
PreISelOpcode = 0,
Variadic,
HasOptionalDef,
Pseudo,
Return,
@ -242,6 +243,10 @@ public:
/// Return flags of this instruction.
uint64_t getFlags() const { return Flags; }
/// \returns true if this instruction is emitted before instruction selection
/// and should be legalized/regbankselected/selected.
bool isPreISelOpcode() const { return Flags & (1ULL << MCID::PreISelOpcode); }
/// Return true if this instruction can have a variable number of
/// operands. In this case, the variable operands will be after the normal
/// operands but before the implicit definitions and uses (if any are

View File

@ -15,7 +15,9 @@
// Unary ops.
//------------------------------------------------------------------------------
class GenericInstruction : StandardPseudoInstruction;
class GenericInstruction : StandardPseudoInstruction {
let isPreISelOpcode = 1;
}
// Extend the underlying scalar type of an operation, leaving the high bits
// unspecified.

View File

@ -492,6 +492,10 @@ class Instruction : InstructionEncoding {
// Added complexity passed onto matching pattern.
int AddedComplexity = 0;
// Indicates if this is a pre-isel opcode that should be
// legalized/regbankselected/selected.
bit isPreISelOpcode = 0;
// These bits capture information about the high-level semantics of the
// instruction.
bit isReturn = 0; // Is this instruction a return instruction?

View File

@ -687,8 +687,9 @@ bool RegBankSelect::runOnMachineFunction(MachineFunction &MF) {
// iterator before hand.
MachineInstr &MI = *MII++;
// Ignore target-specific instructions: they should use proper regclasses.
if (isTargetSpecificOpcode(MI.getOpcode()))
// Ignore target-specific post-isel instructions: they should use proper
// regclasses.
if (isTargetSpecificOpcode(MI.getOpcode()) && !MI.isPreISelOpcode())
continue;
if (!assignInstr(MI)) {

View File

@ -116,6 +116,7 @@ def : GINodeEquiv<G_ATOMICRMW_UMIN, atomic_load_umin_glue>;
def : GINodeEquiv<G_ATOMICRMW_UMAX, atomic_load_umax_glue>;
def : GINodeEquiv<G_ATOMICRMW_FADD, atomic_load_fadd_glue>;
def : GINodeEquiv<G_AMDGPU_FFBH_U32, AMDGPUffbh_u32>;
class GISelSop2Pat <
SDPatternOperator node,

View File

@ -1650,7 +1650,7 @@ bool AMDGPUInstructionSelector::select(MachineInstr &I) {
if (I.isPHI())
return selectPHI(I);
if (!isPreISelGenericOpcode(I.getOpcode())) {
if (!I.isPreISelOpcode()) {
if (I.isCopy())
return selectCOPY(I);
return true;

View File

@ -2305,6 +2305,7 @@ AMDGPURegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
case AMDGPU::G_FCANONICALIZE:
case AMDGPU::G_INTRINSIC_TRUNC:
case AMDGPU::G_INTRINSIC_ROUND:
case AMDGPU::G_AMDGPU_FFBH_U32:
return getDefaultMappingVOP(MI);
case AMDGPU::G_UMULH:
case AMDGPU::G_SMULH: {

View File

@ -3117,7 +3117,8 @@ static bool shouldReadExec(const MachineInstr &MI) {
return true;
}
if (SIInstrInfo::isGenericOpcode(MI.getOpcode()) ||
if (MI.isPreISelOpcode() ||
SIInstrInfo::isGenericOpcode(MI.getOpcode()) ||
SIInstrInfo::isSALU(MI) ||
SIInstrInfo::isSMRD(MI))
return false;

View File

@ -1982,3 +1982,13 @@ def : FP16Med3Pat<f16, V_MED3_F16>;
defm : Int16Med3Pat<V_MED3_I16, smin, smax, smax_oneuse, smin_oneuse>;
defm : Int16Med3Pat<V_MED3_U16, umin, umax, umax_oneuse, umin_oneuse>;
} // End Predicates = [isGFX9Plus]
class AMDGPUGenericInstruction : GenericInstruction {
let Namespace = "AMDGPU";
}
def G_AMDGPU_FFBH_U32 : AMDGPUGenericInstruction {
let OutOperandList = (outs type0:$dst);
let InOperandList = (ins type1:$src);
let hasSideEffects = 0;
}

View File

@ -0,0 +1,68 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
# RUN: llc -march=amdgcn -run-pass=instruction-select -verify-machineinstrs -global-isel-abort=0 %s -o - | FileCheck %s
---
name: ffbh_u32_s32_s_s
legalized: true
regBankSelected: true
tracksRegLiveness: true
body: |
bb.0:
liveins: $sgpr0
; CHECK-LABEL: name: ffbh_u32_s32_s_s
; CHECK: liveins: $sgpr0
; CHECK: [[COPY:%[0-9]+]]:sreg_32 = COPY $sgpr0
; CHECK: [[S_FLBIT_I32_B32_:%[0-9]+]]:sreg_32 = S_FLBIT_I32_B32 [[COPY]]
; CHECK: S_ENDPGM 0, implicit [[S_FLBIT_I32_B32_]]
%0:sgpr(s32) = COPY $sgpr0
%1:sgpr(s32) = G_AMDGPU_FFBH_U32 %0
S_ENDPGM 0, implicit %1
...
---
name: ffbh_u32_s32_v_v
legalized: true
regBankSelected: true
tracksRegLiveness: true
body: |
bb.0:
liveins: $vgpr0
; CHECK-LABEL: name: ffbh_u32_s32_v_v
; CHECK: liveins: $vgpr0
; CHECK: [[COPY:%[0-9]+]]:vgpr(s32) = COPY $vgpr0
; CHECK: [[AMDGPU_FFBH_U32_:%[0-9]+]]:vgpr(s32) = G_AMDGPU_FFBH_U32 [[COPY]](s32)
; CHECK: S_ENDPGM 0, implicit [[AMDGPU_FFBH_U32_]](s32)
%0:vgpr(s32) = COPY $vgpr0
%1:vgpr(s32) = G_AMDGPU_FFBH_U32 %0
S_ENDPGM 0, implicit %1
...
---
name: ffbh_u32_v_s
legalized: true
regBankSelected: true
tracksRegLiveness: true
body: |
bb.0:
liveins: $sgpr0
; CHECK-LABEL: name: ffbh_u32_v_s
; CHECK: liveins: $sgpr0
; CHECK: [[COPY:%[0-9]+]]:sgpr(s32) = COPY $sgpr0
; CHECK: [[AMDGPU_FFBH_U32_:%[0-9]+]]:vgpr(s32) = G_AMDGPU_FFBH_U32 [[COPY]](s32)
; CHECK: S_ENDPGM 0, implicit [[AMDGPU_FFBH_U32_]](s32)
%0:sgpr(s32) = COPY $sgpr0
%1:vgpr(s32) = G_AMDGPU_FFBH_U32 %0
S_ENDPGM 0, implicit %1
...

View File

@ -0,0 +1,32 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
# RUN: llc -march=amdgcn -mcpu=fiji -run-pass=regbankselect %s -verify-machineinstrs -o - -regbankselect-fast | FileCheck %s
# RUN: llc -march=amdgcn -mcpu=fiji -run-pass=regbankselect %s -verify-machineinstrs -o - -regbankselect-greedy | FileCheck %s
---
name: ffbh_u32_s
legalized: true
body: |
bb.0:
liveins: $sgpr0
; CHECK-LABEL: name: ffbh_u32_s
; CHECK: [[COPY:%[0-9]+]]:sgpr(s32) = COPY $sgpr0
; CHECK: [[AMDGPU_FFBH_U32_:%[0-9]+]]:vgpr(s32) = G_AMDGPU_FFBH_U32 [[COPY]](s32)
%0:_(s32) = COPY $sgpr0
%1:_(s32) = G_AMDGPU_FFBH_U32 %0
...
---
name: ffbh_u32_v
legalized: true
body: |
bb.0:
liveins: $vgpr0_vgpr1
; CHECK-LABEL: name: ffbh_u32_v
; CHECK: [[COPY:%[0-9]+]]:vgpr(s32) = COPY $vgpr0
; CHECK: [[AMDGPU_FFBH_U32_:%[0-9]+]]:vgpr(s32) = G_AMDGPU_FFBH_U32 [[COPY]](s32)
%0:_(s32) = COPY $vgpr0
%1:_(s32) = G_AMDGPU_FFBH_U32 %0
...

View File

@ -363,6 +363,7 @@ CodeGenInstruction::CodeGenInstruction(Record *R)
Namespace = R->getValueAsString("Namespace");
AsmString = R->getValueAsString("AsmString");
isPreISelOpcode = R->getValueAsBit("isPreISelOpcode");
isReturn = R->getValueAsBit("isReturn");
isEHScopeReturn = R->getValueAsBit("isEHScopeReturn");
isBranch = R->getValueAsBit("isBranch");

View File

@ -231,6 +231,7 @@ template <typename T> class ArrayRef;
std::vector<Record*> ImplicitDefs, ImplicitUses;
// Various boolean values we track for the instruction.
bool isPreISelOpcode : 1;
bool isReturn : 1;
bool isEHScopeReturn : 1;
bool isBranch : 1;

View File

@ -662,6 +662,7 @@ void InstrInfoEmitter::emitRecord(const CodeGenInstruction &Inst, unsigned Num,
CodeGenTarget &Target = CDP.getTargetInfo();
// Emit all of the target independent flags...
if (Inst.isPreISelOpcode) OS << "|(1ULL<<MCID::PreISelOpcode)";
if (Inst.isPseudo) OS << "|(1ULL<<MCID::Pseudo)";
if (Inst.isReturn) OS << "|(1ULL<<MCID::Return)";
if (Inst.isEHScopeReturn) OS << "|(1ULL<<MCID::EHScopeReturn)";