forked from OSchip/llvm-project
TableGen support for parameterized register class information
This replaces TableGen's type inference to operate on parameterized types instead of MVTs, and as a consequence, some interfaces have changed: - Uses of MVTs are replaced by ValueTypeByHwMode. - EEVT::TypeSet is replaced by TypeSetByHwMode. This affects the way that types and type sets are printed, and the tests relying on that have been updated. There are certain users of the inferred types outside of TableGen itself, namely FastISel and GlobalISel. For those users, the way that the types are accessed have changed. For typical scenarios, these replacements can be used: - TreePatternNode::getType(ResNo) -> getSimpleType(ResNo) - TreePatternNode::hasTypeSet(ResNo) -> hasConcreteType(ResNo) - TypeSet::isConcrete -> TypeSetByHwMode::isValueTypeByHwMode(false) For more information, please refer to the review page. Differential Revision: https://reviews.llvm.org/D31951 llvm-svn: 313271
This commit is contained in:
parent
a0e55b6403
commit
779d98e1c0
|
@ -21,6 +21,56 @@ include "llvm/IR/Intrinsics.td"
|
|||
|
||||
class RegisterClass; // Forward def
|
||||
|
||||
class HwMode<string FS> {
|
||||
// A string representing subtarget features that turn on this HW mode.
|
||||
// For example, "+feat1,-feat2" will indicate that the mode is active
|
||||
// when "feat1" is enabled and "feat2" is disabled at the same time.
|
||||
// Any other features are not checked.
|
||||
// When multiple modes are used, they should be mutually exclusive,
|
||||
// otherwise the results are unpredictable.
|
||||
string Features = FS;
|
||||
}
|
||||
|
||||
// A special mode recognized by tablegen. This mode is considered active
|
||||
// when no other mode is active. For targets that do not use specific hw
|
||||
// modes, this is the only mode.
|
||||
def DefaultMode : HwMode<"">;
|
||||
|
||||
// A class used to associate objects with HW modes. It is only intended to
|
||||
// be used as a base class, where the derived class should contain a member
|
||||
// "Objects", which is a list of the same length as the list of modes.
|
||||
// The n-th element on the Objects list will be associated with the n-th
|
||||
// element on the Modes list.
|
||||
class HwModeSelect<list<HwMode> Ms> {
|
||||
list<HwMode> Modes = Ms;
|
||||
}
|
||||
|
||||
// A common class that implements a counterpart of ValueType, which is
|
||||
// dependent on a HW mode. This class inherits from ValueType itself,
|
||||
// which makes it possible to use objects of this class where ValueType
|
||||
// objects could be used. This is specifically applicable to selection
|
||||
// patterns.
|
||||
class ValueTypeByHwMode<list<HwMode> Ms, list<ValueType> Ts>
|
||||
: HwModeSelect<Ms>, ValueType<0, 0> {
|
||||
// The length of this list must be the same as the length of Ms.
|
||||
list<ValueType> Objects = Ts;
|
||||
}
|
||||
|
||||
// A class representing the register size, spill size and spill alignment
|
||||
// in bits of a register.
|
||||
class RegInfo<int RS, int SS, int SA> {
|
||||
int RegSize = RS; // Register size in bits.
|
||||
int SpillSize = SS; // Spill slot size in bits.
|
||||
int SpillAlignment = SA; // Spill slot alignment in bits.
|
||||
}
|
||||
|
||||
// The register size/alignment information, parameterized by a HW mode.
|
||||
class RegInfoByHwMode<list<HwMode> Ms = [], list<RegInfo> Ts = []>
|
||||
: HwModeSelect<Ms> {
|
||||
// The length of this list must be the same as the length of Ms.
|
||||
list<RegInfo> Objects = Ts;
|
||||
}
|
||||
|
||||
// SubRegIndex - Use instances of SubRegIndex to identify subregisters.
|
||||
class SubRegIndex<int size, int offset = 0> {
|
||||
string Namespace = "";
|
||||
|
@ -156,6 +206,9 @@ class RegisterClass<string namespace, list<ValueType> regTypes, int alignment,
|
|||
: DAGOperand {
|
||||
string Namespace = namespace;
|
||||
|
||||
// The register size/alignment information, parameterized by a HW mode.
|
||||
RegInfoByHwMode RegInfos;
|
||||
|
||||
// RegType - Specify the list ValueType of the registers in this register
|
||||
// class. Note that all registers in a register class must have the same
|
||||
// ValueTypes. This is a list because some targets permit storing different
|
||||
|
|
|
@ -49,8 +49,6 @@ public:
|
|||
|
||||
// Instance variables filled by tablegen, do not use!
|
||||
const MCRegisterClass *MC;
|
||||
const uint16_t SpillSize, SpillAlignment;
|
||||
const MVT::SimpleValueType *VTs;
|
||||
const uint32_t *SubClassMask;
|
||||
const uint16_t *SuperRegIndices;
|
||||
const LaneBitmask LaneMask;
|
||||
|
@ -222,7 +220,10 @@ class TargetRegisterInfo : public MCRegisterInfo {
|
|||
public:
|
||||
using regclass_iterator = const TargetRegisterClass * const *;
|
||||
using vt_iterator = const MVT::SimpleValueType *;
|
||||
|
||||
struct RegClassInfo {
|
||||
unsigned RegSize, SpillSize, SpillAlignment;
|
||||
vt_iterator VTList;
|
||||
};
|
||||
private:
|
||||
const TargetRegisterInfoDesc *InfoDesc; // Extra desc array for codegen
|
||||
const char *const *SubRegIndexNames; // Names of subreg indexes.
|
||||
|
@ -231,6 +232,8 @@ private:
|
|||
|
||||
regclass_iterator RegClassBegin, RegClassEnd; // List of regclasses
|
||||
LaneBitmask CoveringLanes;
|
||||
const RegClassInfo *const RCInfos;
|
||||
unsigned HwMode;
|
||||
|
||||
protected:
|
||||
TargetRegisterInfo(const TargetRegisterInfoDesc *ID,
|
||||
|
@ -238,7 +241,9 @@ protected:
|
|||
regclass_iterator RegClassEnd,
|
||||
const char *const *SRINames,
|
||||
const LaneBitmask *SRILaneMasks,
|
||||
LaneBitmask CoveringLanes);
|
||||
LaneBitmask CoveringLanes,
|
||||
const RegClassInfo *const RSI,
|
||||
unsigned Mode = 0);
|
||||
virtual ~TargetRegisterInfo();
|
||||
|
||||
public:
|
||||
|
@ -306,25 +311,25 @@ public:
|
|||
|
||||
/// Return the size in bits of a register from class RC.
|
||||
unsigned getRegSizeInBits(const TargetRegisterClass &RC) const {
|
||||
return RC.SpillSize * 8;
|
||||
return getRegClassInfo(RC).RegSize;
|
||||
}
|
||||
|
||||
/// Return the size in bytes of the stack slot allocated to hold a spilled
|
||||
/// copy of a register from class RC.
|
||||
unsigned getSpillSize(const TargetRegisterClass &RC) const {
|
||||
return RC.SpillSize;
|
||||
return getRegClassInfo(RC).SpillSize / 8;
|
||||
}
|
||||
|
||||
/// Return the minimum required alignment in bytes for a spill slot for
|
||||
/// a register of this class.
|
||||
unsigned getSpillAlignment(const TargetRegisterClass &RC) const {
|
||||
return RC.SpillAlignment;
|
||||
return getRegClassInfo(RC).SpillAlignment / 8;
|
||||
}
|
||||
|
||||
/// Return true if the given TargetRegisterClass has the ValueType T.
|
||||
bool isTypeLegalForClass(const TargetRegisterClass &RC, MVT T) const {
|
||||
for (int i = 0; RC.VTs[i] != MVT::Other; ++i)
|
||||
if (MVT(RC.VTs[i]) == T)
|
||||
for (auto I = legalclasstypes_begin(RC); *I != MVT::Other; ++I)
|
||||
if (MVT(*I) == T)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
@ -332,11 +337,11 @@ public:
|
|||
/// Loop over all of the value types that can be represented by values
|
||||
/// in the given register class.
|
||||
vt_iterator legalclasstypes_begin(const TargetRegisterClass &RC) const {
|
||||
return RC.VTs;
|
||||
return getRegClassInfo(RC).VTList;
|
||||
}
|
||||
|
||||
vt_iterator legalclasstypes_end(const TargetRegisterClass &RC) const {
|
||||
vt_iterator I = RC.VTs;
|
||||
vt_iterator I = legalclasstypes_begin(RC);
|
||||
while (*I != MVT::Other)
|
||||
++I;
|
||||
return I;
|
||||
|
@ -654,7 +659,12 @@ public:
|
|||
//===--------------------------------------------------------------------===//
|
||||
// Register Class Information
|
||||
//
|
||||
protected:
|
||||
const RegClassInfo &getRegClassInfo(const TargetRegisterClass &RC) const {
|
||||
return RCInfos[getNumRegClasses() * HwMode + RC.getID()];
|
||||
}
|
||||
|
||||
public:
|
||||
/// Register class iterators
|
||||
regclass_iterator regclass_begin() const { return RegClassBegin; }
|
||||
regclass_iterator regclass_end() const { return RegClassEnd; }
|
||||
|
|
|
@ -41,11 +41,14 @@ TargetRegisterInfo::TargetRegisterInfo(const TargetRegisterInfoDesc *ID,
|
|||
regclass_iterator RCB, regclass_iterator RCE,
|
||||
const char *const *SRINames,
|
||||
const LaneBitmask *SRILaneMasks,
|
||||
LaneBitmask SRICoveringLanes)
|
||||
LaneBitmask SRICoveringLanes,
|
||||
const RegClassInfo *const RCIs,
|
||||
unsigned Mode)
|
||||
: InfoDesc(ID), SubRegIndexNames(SRINames),
|
||||
SubRegIndexLaneMasks(SRILaneMasks),
|
||||
RegClassBegin(RCB), RegClassEnd(RCE),
|
||||
CoveringLanes(SRICoveringLanes) {
|
||||
CoveringLanes(SRICoveringLanes),
|
||||
RCInfos(RCIs), HwMode(Mode) {
|
||||
}
|
||||
|
||||
TargetRegisterInfo::~TargetRegisterInfo() = default;
|
||||
|
|
|
@ -147,7 +147,7 @@ def HasC : Predicate<"Subtarget->hasC()"> { let RecomputePerFunction = 1; }
|
|||
// CHECK-NEXT: // MIs[0] src3
|
||||
// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/3, /*Type*/GILLT_s32,
|
||||
// CHECK-NEXT: GIM_CheckComplexPattern, /*MI*/0, /*Op*/3, /*Renderer*/1, GICP_gi_complex,
|
||||
// CHECK-NEXT: // (select:i32 GPR32:i32:$src1, complex:i32:$src2, complex:i32:$src3) => (INSN2:i32 GPR32:i32:$src1, complex:i32:$src3, complex:i32:$src2)
|
||||
// CHECK-NEXT: // (select:{ *:[i32] } GPR32:{ *:[i32] }:$src1, complex:{ *:[i32] }:$src2, complex:{ *:[i32] }:$src3) => (INSN2:{ *:[i32] } GPR32:{ *:[i32] }:$src1, complex:{ *:[i32] }:$src3, complex:{ *:[i32] }:$src2)
|
||||
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::INSN2,
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src1
|
||||
|
@ -196,7 +196,7 @@ def : Pat<(select GPR32:$src1, complex:$src2, (select GPR32:$src3, complex:$src4
|
|||
// CHECK-NEXT: GIM_CheckType, /*MI*/1, /*Op*/3, /*Type*/GILLT_s32,
|
||||
// CHECK-NEXT: GIM_CheckComplexPattern, /*MI*/1, /*Op*/3, /*Renderer*/2, GICP_gi_complex,
|
||||
// CHECK-NEXT: GIM_CheckIsSafeToFold, /*InsnID*/1,
|
||||
// CHECK-NEXT: // (select:i32 GPR32:i32:$src1, complex:i32:$src2, (select:i32 GPR32:i32:$src3, complex:i32:$src4, complex:i32:$src5)) => (INSN3:i32 GPR32:i32:$src1, complex:i32:$src2, GPR32:i32:$src3, complex:i32:$src4, complex:i32:$src5)
|
||||
// CHECK-NEXT: // (select:{ *:[i32] } GPR32:{ *:[i32] }:$src1, complex:{ *:[i32] }:$src2, (select:{ *:[i32] } GPR32:{ *:[i32] }:$src3, complex:{ *:[i32] }:$src4, complex:{ *:[i32] }:$src5)) => (INSN3:{ *:[i32] } GPR32:{ *:[i32] }:$src1, complex:{ *:[i32] }:$src2, GPR32:{ *:[i32] }:$src3, complex:{ *:[i32] }:$src4, complex:{ *:[i32] }:$src5)
|
||||
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::INSN3,
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src1
|
||||
|
@ -230,7 +230,7 @@ def : Pat<(select GPR32:$src1, complex:$src2, complex:$src3),
|
|||
// CHECK-NEXT: // MIs[0] src2
|
||||
// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_s32,
|
||||
// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/2, /*RC*/MyTarget::GPR32RegClassID,
|
||||
// CHECK-NEXT: // (add:i32 GPR32:i32:$src1, GPR32:i32:$src2) => (ADD:i32 GPR32:i32:$src1, GPR32:i32:$src2)
|
||||
// CHECK-NEXT: // (add:{ *:[i32] } GPR32:{ *:[i32] }:$src1, GPR32:{ *:[i32] }:$src2) => (ADD:{ *:[i32] } GPR32:{ *:[i32] }:$src1, GPR32:{ *:[i32] }:$src2)
|
||||
// CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/MyTarget::ADD,
|
||||
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
|
@ -253,7 +253,7 @@ def ADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2),
|
|||
// CHECK-NEXT: // MIs[0] src1
|
||||
// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_s32,
|
||||
// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/2, /*RC*/MyTarget::GPR32RegClassID,
|
||||
// CHECK-NEXT: // (intrinsic_wo_chain:i32 [[ID:[0-9]+]]:iPTR, GPR32:i32:$src1) => (MOV:i32 GPR32:i32:$src1)
|
||||
// CHECK-NEXT: // (intrinsic_wo_chain:{ *:[i32] } [[ID:[0-9]+]]:{ *:[iPTR] }, GPR32:{ *:[i32] }:$src1) => (MOV:{ *:[i32] } GPR32:{ *:[i32] }:$src1)
|
||||
|
||||
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::MOV,
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
|
||||
|
@ -292,7 +292,7 @@ def MOV : I<(outs GPR32:$dst), (ins GPR32:$src1),
|
|||
// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_s32,
|
||||
// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/2, /*RC*/MyTarget::GPR32RegClassID,
|
||||
// CHECK-NEXT: GIM_CheckIsSafeToFold, /*InsnID*/1,
|
||||
// CHECK-NEXT: // (mul:i32 (add:i32 GPR32:i32:$src1, GPR32:i32:$src2), GPR32:i32:$src3) => (MULADD:i32 GPR32:i32:$src1, GPR32:i32:$src2, GPR32:i32:$src3)
|
||||
// CHECK-NEXT: // (mul:{ *:[i32] } (add:{ *:[i32] } GPR32:{ *:[i32] }:$src1, GPR32:{ *:[i32] }:$src2), GPR32:{ *:[i32] }:$src3) => (MULADD:{ *:[i32] } GPR32:{ *:[i32] }:$src1, GPR32:{ *:[i32] }:$src2, GPR32:{ *:[i32] }:$src3)
|
||||
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::MULADD,
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/1, /*OpIdx*/1, // src1
|
||||
|
@ -328,7 +328,7 @@ def MOV : I<(outs GPR32:$dst), (ins GPR32:$src1),
|
|||
// CHECK-NEXT: GIM_CheckType, /*MI*/1, /*Op*/2, /*Type*/GILLT_s32,
|
||||
// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/1, /*Op*/2, /*RC*/MyTarget::GPR32RegClassID,
|
||||
// CHECK-NEXT: GIM_CheckIsSafeToFold, /*InsnID*/1,
|
||||
// CHECK-NEXT: // (mul:i32 GPR32:i32:$src3, (add:i32 GPR32:i32:$src1, GPR32:i32:$src2)) => (MULADD:i32 GPR32:i32:$src1, GPR32:i32:$src2, GPR32:i32:$src3)
|
||||
// CHECK-NEXT: // (mul:{ *:[i32] } GPR32:{ *:[i32] }:$src3, (add:{ *:[i32] } GPR32:{ *:[i32] }:$src1, GPR32:{ *:[i32] }:$src2)) => (MULADD:{ *:[i32] } GPR32:{ *:[i32] }:$src1, GPR32:{ *:[i32] }:$src2, GPR32:{ *:[i32] }:$src3)
|
||||
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::MULADD,
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/1, /*OpIdx*/1, // src1
|
||||
|
@ -359,7 +359,7 @@ def MULADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2, GPR32:$src3),
|
|||
// CHECK-NEXT: // MIs[0] src2
|
||||
// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_s32,
|
||||
// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/2, /*RC*/MyTarget::GPR32RegClassID,
|
||||
// CHECK-NEXT: // (mul:i32 GPR32:i32:$src1, GPR32:i32:$src2) => (MUL:i32 GPR32:i32:$src2, GPR32:i32:$src1)
|
||||
// CHECK-NEXT: // (mul:{ *:[i32] } GPR32:{ *:[i32] }:$src1, GPR32:{ *:[i32] }:$src2) => (MUL:{ *:[i32] } GPR32:{ *:[i32] }:$src2, GPR32:{ *:[i32] }:$src1)
|
||||
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::MUL,
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/2, // src2
|
||||
|
@ -410,7 +410,7 @@ def MUL : I<(outs GPR32:$dst), (ins GPR32:$src2, GPR32:$src1),
|
|||
// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/2, /*Op*/2, /*RC*/MyTarget::GPR32RegClassID,
|
||||
// CHECK-NEXT: GIM_CheckIsSafeToFold, /*InsnID*/1,
|
||||
// CHECK-NEXT: GIM_CheckIsSafeToFold, /*InsnID*/2,
|
||||
// CHECK-NEXT: // (sub:i32 (sub:i32 GPR32:i32:$src1, GPR32:i32:$src2), (sub:i32 GPR32:i32:$src3, GPR32:i32:$src4)) => (INSNBOB:i32 GPR32:i32:$src1, GPR32:i32:$src2, GPR32:i32:$src3, GPR32:i32:$src4)
|
||||
// CHECK-NEXT: // (sub:{ *:[i32] } (sub:{ *:[i32] } GPR32:{ *:[i32] }:$src1, GPR32:{ *:[i32] }:$src2), (sub:{ *:[i32] } GPR32:{ *:[i32] }:$src3, GPR32:{ *:[i32] }:$src4)) => (INSNBOB:{ *:[i32] } GPR32:{ *:[i32] }:$src1, GPR32:{ *:[i32] }:$src2, GPR32:{ *:[i32] }:$src3, GPR32:{ *:[i32] }:$src4)
|
||||
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::INSNBOB,
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/1, /*OpIdx*/1, // src1
|
||||
|
@ -442,7 +442,7 @@ def INSNBOB : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2, GPR32:$src3, G
|
|||
// CHECK-NEXT: // MIs[0] src2
|
||||
// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_s32,
|
||||
// CHECK-NEXT: GIM_CheckComplexPattern, /*MI*/0, /*Op*/2, /*Renderer*/0, GICP_gi_complex,
|
||||
// CHECK-NEXT: // (sub:i32 GPR32:i32:$src1, complex:i32:$src2) => (INSN1:i32 GPR32:i32:$src1, complex:i32:$src2)
|
||||
// CHECK-NEXT: // (sub:{ *:[i32] } GPR32:{ *:[i32] }:$src1, complex:{ *:[i32] }:$src2) => (INSN1:{ *:[i32] } GPR32:{ *:[i32] }:$src1, complex:{ *:[i32] }:$src2)
|
||||
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::INSN1,
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src1
|
||||
|
@ -470,7 +470,7 @@ def : Pat<(sub GPR32:$src1, complex:$src2), (INSN1 GPR32:$src1, complex:$src2)>;
|
|||
// CHECK-NEXT: // MIs[0] Operand 2
|
||||
// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_s32,
|
||||
// CHECK-NEXT: GIM_CheckConstantInt, /*MI*/0, /*Op*/2, -2
|
||||
// CHECK-NEXT: // (xor:i32 GPR32:i32:$src1, -2:i32) => (XORI:i32 GPR32:i32:$src1)
|
||||
// CHECK-NEXT: // (xor:{ *:[i32] } GPR32:{ *:[i32] }:$src1, -2:{ *:[i32] }) => (XORI:{ *:[i32] } GPR32:{ *:[i32] }:$src1)
|
||||
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::XORI,
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
|
||||
// CHECK-NEXT: GIR_AddImm, /*InsnID*/0, /*Imm*/-1,
|
||||
|
@ -499,7 +499,7 @@ def XORI : I<(outs GPR32:$dst), (ins m1:$src2, GPR32:$src1),
|
|||
// CHECK-NEXT: // MIs[0] Operand 2
|
||||
// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_s32,
|
||||
// CHECK-NEXT: GIM_CheckConstantInt, /*MI*/0, /*Op*/2, -3
|
||||
// CHECK-NEXT: // (xor:i32 GPR32:i32:$src1, -3:i32) => (XOR:i32 GPR32:i32:$src1)
|
||||
// CHECK-NEXT: // (xor:{ *:[i32] } GPR32:{ *:[i32] }:$src1, -3:{ *:[i32] }) => (XOR:{ *:[i32] } GPR32:{ *:[i32] }:$src1)
|
||||
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::XOR,
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
|
||||
// CHECK-NEXT: GIR_AddRegister, /*InsnID*/0, MyTarget::R0,
|
||||
|
@ -528,7 +528,7 @@ def XOR : I<(outs GPR32:$dst), (ins Z:$src2, GPR32:$src1),
|
|||
// CHECK-NEXT: // MIs[0] Operand 2
|
||||
// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_s32,
|
||||
// CHECK-NEXT: GIM_CheckConstantInt, /*MI*/0, /*Op*/2, -4
|
||||
// CHECK-NEXT: // (xor:i32 GPR32:i32:$src1, -4:i32) => (XORlike:i32 GPR32:i32:$src1)
|
||||
// CHECK-NEXT: // (xor:{ *:[i32] } GPR32:{ *:[i32] }:$src1, -4:{ *:[i32] }) => (XORlike:{ *:[i32] } GPR32:{ *:[i32] }:$src1)
|
||||
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::XORlike,
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
|
||||
// CHECK-NEXT: GIR_AddImm, /*InsnID*/0, /*Imm*/-1,
|
||||
|
@ -558,7 +558,7 @@ def XORlike : I<(outs GPR32:$dst), (ins m1Z:$src2, GPR32:$src1),
|
|||
// CHECK-NEXT: // MIs[0] Operand 2
|
||||
// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_s32,
|
||||
// CHECK-NEXT: GIM_CheckConstantInt, /*MI*/0, /*Op*/2, -5,
|
||||
// CHECK-NEXT: // (xor:i32 GPR32:i32:$src1, -5:i32) => (XORManyDefaults:i32 GPR32:i32:$src1)
|
||||
// CHECK-NEXT: // (xor:{ *:[i32] } GPR32:{ *:[i32] }:$src1, -5:{ *:[i32] }) => (XORManyDefaults:{ *:[i32] } GPR32:{ *:[i32] }:$src1)
|
||||
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::XORManyDefaults,
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
|
||||
// CHECK-NEXT: GIR_AddImm, /*InsnID*/0, /*Imm*/-1,
|
||||
|
@ -591,7 +591,7 @@ def XORManyDefaults : I<(outs GPR32:$dst), (ins m1Z:$src3, Z:$src2, GPR32:$src1)
|
|||
// CHECK-NEXT: // MIs[0] Operand 2
|
||||
// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_s32,
|
||||
// CHECK-NEXT: GIM_CheckConstantInt, /*MI*/0, /*Op*/2, -1,
|
||||
// CHECK-NEXT: // (xor:i32 GPR32:i32:$Wm, -1:i32) => (ORN:i32 R0:i32, GPR32:i32:$Wm)
|
||||
// CHECK-NEXT: // (xor:{ *:[i32] } GPR32:{ *:[i32] }:$Wm, -1:{ *:[i32] }) => (ORN:{ *:[i32] } R0:{ *:[i32] }, GPR32:{ *:[i32] }:$Wm)
|
||||
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::ORN,
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
|
||||
// CHECK-NEXT: GIR_AddRegister, /*InsnID*/0, MyTarget::R0,
|
||||
|
@ -616,7 +616,7 @@ def : Pat<(not GPR32:$Wm), (ORN R0, GPR32:$Wm)>;
|
|||
// CHECK-NEXT: // MIs[0] src1
|
||||
// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/1, /*Type*/GILLT_s32,
|
||||
// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/1, /*RC*/MyTarget::FPR32RegClassID,
|
||||
// CHECK-NEXT: // (bitconvert:i32 FPR32:f32:$src1) => (COPY_TO_REGCLASS:i32 FPR32:f32:$src1, GPR32:i32)
|
||||
// CHECK-NEXT: // (bitconvert:{ *:[i32] } FPR32:{ *:[f32] }:$src1) => (COPY_TO_REGCLASS:{ *:[i32] } FPR32:{ *:[f32] }:$src1, GPR32:{ *:[i32] })
|
||||
// CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/TargetOpcode::COPY,
|
||||
// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/0, /*Op*/0, /*RC GPR32*/1,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
|
@ -635,7 +635,7 @@ def : Pat<(i32 (bitconvert FPR32:$src1)),
|
|||
// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID,
|
||||
// CHECK-NEXT: // MIs[0] Operand 1
|
||||
// CHECK-NEXT: GIM_CheckLiteralInt, /*MI*/0, /*Op*/1, 1,
|
||||
// CHECK-NEXT: // 1:i32 => (MOV1:i32)
|
||||
// CHECK-NEXT: // 1:{ *:[i32] } => (MOV1:{ *:[i32] })
|
||||
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::MOV1,
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
|
||||
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
|
||||
|
@ -656,7 +656,7 @@ def MOV1 : I<(outs GPR32:$dst), (ins), [(set GPR32:$dst, 1)]>;
|
|||
// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID,
|
||||
// CHECK-NEXT: // MIs[0] Operand 1
|
||||
// CHECK-NEXT: // No operand predicates
|
||||
// CHECK-NEXT: // (imm:i32)<<P:Predicate_simm8>>:$imm => (MOVimm8:i32 (imm:i32):$imm)
|
||||
// CHECK-NEXT: // (imm:{ *:[i32] })<<P:Predicate_simm8>>:$imm => (MOVimm8:{ *:[i32] } (imm:{ *:[i32] }):$imm)
|
||||
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::MOVimm8,
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
|
||||
// CHECK-NEXT: GIR_CopyConstantAsSImm, /*NewInsnID*/0, /*OldInsnID*/0, // imm
|
||||
|
@ -678,7 +678,7 @@ def MOVimm8 : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, simm8:$i
|
|||
// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID,
|
||||
// CHECK-NEXT: // MIs[0] Operand 1
|
||||
// CHECK-NEXT: // No operand predicates
|
||||
// CHECK-NEXT: // (imm:i32):$imm => (MOVimm:i32 (imm:i32):$imm)
|
||||
// CHECK-NEXT: // (imm:{ *:[i32] }):$imm => (MOVimm:{ *:[i32] } (imm:{ *:[i32] }):$imm)
|
||||
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::MOVimm,
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
|
||||
// CHECK-NEXT: GIR_CopyConstantAsSImm, /*NewInsnID*/0, /*OldInsnID*/0, // imm
|
||||
|
@ -696,7 +696,7 @@ def MOVimm : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, imm:$imm)
|
|||
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_BR,
|
||||
// CHECK-NEXT: // MIs[0] target
|
||||
// CHECK-NEXT: GIM_CheckIsMBB, /*MI*/0, /*Op*/0,
|
||||
// CHECK-NEXT: // (br (bb:Other):$target) => (BR (bb:Other):$target)
|
||||
// CHECK-NEXT: // (br (bb:{ *:[Other] }):$target) => (BR (bb:{ *:[Other] }):$target)
|
||||
// CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/MyTarget::BR,
|
||||
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
// RUN: not llvm-tblgen -gen-dag-isel -I %p/../../include %s 2>&1 | FileCheck %s
|
||||
|
||||
// The HwModeSelect class is intended to serve as a base class for other
|
||||
// classes that are then used to select a value based on the HW mode.
|
||||
// It contains a list of HW modes, and a derived class should provide a
|
||||
// list of corresponding values.
|
||||
// These two lists must have the same size. Make sure that a violation of
|
||||
// this requirement is diagnosed.
|
||||
|
||||
include "llvm/Target/Target.td"
|
||||
|
||||
def TestTargetInstrInfo : InstrInfo;
|
||||
|
||||
def TestTarget : Target {
|
||||
let InstructionSet = TestTargetInstrInfo;
|
||||
}
|
||||
|
||||
def TestReg : Register<"testreg">;
|
||||
def TestClass : RegisterClass<"TestTarget", [i32], 32, (add TestReg)>;
|
||||
|
||||
def TestMode1 : HwMode<"+feat1">;
|
||||
def TestMode2 : HwMode<"+feat2">;
|
||||
|
||||
def BadDef : ValueTypeByHwMode<[TestMode1, TestMode2, DefaultMode],
|
||||
[i8, i16, i32, i64]>;
|
||||
|
||||
// CHECK: error: in record BadDef derived from HwModeSelect: the lists Modes and Objects should have the same size
|
|
@ -8,6 +8,7 @@ add_tablegen(llvm-tblgen LLVM
|
|||
CallingConvEmitter.cpp
|
||||
CodeEmitterGen.cpp
|
||||
CodeGenDAGPatterns.cpp
|
||||
CodeGenHwModes.cpp
|
||||
CodeGenInstruction.cpp
|
||||
CodeGenMapTable.cpp
|
||||
CodeGenRegisters.cpp
|
||||
|
@ -23,6 +24,7 @@ add_tablegen(llvm-tblgen LLVM
|
|||
FastISelEmitter.cpp
|
||||
FixedLenDecoderEmitter.cpp
|
||||
GlobalISelEmitter.cpp
|
||||
InfoByHwMode.cpp
|
||||
InstrInfoEmitter.cpp
|
||||
IntrinsicEmitter.cpp
|
||||
OptParserEmitter.cpp
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -15,6 +15,7 @@
|
|||
#ifndef LLVM_UTILS_TABLEGEN_CODEGENDAGPATTERNS_H
|
||||
#define LLVM_UTILS_TABLEGEN_CODEGENDAGPATTERNS_H
|
||||
|
||||
#include "CodeGenHwModes.h"
|
||||
#include "CodeGenIntrinsics.h"
|
||||
#include "CodeGenTarget.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
|
@ -36,134 +37,148 @@ namespace llvm {
|
|||
class CodeGenDAGPatterns;
|
||||
class ComplexPattern;
|
||||
|
||||
/// EEVT::DAGISelGenValueType - These are some extended forms of
|
||||
/// MVT::SimpleValueType that we use as lattice values during type inference.
|
||||
/// The existing MVT iAny, fAny and vAny types suffice to represent
|
||||
/// arbitrary integer, floating-point, and vector types, so only an unknown
|
||||
/// value is needed.
|
||||
namespace EEVT {
|
||||
/// TypeSet - This is either empty if it's completely unknown, or holds a set
|
||||
/// of types. It is used during type inference because register classes can
|
||||
/// have multiple possible types and we don't know which one they get until
|
||||
/// type inference is complete.
|
||||
///
|
||||
/// TypeSet can have three states:
|
||||
/// Vector is empty: The type is completely unknown, it can be any valid
|
||||
/// target type.
|
||||
/// Vector has multiple constrained types: (e.g. v4i32 + v4f32) it is one
|
||||
/// of those types only.
|
||||
/// Vector has one concrete type: The type is completely known.
|
||||
///
|
||||
class TypeSet {
|
||||
SmallVector<MVT::SimpleValueType, 4> TypeVec;
|
||||
public:
|
||||
TypeSet() {}
|
||||
TypeSet(MVT::SimpleValueType VT, TreePattern &TP);
|
||||
TypeSet(ArrayRef<MVT::SimpleValueType> VTList);
|
||||
struct TypeSetByHwMode : public InfoByHwMode<std::set<MVT>> {
|
||||
typedef std::set<MVT> SetType;
|
||||
|
||||
bool isCompletelyUnknown() const { return TypeVec.empty(); }
|
||||
TypeSetByHwMode() = default;
|
||||
TypeSetByHwMode(const TypeSetByHwMode &VTS) = default;
|
||||
TypeSetByHwMode(MVT::SimpleValueType VT)
|
||||
: TypeSetByHwMode(ValueTypeByHwMode(VT)) {}
|
||||
TypeSetByHwMode(ValueTypeByHwMode VT)
|
||||
: TypeSetByHwMode(ArrayRef<ValueTypeByHwMode>(&VT, 1)) {}
|
||||
TypeSetByHwMode(ArrayRef<ValueTypeByHwMode> VTList);
|
||||
|
||||
bool isConcrete() const {
|
||||
if (TypeVec.size() != 1) return false;
|
||||
unsigned char T = TypeVec[0]; (void)T;
|
||||
assert(T < MVT::LAST_VALUETYPE || T == MVT::iPTR || T == MVT::iPTRAny);
|
||||
return true;
|
||||
}
|
||||
SetType &getOrCreate(unsigned Mode) {
|
||||
if (hasMode(Mode))
|
||||
return get(Mode);
|
||||
return Map.insert({Mode,SetType()}).first->second;
|
||||
}
|
||||
|
||||
MVT::SimpleValueType getConcrete() const {
|
||||
assert(isConcrete() && "Type isn't concrete yet");
|
||||
return (MVT::SimpleValueType)TypeVec[0];
|
||||
}
|
||||
bool isValueTypeByHwMode(bool AllowEmpty) const;
|
||||
ValueTypeByHwMode getValueTypeByHwMode() const;
|
||||
bool isMachineValueType() const {
|
||||
return isDefaultOnly() && Map.begin()->second.size() == 1;
|
||||
}
|
||||
|
||||
bool isDynamicallyResolved() const {
|
||||
return getConcrete() == MVT::iPTR || getConcrete() == MVT::iPTRAny;
|
||||
}
|
||||
MVT getMachineValueType() const {
|
||||
assert(isMachineValueType());
|
||||
return *Map.begin()->second.begin();
|
||||
}
|
||||
|
||||
const SmallVectorImpl<MVT::SimpleValueType> &getTypeList() const {
|
||||
assert(!TypeVec.empty() && "Not a type list!");
|
||||
return TypeVec;
|
||||
}
|
||||
bool isPossible() const;
|
||||
bool isDefaultOnly() const {
|
||||
return Map.size() == 1 &&
|
||||
Map.begin()->first == DefaultMode;
|
||||
}
|
||||
|
||||
bool isVoid() const {
|
||||
return TypeVec.size() == 1 && TypeVec[0] == MVT::isVoid;
|
||||
}
|
||||
bool insert(const ValueTypeByHwMode &VVT);
|
||||
bool constrain(const TypeSetByHwMode &VTS);
|
||||
template <typename Predicate> bool constrain(Predicate P);
|
||||
template <typename Predicate> bool assign_if(const TypeSetByHwMode &VTS,
|
||||
Predicate P);
|
||||
|
||||
/// hasIntegerTypes - Return true if this TypeSet contains any integer value
|
||||
/// types.
|
||||
bool hasIntegerTypes() const;
|
||||
std::string getAsString() const;
|
||||
static std::string getAsString(const SetType &S);
|
||||
|
||||
/// hasFloatingPointTypes - Return true if this TypeSet contains an fAny or
|
||||
/// a floating point value type.
|
||||
bool hasFloatingPointTypes() const;
|
||||
bool operator==(const TypeSetByHwMode &VTS) const;
|
||||
bool operator!=(const TypeSetByHwMode &VTS) const { return !(*this == VTS); }
|
||||
|
||||
/// hasScalarTypes - Return true if this TypeSet contains a scalar value
|
||||
/// type.
|
||||
bool hasScalarTypes() const;
|
||||
void dump() const;
|
||||
void validate() const;
|
||||
|
||||
/// hasVectorTypes - Return true if this TypeSet contains a vector value
|
||||
/// type.
|
||||
bool hasVectorTypes() const;
|
||||
private:
|
||||
/// Intersect two sets. Return true if anything has changed.
|
||||
bool intersect(SetType &Out, const SetType &In);
|
||||
};
|
||||
|
||||
/// getName() - Return this TypeSet as a string.
|
||||
std::string getName() const;
|
||||
struct TypeInfer {
|
||||
TypeInfer(TreePattern &T) : TP(T), ForceMode(0) {}
|
||||
|
||||
/// MergeInTypeInfo - This merges in type information from the specified
|
||||
/// argument. If 'this' changes, it returns true. If the two types are
|
||||
/// contradictory (e.g. merge f32 into i32) then this flags an error.
|
||||
bool MergeInTypeInfo(const EEVT::TypeSet &InVT, TreePattern &TP);
|
||||
bool isConcrete(const TypeSetByHwMode &VTS, bool AllowEmpty) const {
|
||||
return VTS.isValueTypeByHwMode(AllowEmpty);
|
||||
}
|
||||
ValueTypeByHwMode getConcrete(const TypeSetByHwMode &VTS,
|
||||
bool AllowEmpty) const {
|
||||
assert(VTS.isValueTypeByHwMode(AllowEmpty));
|
||||
return VTS.getValueTypeByHwMode();
|
||||
}
|
||||
|
||||
bool MergeInTypeInfo(MVT::SimpleValueType InVT, TreePattern &TP) {
|
||||
return MergeInTypeInfo(EEVT::TypeSet(InVT, TP), TP);
|
||||
}
|
||||
/// The protocol in the following functions (Merge*, force*, Enforce*,
|
||||
/// expand*) is to return "true" if a change has been made, "false"
|
||||
/// otherwise.
|
||||
|
||||
/// Force this type list to only contain integer types.
|
||||
bool EnforceInteger(TreePattern &TP);
|
||||
bool MergeInTypeInfo(TypeSetByHwMode &Out, const TypeSetByHwMode &In);
|
||||
bool MergeInTypeInfo(TypeSetByHwMode &Out, MVT::SimpleValueType InVT) {
|
||||
return MergeInTypeInfo(Out, TypeSetByHwMode(InVT));
|
||||
}
|
||||
bool MergeInTypeInfo(TypeSetByHwMode &Out, ValueTypeByHwMode InVT) {
|
||||
return MergeInTypeInfo(Out, TypeSetByHwMode(InVT));
|
||||
}
|
||||
|
||||
/// Force this type list to only contain floating point types.
|
||||
bool EnforceFloatingPoint(TreePattern &TP);
|
||||
/// Reduce the set \p Out to have at most one element for each mode.
|
||||
bool forceArbitrary(TypeSetByHwMode &Out);
|
||||
|
||||
/// EnforceScalar - Remove all vector types from this type list.
|
||||
bool EnforceScalar(TreePattern &TP);
|
||||
/// The following four functions ensure that upon return the set \p Out
|
||||
/// will only contain types of the specified kind: integer, floating-point,
|
||||
/// scalar, or vector.
|
||||
/// If \p Out is empty, all legal types of the specified kind will be added
|
||||
/// to it. Otherwise, all types that are not of the specified kind will be
|
||||
/// removed from \p Out.
|
||||
bool EnforceInteger(TypeSetByHwMode &Out);
|
||||
bool EnforceFloatingPoint(TypeSetByHwMode &Out);
|
||||
bool EnforceScalar(TypeSetByHwMode &Out);
|
||||
bool EnforceVector(TypeSetByHwMode &Out);
|
||||
|
||||
/// EnforceVector - Remove all non-vector types from this type list.
|
||||
bool EnforceVector(TreePattern &TP);
|
||||
/// If \p Out is empty, fill it with all legal types. Otherwise, leave it
|
||||
/// unchanged.
|
||||
bool EnforceAny(TypeSetByHwMode &Out);
|
||||
/// Make sure that for each type in \p Small, there exists a larger type
|
||||
/// in \p Big.
|
||||
bool EnforceSmallerThan(TypeSetByHwMode &Small, TypeSetByHwMode &Big);
|
||||
/// 1. Ensure that for each type T in \p Vec, T is a vector type, and that
|
||||
/// for each type U in \p Elem, U is a scalar type.
|
||||
/// 2. Ensure that for each (scalar) type U in \p Elem, there exists a
|
||||
/// (vector) type T in \p Vec, such that U is the element type of T.
|
||||
bool EnforceVectorEltTypeIs(TypeSetByHwMode &Vec, TypeSetByHwMode &Elem);
|
||||
bool EnforceVectorEltTypeIs(TypeSetByHwMode &Vec,
|
||||
const ValueTypeByHwMode &VVT);
|
||||
/// Ensure that for each type T in \p Sub, T is a vector type, and there
|
||||
/// exists a type U in \p Vec such that U is a vector type with the same
|
||||
/// element type as T and at least as many elements as T.
|
||||
bool EnforceVectorSubVectorTypeIs(TypeSetByHwMode &Vec,
|
||||
TypeSetByHwMode &Sub);
|
||||
/// 1. Ensure that \p V has a scalar type iff \p W has a scalar type.
|
||||
/// 2. Ensure that for each vector type T in \p V, there exists a vector
|
||||
/// type U in \p W, such that T and U have the same number of elements.
|
||||
/// 3. Ensure that for each vector type U in \p W, there exists a vector
|
||||
/// type T in \p V, such that T and U have the same number of elements
|
||||
/// (reverse of 2).
|
||||
bool EnforceSameNumElts(TypeSetByHwMode &V, TypeSetByHwMode &W);
|
||||
/// 1. Ensure that for each type T in \p A, there exists a type U in \p B,
|
||||
/// such that T and U have equal size in bits.
|
||||
/// 2. Ensure that for each type U in \p B, there exists a type T in \p A
|
||||
/// such that T and U have equal size in bits (reverse of 1).
|
||||
bool EnforceSameSize(TypeSetByHwMode &A, TypeSetByHwMode &B);
|
||||
|
||||
/// EnforceSmallerThan - 'this' must be a smaller VT than Other. Update
|
||||
/// this an other based on this information.
|
||||
bool EnforceSmallerThan(EEVT::TypeSet &Other, TreePattern &TP);
|
||||
/// For each overloaded type (i.e. of form *Any), replace it with the
|
||||
/// corresponding subset of legal, specific types.
|
||||
void expandOverloads(TypeSetByHwMode &VTS);
|
||||
void expandOverloads(TypeSetByHwMode::SetType &Out,
|
||||
const TypeSetByHwMode::SetType &Legal);
|
||||
|
||||
/// EnforceVectorEltTypeIs - 'this' is now constrained to be a vector type
|
||||
/// whose element is VT.
|
||||
bool EnforceVectorEltTypeIs(EEVT::TypeSet &VT, TreePattern &TP);
|
||||
|
||||
/// EnforceVectorEltTypeIs - 'this' is now constrained to be a vector type
|
||||
/// whose element is VT.
|
||||
bool EnforceVectorEltTypeIs(MVT::SimpleValueType VT, TreePattern &TP);
|
||||
|
||||
/// EnforceVectorSubVectorTypeIs - 'this' is now constrained to
|
||||
/// be a vector type VT.
|
||||
bool EnforceVectorSubVectorTypeIs(EEVT::TypeSet &VT, TreePattern &TP);
|
||||
|
||||
/// EnforceSameNumElts - If VTOperand is a scalar, then 'this' is a scalar.
|
||||
/// If VTOperand is a vector, then 'this' must have the same number of
|
||||
/// elements.
|
||||
bool EnforceSameNumElts(EEVT::TypeSet &VT, TreePattern &TP);
|
||||
|
||||
/// EnforceSameSize - 'this' is now constrained to be the same size as VT.
|
||||
bool EnforceSameSize(EEVT::TypeSet &VT, TreePattern &TP);
|
||||
|
||||
bool operator!=(const TypeSet &RHS) const { return TypeVec != RHS.TypeVec; }
|
||||
bool operator==(const TypeSet &RHS) const { return TypeVec == RHS.TypeVec; }
|
||||
|
||||
private:
|
||||
/// FillWithPossibleTypes - Set to all legal types and return true, only
|
||||
/// valid on completely unknown type sets. If Pred is non-null, only MVTs
|
||||
/// that pass the predicate are added.
|
||||
bool FillWithPossibleTypes(TreePattern &TP,
|
||||
bool (*Pred)(MVT::SimpleValueType) = nullptr,
|
||||
const char *PredicateName = nullptr);
|
||||
struct ValidateOnExit {
|
||||
ValidateOnExit(TypeSetByHwMode &T) : VTS(T) {}
|
||||
~ValidateOnExit() { VTS.validate(); }
|
||||
TypeSetByHwMode &VTS;
|
||||
};
|
||||
}
|
||||
|
||||
TreePattern &TP;
|
||||
unsigned ForceMode; // Mode to use when set.
|
||||
bool CodeGen = false; // Set during generation of matcher code.
|
||||
|
||||
private:
|
||||
TypeSetByHwMode getLegalTypes();
|
||||
};
|
||||
|
||||
/// Set type used to track multiply used variables in patterns
|
||||
typedef std::set<std::string> MultipleUseVarSet;
|
||||
|
@ -171,7 +186,7 @@ typedef std::set<std::string> MultipleUseVarSet;
|
|||
/// SDTypeConstraint - This is a discriminated union of constraints,
|
||||
/// corresponding to the SDTypeConstraint tablegen class in Target.td.
|
||||
struct SDTypeConstraint {
|
||||
SDTypeConstraint(Record *R);
|
||||
SDTypeConstraint(Record *R, const CodeGenHwModes &CGH);
|
||||
|
||||
unsigned OperandNo; // The operand # this constraint applies to.
|
||||
enum {
|
||||
|
@ -181,9 +196,6 @@ struct SDTypeConstraint {
|
|||
} ConstraintType;
|
||||
|
||||
union { // The discriminated union.
|
||||
struct {
|
||||
MVT::SimpleValueType VT;
|
||||
} SDTCisVT_Info;
|
||||
struct {
|
||||
unsigned OtherOperandNum;
|
||||
} SDTCisSameAs_Info;
|
||||
|
@ -199,9 +211,6 @@ struct SDTypeConstraint {
|
|||
struct {
|
||||
unsigned OtherOperandNum;
|
||||
} SDTCisSubVecOfVec_Info;
|
||||
struct {
|
||||
MVT::SimpleValueType VT;
|
||||
} SDTCVecEltisVT_Info;
|
||||
struct {
|
||||
unsigned OtherOperandNum;
|
||||
} SDTCisSameNumEltsAs_Info;
|
||||
|
@ -210,6 +219,10 @@ struct SDTypeConstraint {
|
|||
} SDTCisSameSizeAs_Info;
|
||||
} x;
|
||||
|
||||
// The VT for SDTCisVT and SDTCVecEltisVT.
|
||||
// Must not be in the union because it has a non-trivial destructor.
|
||||
ValueTypeByHwMode VVT;
|
||||
|
||||
/// ApplyTypeConstraint - Given a node in a pattern, apply this type
|
||||
/// constraint to the nodes operands. This returns true if it makes a
|
||||
/// change, false otherwise. If a type contradiction is found, an error
|
||||
|
@ -230,7 +243,8 @@ class SDNodeInfo {
|
|||
int NumOperands;
|
||||
std::vector<SDTypeConstraint> TypeConstraints;
|
||||
public:
|
||||
SDNodeInfo(Record *R); // Parse the specified record.
|
||||
// Parse the specified record.
|
||||
SDNodeInfo(Record *R, const CodeGenHwModes &CGH);
|
||||
|
||||
unsigned getNumResults() const { return NumResults; }
|
||||
|
||||
|
@ -258,12 +272,7 @@ public:
|
|||
/// constraints for this node to the operands of the node. This returns
|
||||
/// true if it makes a change, false otherwise. If a type contradiction is
|
||||
/// found, an error is flagged.
|
||||
bool ApplyTypeConstraints(TreePatternNode *N, TreePattern &TP) const {
|
||||
bool MadeChange = false;
|
||||
for (unsigned i = 0, e = TypeConstraints.size(); i != e; ++i)
|
||||
MadeChange |= TypeConstraints[i].ApplyTypeConstraint(N, *this, TP);
|
||||
return MadeChange;
|
||||
}
|
||||
bool ApplyTypeConstraints(TreePatternNode *N, TreePattern &TP) const;
|
||||
};
|
||||
|
||||
/// TreePredicateFn - This is an abstraction that represents the predicates on
|
||||
|
@ -324,7 +333,7 @@ class TreePatternNode {
|
|||
/// The type of each node result. Before and during type inference, each
|
||||
/// result may be a set of possible types. After (successful) type inference,
|
||||
/// each is a single concrete type.
|
||||
SmallVector<EEVT::TypeSet, 1> Types;
|
||||
std::vector<TypeSetByHwMode> Types;
|
||||
|
||||
/// Operator - The Record for the operator if this is an interior node (not
|
||||
/// a leaf).
|
||||
|
@ -367,22 +376,24 @@ public:
|
|||
|
||||
// Type accessors.
|
||||
unsigned getNumTypes() const { return Types.size(); }
|
||||
MVT::SimpleValueType getType(unsigned ResNo) const {
|
||||
return Types[ResNo].getConcrete();
|
||||
ValueTypeByHwMode getType(unsigned ResNo) const {
|
||||
return Types[ResNo].getValueTypeByHwMode();
|
||||
}
|
||||
const std::vector<TypeSetByHwMode> &getExtTypes() const { return Types; }
|
||||
const TypeSetByHwMode &getExtType(unsigned ResNo) const {
|
||||
return Types[ResNo];
|
||||
}
|
||||
TypeSetByHwMode &getExtType(unsigned ResNo) { return Types[ResNo]; }
|
||||
void setType(unsigned ResNo, const TypeSetByHwMode &T) { Types[ResNo] = T; }
|
||||
MVT::SimpleValueType getSimpleType(unsigned ResNo) const {
|
||||
return Types[ResNo].getMachineValueType().SimpleTy;
|
||||
}
|
||||
const SmallVectorImpl<EEVT::TypeSet> &getExtTypes() const { return Types; }
|
||||
const EEVT::TypeSet &getExtType(unsigned ResNo) const { return Types[ResNo]; }
|
||||
EEVT::TypeSet &getExtType(unsigned ResNo) { return Types[ResNo]; }
|
||||
void setType(unsigned ResNo, const EEVT::TypeSet &T) { Types[ResNo] = T; }
|
||||
|
||||
bool hasTypeSet(unsigned ResNo) const {
|
||||
return Types[ResNo].isConcrete();
|
||||
bool hasConcreteType(unsigned ResNo) const {
|
||||
return Types[ResNo].isValueTypeByHwMode(false);
|
||||
}
|
||||
bool isTypeCompletelyUnknown(unsigned ResNo) const {
|
||||
return Types[ResNo].isCompletelyUnknown();
|
||||
}
|
||||
bool isTypeDynamicallyResolved(unsigned ResNo) const {
|
||||
return Types[ResNo].isDynamicallyResolved();
|
||||
bool isTypeCompletelyUnknown(unsigned ResNo, TreePattern &TP) const {
|
||||
return Types[ResNo].empty();
|
||||
}
|
||||
|
||||
Init *getLeafValue() const { assert(isLeaf()); return Val; }
|
||||
|
@ -401,6 +412,10 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
bool hasProperTypeByHwMode() const;
|
||||
bool hasPossibleType() const;
|
||||
bool setDefaultMode(unsigned Mode);
|
||||
|
||||
bool hasAnyPredicate() const { return !PredicateFns.empty(); }
|
||||
|
||||
const std::vector<TreePredicateFn> &getPredicateFns() const {
|
||||
|
@ -484,15 +499,12 @@ public: // Higher level manipulation routines.
|
|||
/// information. If N already contains a conflicting type, then flag an
|
||||
/// error. This returns true if any information was updated.
|
||||
///
|
||||
bool UpdateNodeType(unsigned ResNo, const EEVT::TypeSet &InTy,
|
||||
TreePattern &TP) {
|
||||
return Types[ResNo].MergeInTypeInfo(InTy, TP);
|
||||
}
|
||||
|
||||
bool UpdateNodeType(unsigned ResNo, const TypeSetByHwMode &InTy,
|
||||
TreePattern &TP);
|
||||
bool UpdateNodeType(unsigned ResNo, MVT::SimpleValueType InTy,
|
||||
TreePattern &TP) {
|
||||
return Types[ResNo].MergeInTypeInfo(EEVT::TypeSet(InTy, TP), TP);
|
||||
}
|
||||
TreePattern &TP);
|
||||
bool UpdateNodeType(unsigned ResNo, ValueTypeByHwMode InTy,
|
||||
TreePattern &TP);
|
||||
|
||||
// Update node type with types inferred from an instruction operand or result
|
||||
// def from the ins/outs lists.
|
||||
|
@ -501,14 +513,7 @@ public: // Higher level manipulation routines.
|
|||
|
||||
/// ContainsUnresolvedType - Return true if this tree contains any
|
||||
/// unresolved types.
|
||||
bool ContainsUnresolvedType() const {
|
||||
for (unsigned i = 0, e = Types.size(); i != e; ++i)
|
||||
if (!Types[i].isConcrete()) return true;
|
||||
|
||||
for (unsigned i = 0, e = getNumChildren(); i != e; ++i)
|
||||
if (getChild(i)->ContainsUnresolvedType()) return true;
|
||||
return false;
|
||||
}
|
||||
bool ContainsUnresolvedType(TreePattern &TP) const;
|
||||
|
||||
/// canPatternMatch - If it is impossible for this pattern to match on this
|
||||
/// target, fill in Reason and return false. Otherwise, return true.
|
||||
|
@ -560,6 +565,9 @@ class TreePattern {
|
|||
/// number for each operand encountered in a ComplexPattern to aid in that
|
||||
/// check.
|
||||
StringMap<std::pair<Record *, unsigned>> ComplexPatternOperands;
|
||||
|
||||
TypeInfer Infer;
|
||||
|
||||
public:
|
||||
|
||||
/// TreePattern constructor - Parse the specified DagInits into the
|
||||
|
@ -625,6 +633,8 @@ public:
|
|||
HasError = false;
|
||||
}
|
||||
|
||||
TypeInfer &getInfer() { return Infer; }
|
||||
|
||||
void print(raw_ostream &OS) const;
|
||||
void dump() const;
|
||||
|
||||
|
@ -634,6 +644,32 @@ private:
|
|||
void ComputeNamedNodes(TreePatternNode *N);
|
||||
};
|
||||
|
||||
|
||||
inline bool TreePatternNode::UpdateNodeType(unsigned ResNo,
|
||||
const TypeSetByHwMode &InTy,
|
||||
TreePattern &TP) {
|
||||
TypeSetByHwMode VTS(InTy);
|
||||
TP.getInfer().expandOverloads(VTS);
|
||||
return TP.getInfer().MergeInTypeInfo(Types[ResNo], VTS);
|
||||
}
|
||||
|
||||
inline bool TreePatternNode::UpdateNodeType(unsigned ResNo,
|
||||
MVT::SimpleValueType InTy,
|
||||
TreePattern &TP) {
|
||||
TypeSetByHwMode VTS(InTy);
|
||||
TP.getInfer().expandOverloads(VTS);
|
||||
return TP.getInfer().MergeInTypeInfo(Types[ResNo], VTS);
|
||||
}
|
||||
|
||||
inline bool TreePatternNode::UpdateNodeType(unsigned ResNo,
|
||||
ValueTypeByHwMode InTy,
|
||||
TreePattern &TP) {
|
||||
TypeSetByHwMode VTS(InTy);
|
||||
TP.getInfer().expandOverloads(VTS);
|
||||
return TP.getInfer().MergeInTypeInfo(Types[ResNo], VTS);
|
||||
}
|
||||
|
||||
|
||||
/// DAGDefaultOperand - One of these is created for each OperandWithDefaultOps
|
||||
/// that has a set ExecuteAlways / DefaultOps field.
|
||||
struct DAGDefaultOperand {
|
||||
|
@ -680,31 +716,89 @@ public:
|
|||
TreePatternNode *getResultPattern() const { return ResultPattern; }
|
||||
};
|
||||
|
||||
/// This class represents a condition that has to be satisfied for a pattern
|
||||
/// to be tried. It is a generalization of a class "Pattern" from Target.td:
|
||||
/// in addition to the Target.td's predicates, this class can also represent
|
||||
/// conditions associated with HW modes. Both types will eventually become
|
||||
/// strings containing C++ code to be executed, the difference is in how
|
||||
/// these strings are generated.
|
||||
class Predicate {
|
||||
public:
|
||||
Predicate(Record *R, bool C = true) : Def(R), IfCond(C), IsHwMode(false) {
|
||||
assert(R->isSubClassOf("Predicate") &&
|
||||
"Predicate objects should only be created for records derived"
|
||||
"from Predicate class");
|
||||
}
|
||||
Predicate(StringRef FS, bool C = true) : Def(nullptr), Features(FS.str()),
|
||||
IfCond(C), IsHwMode(true) {}
|
||||
|
||||
/// Return a string which contains the C++ condition code that will serve
|
||||
/// as a predicate during instruction selection.
|
||||
std::string getCondString() const {
|
||||
// The string will excute in a subclass of SelectionDAGISel.
|
||||
// Cast to std::string explicitly to avoid ambiguity with StringRef.
|
||||
std::string C = IsHwMode
|
||||
? std::string("MF->getSubtarget().checkFeatures(\"" + Features + "\")")
|
||||
: std::string(Def->getValueAsString("CondString"));
|
||||
return IfCond ? C : "!("+C+')';
|
||||
}
|
||||
bool operator==(const Predicate &P) const {
|
||||
return IfCond == P.IfCond && IsHwMode == P.IsHwMode && Def == P.Def;
|
||||
}
|
||||
bool operator<(const Predicate &P) const {
|
||||
if (IsHwMode != P.IsHwMode)
|
||||
return IsHwMode < P.IsHwMode;
|
||||
assert(!Def == !P.Def && "Inconsistency between Def and IsHwMode");
|
||||
if (IfCond != P.IfCond)
|
||||
return IfCond < P.IfCond;
|
||||
if (Def)
|
||||
return LessRecord()(Def, P.Def);
|
||||
return Features < P.Features;
|
||||
}
|
||||
Record *Def; ///< Predicate definition from .td file, null for
|
||||
///< HW modes.
|
||||
std::string Features; ///< Feature string for HW mode.
|
||||
bool IfCond; ///< The boolean value that the condition has to
|
||||
///< evaluate to for this predicate to be true.
|
||||
bool IsHwMode; ///< Does this predicate correspond to a HW mode?
|
||||
};
|
||||
|
||||
/// PatternToMatch - Used by CodeGenDAGPatterns to keep tab of patterns
|
||||
/// processed to produce isel.
|
||||
class PatternToMatch {
|
||||
public:
|
||||
PatternToMatch(Record *srcrecord, ListInit *preds, TreePatternNode *src,
|
||||
TreePatternNode *dst, std::vector<Record *> dstregs,
|
||||
int complexity, unsigned uid)
|
||||
: SrcRecord(srcrecord), Predicates(preds), SrcPattern(src),
|
||||
DstPattern(dst), Dstregs(std::move(dstregs)),
|
||||
AddedComplexity(complexity), ID(uid) {}
|
||||
PatternToMatch(Record *srcrecord, const std::vector<Predicate> &preds,
|
||||
TreePatternNode *src, TreePatternNode *dst,
|
||||
const std::vector<Record*> &dstregs,
|
||||
int complexity, unsigned uid, unsigned setmode = 0)
|
||||
: SrcRecord(srcrecord), SrcPattern(src), DstPattern(dst),
|
||||
Predicates(preds), Dstregs(std::move(dstregs)),
|
||||
AddedComplexity(complexity), ID(uid), ForceMode(setmode) {}
|
||||
|
||||
PatternToMatch(Record *srcrecord, std::vector<Predicate> &&preds,
|
||||
TreePatternNode *src, TreePatternNode *dst,
|
||||
std::vector<Record*> &&dstregs,
|
||||
int complexity, unsigned uid, unsigned setmode = 0)
|
||||
: SrcRecord(srcrecord), SrcPattern(src), DstPattern(dst),
|
||||
Predicates(preds), Dstregs(std::move(dstregs)),
|
||||
AddedComplexity(complexity), ID(uid), ForceMode(setmode) {}
|
||||
|
||||
Record *SrcRecord; // Originating Record for the pattern.
|
||||
ListInit *Predicates; // Top level predicate conditions to match.
|
||||
TreePatternNode *SrcPattern; // Source pattern to match.
|
||||
TreePatternNode *DstPattern; // Resulting pattern.
|
||||
std::vector<Predicate> Predicates; // Top level predicate conditions
|
||||
// to match.
|
||||
std::vector<Record*> Dstregs; // Physical register defs being matched.
|
||||
int AddedComplexity; // Add to matching pattern complexity.
|
||||
unsigned ID; // Unique ID for the record.
|
||||
unsigned ForceMode; // Force this mode in type inference when set.
|
||||
|
||||
Record *getSrcRecord() const { return SrcRecord; }
|
||||
ListInit *getPredicates() const { return Predicates; }
|
||||
TreePatternNode *getSrcPattern() const { return SrcPattern; }
|
||||
TreePatternNode *getDstPattern() const { return DstPattern; }
|
||||
const std::vector<Record*> &getDstRegs() const { return Dstregs; }
|
||||
int getAddedComplexity() const { return AddedComplexity; }
|
||||
const std::vector<Predicate> &getPredicates() const { return Predicates; }
|
||||
|
||||
std::string getPredicateCheck() const;
|
||||
|
||||
|
@ -736,11 +830,15 @@ class CodeGenDAGPatterns {
|
|||
/// value is the pattern to match, the second pattern is the result to
|
||||
/// emit.
|
||||
std::vector<PatternToMatch> PatternsToMatch;
|
||||
|
||||
TypeSetByHwMode LegalVTS;
|
||||
|
||||
public:
|
||||
CodeGenDAGPatterns(RecordKeeper &R);
|
||||
|
||||
CodeGenTarget &getTargetInfo() { return Target; }
|
||||
const CodeGenTarget &getTargetInfo() const { return Target; }
|
||||
const TypeSetByHwMode &getLegalTypes() const { return LegalVTS; }
|
||||
|
||||
Record *getSDNodeNamed(const std::string &Name) const;
|
||||
|
||||
|
@ -850,10 +948,13 @@ private:
|
|||
void ParseDefaultOperands();
|
||||
void ParseInstructions();
|
||||
void ParsePatterns();
|
||||
void ExpandHwModeBasedTypes();
|
||||
void InferInstructionFlags();
|
||||
void GenerateVariants();
|
||||
void VerifyInstructionFlags();
|
||||
|
||||
std::vector<Predicate> makePredList(ListInit *L);
|
||||
|
||||
void AddPatternToMatch(TreePattern *Pattern, PatternToMatch &&PTM);
|
||||
void FindPatternInputsAndOutputs(TreePattern *I, TreePatternNode *Pat,
|
||||
std::map<std::string,
|
||||
|
@ -862,6 +963,15 @@ private:
|
|||
TreePatternNode*> &InstResults,
|
||||
std::vector<Record*> &InstImpResults);
|
||||
};
|
||||
|
||||
|
||||
inline bool SDNodeInfo::ApplyTypeConstraints(TreePatternNode *N,
|
||||
TreePattern &TP) const {
|
||||
bool MadeChange = false;
|
||||
for (unsigned i = 0, e = TypeConstraints.size(); i != e; ++i)
|
||||
MadeChange |= TypeConstraints[i].ApplyTypeConstraint(N, *this, TP);
|
||||
return MadeChange;
|
||||
}
|
||||
} // end namespace llvm
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
//===--- CodeGenHwModes.cpp -----------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Classes to parse and store HW mode information for instruction selection
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "CodeGenHwModes.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/TableGen/Error.h"
|
||||
#include "llvm/TableGen/Record.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
StringRef CodeGenHwModes::DefaultModeName = "DefaultMode";
|
||||
|
||||
HwMode::HwMode(Record *R) {
|
||||
Name = R->getName();
|
||||
Features = R->getValueAsString("Features");
|
||||
}
|
||||
|
||||
LLVM_DUMP_METHOD
|
||||
void HwMode::dump() const {
|
||||
dbgs() << Name << ": " << Features << '\n';
|
||||
}
|
||||
|
||||
HwModeSelect::HwModeSelect(Record *R, CodeGenHwModes &CGH) {
|
||||
std::vector<Record*> Modes = R->getValueAsListOfDefs("Modes");
|
||||
std::vector<Record*> Objects = R->getValueAsListOfDefs("Objects");
|
||||
if (Modes.size() != Objects.size()) {
|
||||
PrintError(R->getLoc(), "in record " + R->getName() +
|
||||
" derived from HwModeSelect: the lists Modes and Objects should "
|
||||
"have the same size");
|
||||
report_fatal_error("error in target description.");
|
||||
}
|
||||
for (unsigned i = 0, e = Modes.size(); i != e; ++i) {
|
||||
unsigned ModeId = CGH.getHwModeId(Modes[i]->getName());
|
||||
Items.push_back(std::make_pair(ModeId, Objects[i]));
|
||||
}
|
||||
}
|
||||
|
||||
LLVM_DUMP_METHOD
|
||||
void HwModeSelect::dump() const {
|
||||
dbgs() << '{';
|
||||
for (const PairType &P : Items)
|
||||
dbgs() << " (" << P.first << ',' << P.second->getName() << ')';
|
||||
dbgs() << " }\n";
|
||||
}
|
||||
|
||||
CodeGenHwModes::CodeGenHwModes(RecordKeeper &RK) : Records(RK) {
|
||||
std::vector<Record*> MRs = Records.getAllDerivedDefinitions("HwMode");
|
||||
// The default mode needs a definition in the .td sources for TableGen
|
||||
// to accept references to it. We need to ignore the definition here.
|
||||
for (auto I = MRs.begin(), E = MRs.end(); I != E; ++I) {
|
||||
if ((*I)->getName() != DefaultModeName)
|
||||
continue;
|
||||
MRs.erase(I);
|
||||
break;
|
||||
}
|
||||
|
||||
for (Record *R : MRs) {
|
||||
Modes.emplace_back(R);
|
||||
unsigned NewId = Modes.size();
|
||||
ModeIds.insert(std::make_pair(Modes[NewId-1].Name, NewId));
|
||||
}
|
||||
|
||||
std::vector<Record*> MSs = Records.getAllDerivedDefinitions("HwModeSelect");
|
||||
for (Record *R : MSs) {
|
||||
auto P = ModeSelects.emplace(std::make_pair(R, HwModeSelect(R, *this)));
|
||||
assert(P.second);
|
||||
(void)P;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned CodeGenHwModes::getHwModeId(StringRef Name) const {
|
||||
if (Name == DefaultModeName)
|
||||
return DefaultMode;
|
||||
auto F = ModeIds.find(Name);
|
||||
assert(F != ModeIds.end() && "Unknown mode name");
|
||||
return F->second;
|
||||
}
|
||||
|
||||
const HwModeSelect &CodeGenHwModes::getHwModeSelect(Record *R) const {
|
||||
auto F = ModeSelects.find(R);
|
||||
assert(F != ModeSelects.end() && "Record is not a \"mode select\"");
|
||||
return F->second;
|
||||
}
|
||||
|
||||
LLVM_DUMP_METHOD
|
||||
void CodeGenHwModes::dump() const {
|
||||
dbgs() << "Modes: {\n";
|
||||
for (const HwMode &M : Modes) {
|
||||
dbgs() << " ";
|
||||
M.dump();
|
||||
}
|
||||
dbgs() << "}\n";
|
||||
|
||||
dbgs() << "ModeIds: {\n";
|
||||
for (const auto &P : ModeIds)
|
||||
dbgs() << " " << P.first() << " -> " << P.second << '\n';
|
||||
dbgs() << "}\n";
|
||||
|
||||
dbgs() << "ModeSelects: {\n";
|
||||
for (const auto &P : ModeSelects) {
|
||||
dbgs() << " " << P.first->getName() << " -> ";
|
||||
P.second.dump();
|
||||
}
|
||||
dbgs() << "}\n";
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
//===--- CodeGenHwModes.h ---------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Classes to parse and store HW mode information for instruction selection.
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_UTILS_TABLEGEN_CODEGENHWMODES_H
|
||||
#define LLVM_UTILS_TABLEGEN_CODEGENHWMODES_H
|
||||
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// HwModeId -> list of predicates (definition)
|
||||
|
||||
namespace llvm {
|
||||
class Record;
|
||||
class RecordKeeper;
|
||||
|
||||
struct CodeGenHwModes;
|
||||
|
||||
struct HwMode {
|
||||
HwMode(Record *R);
|
||||
StringRef Name;
|
||||
std::string Features;
|
||||
void dump() const;
|
||||
};
|
||||
|
||||
struct HwModeSelect {
|
||||
HwModeSelect(Record *R, CodeGenHwModes &CGH);
|
||||
typedef std::pair<unsigned, Record*> PairType;
|
||||
std::vector<PairType> Items;
|
||||
void dump() const;
|
||||
};
|
||||
|
||||
struct CodeGenHwModes {
|
||||
enum : unsigned { DefaultMode = 0 };
|
||||
static StringRef DefaultModeName;
|
||||
|
||||
CodeGenHwModes(RecordKeeper &R);
|
||||
unsigned getHwModeId(StringRef Name) const;
|
||||
const HwMode &getMode(unsigned Id) const {
|
||||
assert(Id != 0 && "Mode id of 0 is reserved for the default mode");
|
||||
return Modes[Id-1];
|
||||
}
|
||||
const HwModeSelect &getHwModeSelect(Record *R) const;
|
||||
unsigned getNumModeIds() const { return Modes.size()+1; }
|
||||
void dump() const;
|
||||
|
||||
private:
|
||||
RecordKeeper &Records;
|
||||
StringMap<unsigned> ModeIds; // HwMode (string) -> HwModeId
|
||||
std::vector<HwMode> Modes;
|
||||
std::map<Record*,HwModeSelect> ModeSelects;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // LLVM_UTILS_TABLEGEN_CODEGENHWMODES_H
|
|
@ -375,10 +375,10 @@ HasOneImplicitDefWithKnownVT(const CodeGenTarget &TargetInfo) const {
|
|||
// Check to see if the first implicit def has a resolvable type.
|
||||
Record *FirstImplicitDef = ImplicitDefs[0];
|
||||
assert(FirstImplicitDef->isSubClassOf("Register"));
|
||||
const std::vector<MVT::SimpleValueType> &RegVTs =
|
||||
const std::vector<ValueTypeByHwMode> &RegVTs =
|
||||
TargetInfo.getRegisterVTs(FirstImplicitDef);
|
||||
if (RegVTs.size() == 1)
|
||||
return RegVTs[0];
|
||||
if (RegVTs.size() == 1 && RegVTs[0].isSimple())
|
||||
return RegVTs[0].getSimple().SimpleTy;
|
||||
return MVT::Other;
|
||||
}
|
||||
|
||||
|
|
|
@ -731,7 +731,7 @@ CodeGenRegisterClass::CodeGenRegisterClass(CodeGenRegBank &RegBank, Record *R)
|
|||
if (!Type->isSubClassOf("ValueType"))
|
||||
PrintFatalError("RegTypes list member '" + Type->getName() +
|
||||
"' does not derive from the ValueType class!");
|
||||
VTs.push_back(getValueType(Type));
|
||||
VTs.push_back(getValueTypeByHwMode(Type, RegBank.getHwModes()));
|
||||
}
|
||||
assert(!VTs.empty() && "RegisterClass must contain at least one ValueType!");
|
||||
|
||||
|
@ -764,12 +764,22 @@ CodeGenRegisterClass::CodeGenRegisterClass(CodeGenRegBank &RegBank, Record *R)
|
|||
}
|
||||
}
|
||||
|
||||
// Allow targets to override the size in bits of the RegisterClass.
|
||||
unsigned Size = R->getValueAsInt("Size");
|
||||
|
||||
Namespace = R->getValueAsString("Namespace");
|
||||
SpillSize = Size ? Size : MVT(VTs[0]).getSizeInBits();
|
||||
SpillAlignment = R->getValueAsInt("Alignment");
|
||||
|
||||
if (const RecordVal *RV = R->getValue("RegInfos"))
|
||||
if (DefInit *DI = dyn_cast_or_null<DefInit>(RV->getValue()))
|
||||
RSI = RegSizeInfoByHwMode(DI->getDef(), RegBank.getHwModes());
|
||||
unsigned Size = R->getValueAsInt("Size");
|
||||
assert((RSI.hasDefault() || Size != 0 || VTs[0].isSimple()) &&
|
||||
"Impossible to determine register size");
|
||||
if (!RSI.hasDefault()) {
|
||||
RegSizeInfo RI;
|
||||
RI.RegSize = RI.SpillSize = Size ? Size
|
||||
: VTs[0].getSimple().getSizeInBits();
|
||||
RI.SpillAlignment = R->getValueAsInt("Alignment");
|
||||
RSI.Map.insert({DefaultMode, RI});
|
||||
}
|
||||
|
||||
CopyCost = R->getValueAsInt("CopyCost");
|
||||
Allocatable = R->getValueAsBit("isAllocatable");
|
||||
AltOrderSelect = R->getValueAsString("AltOrderSelect");
|
||||
|
@ -789,8 +799,7 @@ CodeGenRegisterClass::CodeGenRegisterClass(CodeGenRegBank &RegBank,
|
|||
Name(Name),
|
||||
TopoSigs(RegBank.getNumTopoSigs()),
|
||||
EnumValue(-1),
|
||||
SpillSize(Props.SpillSize),
|
||||
SpillAlignment(Props.SpillAlignment),
|
||||
RSI(Props.RSI),
|
||||
CopyCost(0),
|
||||
Allocatable(true),
|
||||
AllocationPriority(0) {
|
||||
|
@ -832,7 +841,7 @@ bool CodeGenRegisterClass::contains(const CodeGenRegister *Reg) const {
|
|||
namespace llvm {
|
||||
|
||||
raw_ostream &operator<<(raw_ostream &OS, const CodeGenRegisterClass::Key &K) {
|
||||
OS << "{ S=" << K.SpillSize << ", A=" << K.SpillAlignment;
|
||||
OS << "{ " << K.RSI.getAsString();
|
||||
for (const auto R : *K.Members)
|
||||
OS << ", " << R->getName();
|
||||
return OS << " }";
|
||||
|
@ -845,8 +854,7 @@ namespace llvm {
|
|||
bool CodeGenRegisterClass::Key::
|
||||
operator<(const CodeGenRegisterClass::Key &B) const {
|
||||
assert(Members && B.Members);
|
||||
return std::tie(*Members, SpillSize, SpillAlignment) <
|
||||
std::tie(*B.Members, B.SpillSize, B.SpillAlignment);
|
||||
return std::tie(*Members, RSI) < std::tie(*B.Members, B.RSI);
|
||||
}
|
||||
|
||||
// Returns true if RC is a strict subclass.
|
||||
|
@ -860,8 +868,7 @@ operator<(const CodeGenRegisterClass::Key &B) const {
|
|||
//
|
||||
static bool testSubClass(const CodeGenRegisterClass *A,
|
||||
const CodeGenRegisterClass *B) {
|
||||
return A->SpillAlignment && B->SpillAlignment % A->SpillAlignment == 0 &&
|
||||
A->SpillSize <= B->SpillSize &&
|
||||
return A->RSI.isSubClassOf(B->RSI) &&
|
||||
std::includes(A->getMembers().begin(), A->getMembers().end(),
|
||||
B->getMembers().begin(), B->getMembers().end(),
|
||||
deref<llvm::less>());
|
||||
|
@ -880,16 +887,9 @@ static bool TopoOrderRC(const CodeGenRegisterClass &PA,
|
|||
if (A == B)
|
||||
return false;
|
||||
|
||||
// Order by ascending spill size.
|
||||
if (A->SpillSize < B->SpillSize)
|
||||
if (A->RSI < B->RSI)
|
||||
return true;
|
||||
if (A->SpillSize > B->SpillSize)
|
||||
return false;
|
||||
|
||||
// Order by ascending spill alignment.
|
||||
if (A->SpillAlignment < B->SpillAlignment)
|
||||
return true;
|
||||
if (A->SpillAlignment > B->SpillAlignment)
|
||||
if (A->RSI != B->RSI)
|
||||
return false;
|
||||
|
||||
// Order by descending set size. Note that the classes' allocation order may
|
||||
|
@ -1062,7 +1062,8 @@ void CodeGenRegisterClass::buildRegUnitSet(
|
|||
// CodeGenRegBank
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
CodeGenRegBank::CodeGenRegBank(RecordKeeper &Records) {
|
||||
CodeGenRegBank::CodeGenRegBank(RecordKeeper &Records,
|
||||
const CodeGenHwModes &Modes) : CGH(Modes) {
|
||||
// Configure register Sets to understand register classes and tuples.
|
||||
Sets.addFieldExpander("RegisterClass", "MemberList");
|
||||
Sets.addFieldExpander("CalleeSavedRegs", "SaveList");
|
||||
|
@ -1202,7 +1203,7 @@ CodeGenRegBank::getOrCreateSubClass(const CodeGenRegisterClass *RC,
|
|||
const CodeGenRegister::Vec *Members,
|
||||
StringRef Name) {
|
||||
// Synthetic sub-class has the same size and alignment as RC.
|
||||
CodeGenRegisterClass::Key K(Members, RC->SpillSize, RC->SpillAlignment);
|
||||
CodeGenRegisterClass::Key K(Members, RC->RSI);
|
||||
RCKeyMap::const_iterator FoundI = Key2RC.find(K);
|
||||
if (FoundI != Key2RC.end())
|
||||
return FoundI->second;
|
||||
|
@ -2050,10 +2051,8 @@ void CodeGenRegBank::inferCommonSubClass(CodeGenRegisterClass *RC) {
|
|||
continue;
|
||||
|
||||
// If RC1 and RC2 have different spill sizes or alignments, use the
|
||||
// larger size for sub-classing. If they are equal, prefer RC1.
|
||||
if (RC2->SpillSize > RC1->SpillSize ||
|
||||
(RC2->SpillSize == RC1->SpillSize &&
|
||||
RC2->SpillAlignment > RC1->SpillAlignment))
|
||||
// stricter one for sub-classing. If they are equal, prefer RC1.
|
||||
if (RC2->RSI.hasStricterSpillThan(RC1->RSI))
|
||||
std::swap(RC1, RC2);
|
||||
|
||||
getOrCreateSubClass(RC1, &Intersection,
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#ifndef LLVM_UTILS_TABLEGEN_CODEGENREGISTERS_H
|
||||
#define LLVM_UTILS_TABLEGEN_CODEGENREGISTERS_H
|
||||
|
||||
#include "InfoByHwMode.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/BitVector.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
|
@ -319,9 +320,8 @@ namespace llvm {
|
|||
public:
|
||||
unsigned EnumValue;
|
||||
StringRef Namespace;
|
||||
SmallVector<MVT::SimpleValueType, 4> VTs;
|
||||
unsigned SpillSize;
|
||||
unsigned SpillAlignment;
|
||||
SmallVector<ValueTypeByHwMode, 4> VTs;
|
||||
RegSizeInfoByHwMode RSI;
|
||||
int CopyCost;
|
||||
bool Allocatable;
|
||||
StringRef AltOrderSelect;
|
||||
|
@ -338,13 +338,10 @@ namespace llvm {
|
|||
|
||||
const std::string &getName() const { return Name; }
|
||||
std::string getQualifiedName() const;
|
||||
ArrayRef<MVT::SimpleValueType> getValueTypes() const {return VTs;}
|
||||
bool hasValueType(MVT::SimpleValueType VT) const {
|
||||
return std::find(VTs.begin(), VTs.end(), VT) != VTs.end();
|
||||
}
|
||||
ArrayRef<ValueTypeByHwMode> getValueTypes() const { return VTs; }
|
||||
unsigned getNumValueTypes() const { return VTs.size(); }
|
||||
|
||||
MVT::SimpleValueType getValueTypeNum(unsigned VTNum) const {
|
||||
ValueTypeByHwMode getValueTypeNum(unsigned VTNum) const {
|
||||
if (VTNum < VTs.size())
|
||||
return VTs[VTNum];
|
||||
llvm_unreachable("VTNum greater than number of ValueTypes in RegClass!");
|
||||
|
@ -439,18 +436,15 @@ namespace llvm {
|
|||
// the topological order used for the EnumValues.
|
||||
struct Key {
|
||||
const CodeGenRegister::Vec *Members;
|
||||
unsigned SpillSize;
|
||||
unsigned SpillAlignment;
|
||||
RegSizeInfoByHwMode RSI;
|
||||
|
||||
Key(const CodeGenRegister::Vec *M, unsigned S = 0, unsigned A = 0)
|
||||
: Members(M), SpillSize(S), SpillAlignment(A) {}
|
||||
Key(const CodeGenRegister::Vec *M, const RegSizeInfoByHwMode &I)
|
||||
: Members(M), RSI(I) {}
|
||||
|
||||
Key(const CodeGenRegisterClass &RC)
|
||||
: Members(&RC.getMembers()),
|
||||
SpillSize(RC.SpillSize),
|
||||
SpillAlignment(RC.SpillAlignment) {}
|
||||
: Members(&RC.getMembers()), RSI(RC.RSI) {}
|
||||
|
||||
// Lexicographical order of (Members, SpillSize, SpillAlignment).
|
||||
// Lexicographical order of (Members, RegSizeInfoByHwMode).
|
||||
bool operator<(const Key&) const;
|
||||
};
|
||||
|
||||
|
@ -513,6 +507,8 @@ namespace llvm {
|
|||
class CodeGenRegBank {
|
||||
SetTheory Sets;
|
||||
|
||||
const CodeGenHwModes &CGH;
|
||||
|
||||
std::deque<CodeGenSubRegIndex> SubRegIndices;
|
||||
DenseMap<Record*, CodeGenSubRegIndex*> Def2SubRegIdx;
|
||||
|
||||
|
@ -596,10 +592,12 @@ namespace llvm {
|
|||
void computeRegUnitLaneMasks();
|
||||
|
||||
public:
|
||||
CodeGenRegBank(RecordKeeper&);
|
||||
CodeGenRegBank(RecordKeeper&, const CodeGenHwModes&);
|
||||
|
||||
SetTheory &getSets() { return Sets; }
|
||||
|
||||
const CodeGenHwModes &getHwModes() const { return CGH; }
|
||||
|
||||
// Sub-register indices. The first NumNamedIndices are defined by the user
|
||||
// in the .td files. The rest are synthesized such that all sub-registers
|
||||
// have a unique name.
|
||||
|
|
|
@ -191,7 +191,7 @@ std::string llvm::getQualifiedName(const Record *R) {
|
|||
/// getTarget - Return the current instance of the Target class.
|
||||
///
|
||||
CodeGenTarget::CodeGenTarget(RecordKeeper &records)
|
||||
: Records(records) {
|
||||
: Records(records), CGH(records) {
|
||||
std::vector<Record*> Targets = Records.getAllDerivedDefinitions("Target");
|
||||
if (Targets.size() == 0)
|
||||
PrintFatalError("ERROR: No 'Target' subclasses defined!");
|
||||
|
@ -266,7 +266,7 @@ Record *CodeGenTarget::getAsmWriter() const {
|
|||
|
||||
CodeGenRegBank &CodeGenTarget::getRegBank() const {
|
||||
if (!RegBank)
|
||||
RegBank = llvm::make_unique<CodeGenRegBank>(Records);
|
||||
RegBank = llvm::make_unique<CodeGenRegBank>(Records, getHwModes());
|
||||
return *RegBank;
|
||||
}
|
||||
|
||||
|
@ -285,19 +285,19 @@ const CodeGenRegister *CodeGenTarget::getRegisterByName(StringRef Name) const {
|
|||
return I->second;
|
||||
}
|
||||
|
||||
std::vector<MVT::SimpleValueType> CodeGenTarget::
|
||||
getRegisterVTs(Record *R) const {
|
||||
std::vector<ValueTypeByHwMode> CodeGenTarget::getRegisterVTs(Record *R)
|
||||
const {
|
||||
const CodeGenRegister *Reg = getRegBank().getReg(R);
|
||||
std::vector<MVT::SimpleValueType> Result;
|
||||
std::vector<ValueTypeByHwMode> Result;
|
||||
for (const auto &RC : getRegBank().getRegClasses()) {
|
||||
if (RC.contains(Reg)) {
|
||||
ArrayRef<MVT::SimpleValueType> InVTs = RC.getValueTypes();
|
||||
ArrayRef<ValueTypeByHwMode> InVTs = RC.getValueTypes();
|
||||
Result.insert(Result.end(), InVTs.begin(), InVTs.end());
|
||||
}
|
||||
}
|
||||
|
||||
// Remove duplicates.
|
||||
array_pod_sort(Result.begin(), Result.end());
|
||||
std::sort(Result.begin(), Result.end());
|
||||
Result.erase(std::unique(Result.begin(), Result.end()), Result.end());
|
||||
return Result;
|
||||
}
|
||||
|
@ -308,7 +308,7 @@ void CodeGenTarget::ReadLegalValueTypes() const {
|
|||
LegalValueTypes.insert(LegalValueTypes.end(), RC.VTs.begin(), RC.VTs.end());
|
||||
|
||||
// Remove duplicates.
|
||||
array_pod_sort(LegalValueTypes.begin(), LegalValueTypes.end());
|
||||
std::sort(LegalValueTypes.begin(), LegalValueTypes.end());
|
||||
LegalValueTypes.erase(std::unique(LegalValueTypes.begin(),
|
||||
LegalValueTypes.end()),
|
||||
LegalValueTypes.end());
|
||||
|
|
|
@ -17,8 +17,10 @@
|
|||
#ifndef LLVM_UTILS_TABLEGEN_CODEGENTARGET_H
|
||||
#define LLVM_UTILS_TABLEGEN_CODEGENTARGET_H
|
||||
|
||||
#include "CodeGenHwModes.h"
|
||||
#include "CodeGenInstruction.h"
|
||||
#include "CodeGenRegisters.h"
|
||||
#include "InfoByHwMode.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/TableGen/Record.h"
|
||||
#include <algorithm>
|
||||
|
@ -69,7 +71,8 @@ class CodeGenTarget {
|
|||
std::unique_ptr<CodeGenInstruction>> Instructions;
|
||||
mutable std::unique_ptr<CodeGenRegBank> RegBank;
|
||||
mutable std::vector<Record*> RegAltNameIndices;
|
||||
mutable SmallVector<MVT::SimpleValueType, 8> LegalValueTypes;
|
||||
mutable SmallVector<ValueTypeByHwMode, 8> LegalValueTypes;
|
||||
CodeGenHwModes CGH;
|
||||
void ReadRegAltNameIndices() const;
|
||||
void ReadInstructions() const;
|
||||
void ReadLegalValueTypes() const;
|
||||
|
@ -128,22 +131,18 @@ public:
|
|||
|
||||
/// getRegisterVTs - Find the union of all possible SimpleValueTypes for the
|
||||
/// specified physical register.
|
||||
std::vector<MVT::SimpleValueType> getRegisterVTs(Record *R) const;
|
||||
std::vector<ValueTypeByHwMode> getRegisterVTs(Record *R) const;
|
||||
|
||||
ArrayRef<MVT::SimpleValueType> getLegalValueTypes() const {
|
||||
if (LegalValueTypes.empty()) ReadLegalValueTypes();
|
||||
ArrayRef<ValueTypeByHwMode> getLegalValueTypes() const {
|
||||
if (LegalValueTypes.empty())
|
||||
ReadLegalValueTypes();
|
||||
return LegalValueTypes;
|
||||
}
|
||||
|
||||
/// isLegalValueType - Return true if the specified value type is natively
|
||||
/// supported by the target (i.e. there are registers that directly hold it).
|
||||
bool isLegalValueType(MVT::SimpleValueType VT) const {
|
||||
ArrayRef<MVT::SimpleValueType> LegalVTs = getLegalValueTypes();
|
||||
return is_contained(LegalVTs, VT);
|
||||
}
|
||||
|
||||
CodeGenSchedModels &getSchedModels() const;
|
||||
|
||||
const CodeGenHwModes &getHwModes() const { return CGH; }
|
||||
|
||||
private:
|
||||
DenseMap<const Record*, std::unique_ptr<CodeGenInstruction>> &
|
||||
getInstructions() const {
|
||||
|
|
|
@ -80,11 +80,11 @@ struct PatternSortingPredicate {
|
|||
CodeGenDAGPatterns &CGP;
|
||||
|
||||
bool operator()(const PatternToMatch *LHS, const PatternToMatch *RHS) {
|
||||
const TreePatternNode *LHSSrc = LHS->getSrcPattern();
|
||||
const TreePatternNode *RHSSrc = RHS->getSrcPattern();
|
||||
const TreePatternNode *LT = LHS->getSrcPattern();
|
||||
const TreePatternNode *RT = RHS->getSrcPattern();
|
||||
|
||||
MVT LHSVT = (LHSSrc->getNumTypes() != 0 ? LHSSrc->getType(0) : MVT::Other);
|
||||
MVT RHSVT = (RHSSrc->getNumTypes() != 0 ? RHSSrc->getType(0) : MVT::Other);
|
||||
MVT LHSVT = LT->getNumTypes() != 0 ? LT->getSimpleType(0) : MVT::Other;
|
||||
MVT RHSVT = RT->getNumTypes() != 0 ? RT->getSimpleType(0) : MVT::Other;
|
||||
if (LHSVT.isVector() != RHSVT.isVector())
|
||||
return RHSVT.isVector();
|
||||
|
||||
|
|
|
@ -33,12 +33,18 @@ static MVT::SimpleValueType getRegisterValueType(Record *R,
|
|||
|
||||
if (!FoundRC) {
|
||||
FoundRC = true;
|
||||
VT = RC.getValueTypeNum(0);
|
||||
ValueTypeByHwMode VVT = RC.getValueTypeNum(0);
|
||||
if (VVT.isSimple())
|
||||
VT = VVT.getSimple().SimpleTy;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this occurs in multiple register classes, they all have to agree.
|
||||
assert(VT == RC.getValueTypeNum(0));
|
||||
#ifndef NDEBUG
|
||||
ValueTypeByHwMode T = RC.getValueTypeNum(0);
|
||||
assert((!T.isSimple() || T.getSimple().SimpleTy == VT) &&
|
||||
"ValueType mismatch between register classes for this register");
|
||||
#endif
|
||||
}
|
||||
return VT;
|
||||
}
|
||||
|
@ -105,13 +111,15 @@ namespace {
|
|||
Matcher *GetMatcher() const { return TheMatcher; }
|
||||
private:
|
||||
void AddMatcher(Matcher *NewNode);
|
||||
void InferPossibleTypes();
|
||||
void InferPossibleTypes(unsigned ForceMode);
|
||||
|
||||
// Matcher Generation.
|
||||
void EmitMatchCode(const TreePatternNode *N, TreePatternNode *NodeNoTypes);
|
||||
void EmitMatchCode(const TreePatternNode *N, TreePatternNode *NodeNoTypes,
|
||||
unsigned ForceMode);
|
||||
void EmitLeafMatchCode(const TreePatternNode *N);
|
||||
void EmitOperatorMatchCode(const TreePatternNode *N,
|
||||
TreePatternNode *NodeNoTypes);
|
||||
TreePatternNode *NodeNoTypes,
|
||||
unsigned ForceMode);
|
||||
|
||||
/// If this is the first time a node with unique identifier Name has been
|
||||
/// seen, record it. Otherwise, emit a check to make sure this is the same
|
||||
|
@ -164,17 +172,19 @@ MatcherGen::MatcherGen(const PatternToMatch &pattern,
|
|||
PatWithNoTypes->RemoveAllTypes();
|
||||
|
||||
// If there are types that are manifestly known, infer them.
|
||||
InferPossibleTypes();
|
||||
InferPossibleTypes(Pattern.ForceMode);
|
||||
}
|
||||
|
||||
/// InferPossibleTypes - As we emit the pattern, we end up generating type
|
||||
/// checks and applying them to the 'PatWithNoTypes' tree. As we do this, we
|
||||
/// want to propagate implied types as far throughout the tree as possible so
|
||||
/// that we avoid doing redundant type checks. This does the type propagation.
|
||||
void MatcherGen::InferPossibleTypes() {
|
||||
void MatcherGen::InferPossibleTypes(unsigned ForceMode) {
|
||||
// TP - Get *SOME* tree pattern, we don't care which. It is only used for
|
||||
// diagnostics, which we know are impossible at this point.
|
||||
TreePattern &TP = *CGP.pf_begin()->second;
|
||||
TP.getInfer().CodeGen = true;
|
||||
TP.getInfer().ForceMode = ForceMode;
|
||||
|
||||
bool MadeChange = true;
|
||||
while (MadeChange)
|
||||
|
@ -281,7 +291,8 @@ void MatcherGen::EmitLeafMatchCode(const TreePatternNode *N) {
|
|||
}
|
||||
|
||||
void MatcherGen::EmitOperatorMatchCode(const TreePatternNode *N,
|
||||
TreePatternNode *NodeNoTypes) {
|
||||
TreePatternNode *NodeNoTypes,
|
||||
unsigned ForceMode) {
|
||||
assert(!N->isLeaf() && "Not an operator?");
|
||||
|
||||
if (N->getOperator()->isSubClassOf("ComplexPattern")) {
|
||||
|
@ -334,7 +345,7 @@ void MatcherGen::EmitOperatorMatchCode(const TreePatternNode *N,
|
|||
|
||||
// Match the LHS of the AND as appropriate.
|
||||
AddMatcher(new MoveChildMatcher(0));
|
||||
EmitMatchCode(N->getChild(0), NodeNoTypes->getChild(0));
|
||||
EmitMatchCode(N->getChild(0), NodeNoTypes->getChild(0), ForceMode);
|
||||
AddMatcher(new MoveParentMatcher());
|
||||
return;
|
||||
}
|
||||
|
@ -433,7 +444,7 @@ void MatcherGen::EmitOperatorMatchCode(const TreePatternNode *N,
|
|||
// Get the code suitable for matching this child. Move to the child, check
|
||||
// it then move back to the parent.
|
||||
AddMatcher(new MoveChildMatcher(OpNo));
|
||||
EmitMatchCode(N->getChild(i), NodeNoTypes->getChild(i));
|
||||
EmitMatchCode(N->getChild(i), NodeNoTypes->getChild(i), ForceMode);
|
||||
AddMatcher(new MoveParentMatcher());
|
||||
}
|
||||
}
|
||||
|
@ -456,7 +467,8 @@ bool MatcherGen::recordUniqueNode(const std::string &Name) {
|
|||
}
|
||||
|
||||
void MatcherGen::EmitMatchCode(const TreePatternNode *N,
|
||||
TreePatternNode *NodeNoTypes) {
|
||||
TreePatternNode *NodeNoTypes,
|
||||
unsigned ForceMode) {
|
||||
// If N and NodeNoTypes don't agree on a type, then this is a case where we
|
||||
// need to do a type check. Emit the check, apply the type to NodeNoTypes and
|
||||
// reinfer any correlated types.
|
||||
|
@ -465,7 +477,7 @@ void MatcherGen::EmitMatchCode(const TreePatternNode *N,
|
|||
for (unsigned i = 0, e = NodeNoTypes->getNumTypes(); i != e; ++i) {
|
||||
if (NodeNoTypes->getExtType(i) == N->getExtType(i)) continue;
|
||||
NodeNoTypes->setType(i, N->getExtType(i));
|
||||
InferPossibleTypes();
|
||||
InferPossibleTypes(ForceMode);
|
||||
ResultsToTypeCheck.push_back(i);
|
||||
}
|
||||
|
||||
|
@ -478,14 +490,14 @@ void MatcherGen::EmitMatchCode(const TreePatternNode *N,
|
|||
if (N->isLeaf())
|
||||
EmitLeafMatchCode(N);
|
||||
else
|
||||
EmitOperatorMatchCode(N, NodeNoTypes);
|
||||
EmitOperatorMatchCode(N, NodeNoTypes, ForceMode);
|
||||
|
||||
// If there are node predicates for this node, generate their checks.
|
||||
for (unsigned i = 0, e = N->getPredicateFns().size(); i != e; ++i)
|
||||
AddMatcher(new CheckPredicateMatcher(N->getPredicateFns()[i]));
|
||||
|
||||
for (unsigned i = 0, e = ResultsToTypeCheck.size(); i != e; ++i)
|
||||
AddMatcher(new CheckTypeMatcher(N->getType(ResultsToTypeCheck[i]),
|
||||
AddMatcher(new CheckTypeMatcher(N->getSimpleType(ResultsToTypeCheck[i]),
|
||||
ResultsToTypeCheck[i]));
|
||||
}
|
||||
|
||||
|
@ -509,7 +521,7 @@ bool MatcherGen::EmitMatcherCode(unsigned Variant) {
|
|||
}
|
||||
|
||||
// Emit the matcher for the pattern structure and types.
|
||||
EmitMatchCode(Pattern.getSrcPattern(), PatWithNoTypes);
|
||||
EmitMatchCode(Pattern.getSrcPattern(), PatWithNoTypes, Pattern.ForceMode);
|
||||
|
||||
// If the pattern has a predicate on it (e.g. only enabled when a subtarget
|
||||
// feature is around, do the check).
|
||||
|
@ -606,7 +618,7 @@ void MatcherGen::EmitResultLeafAsOperand(const TreePatternNode *N,
|
|||
assert(N->isLeaf() && "Must be a leaf");
|
||||
|
||||
if (IntInit *II = dyn_cast<IntInit>(N->getLeafValue())) {
|
||||
AddMatcher(new EmitIntegerMatcher(II->getValue(), N->getType(0)));
|
||||
AddMatcher(new EmitIntegerMatcher(II->getValue(), N->getSimpleType(0)));
|
||||
ResultOps.push_back(NextRecordedOperandNo++);
|
||||
return;
|
||||
}
|
||||
|
@ -617,13 +629,13 @@ void MatcherGen::EmitResultLeafAsOperand(const TreePatternNode *N,
|
|||
if (Def->isSubClassOf("Register")) {
|
||||
const CodeGenRegister *Reg =
|
||||
CGP.getTargetInfo().getRegBank().getReg(Def);
|
||||
AddMatcher(new EmitRegisterMatcher(Reg, N->getType(0)));
|
||||
AddMatcher(new EmitRegisterMatcher(Reg, N->getSimpleType(0)));
|
||||
ResultOps.push_back(NextRecordedOperandNo++);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Def->getName() == "zero_reg") {
|
||||
AddMatcher(new EmitRegisterMatcher(nullptr, N->getType(0)));
|
||||
AddMatcher(new EmitRegisterMatcher(nullptr, N->getSimpleType(0)));
|
||||
ResultOps.push_back(NextRecordedOperandNo++);
|
||||
return;
|
||||
}
|
||||
|
@ -834,7 +846,7 @@ EmitResultInstructionAsOperand(const TreePatternNode *N,
|
|||
// Determine the result types.
|
||||
SmallVector<MVT::SimpleValueType, 4> ResultVTs;
|
||||
for (unsigned i = 0, e = N->getNumTypes(); i != e; ++i)
|
||||
ResultVTs.push_back(N->getType(i));
|
||||
ResultVTs.push_back(N->getSimpleType(i));
|
||||
|
||||
// If this is the root instruction of a pattern that has physical registers in
|
||||
// its result pattern, add output VTs for them. For example, X86 has:
|
||||
|
|
|
@ -159,10 +159,11 @@ struct OperandsSignature {
|
|||
TreePredicateFn PredFn = ImmPredicates.getPredicate(Code-1);
|
||||
|
||||
// Emit the type check.
|
||||
OS << "VT == "
|
||||
<< getEnumName(PredFn.getOrigPatFragRecord()->getTree(0)->getType(0))
|
||||
<< " && ";
|
||||
|
||||
TreePattern *TP = PredFn.getOrigPatFragRecord();
|
||||
ValueTypeByHwMode VVT = TP->getTree(0)->getType(0);
|
||||
assert(VVT.isSimple() &&
|
||||
"Cannot use variable value types with fast isel");
|
||||
OS << "VT == " << getEnumName(VVT.getSimple().SimpleTy) << " && ";
|
||||
|
||||
OS << PredFn.getFnName() << "(imm" << i <<')';
|
||||
EmittedAnything = true;
|
||||
|
@ -236,12 +237,12 @@ struct OperandsSignature {
|
|||
return false;
|
||||
}
|
||||
|
||||
assert(Op->hasTypeSet(0) && "Type infererence not done?");
|
||||
assert(Op->hasConcreteType(0) && "Type infererence not done?");
|
||||
|
||||
// For now, all the operands must have the same type (if they aren't
|
||||
// immediates). Note that this causes us to reject variable sized shifts
|
||||
// on X86.
|
||||
if (Op->getType(0) != VT)
|
||||
if (Op->getSimpleType(0) != VT)
|
||||
return false;
|
||||
|
||||
DefInit *OpDI = dyn_cast<DefInit>(Op->getLeafValue());
|
||||
|
@ -502,11 +503,11 @@ void FastISelMap::collectPatterns(CodeGenDAGPatterns &CGP) {
|
|||
Record *InstPatOp = InstPatNode->getOperator();
|
||||
std::string OpcodeName = getOpcodeName(InstPatOp, CGP);
|
||||
MVT::SimpleValueType RetVT = MVT::isVoid;
|
||||
if (InstPatNode->getNumTypes()) RetVT = InstPatNode->getType(0);
|
||||
if (InstPatNode->getNumTypes()) RetVT = InstPatNode->getSimpleType(0);
|
||||
MVT::SimpleValueType VT = RetVT;
|
||||
if (InstPatNode->getNumChildren()) {
|
||||
assert(InstPatNode->getChild(0)->getNumTypes() == 1);
|
||||
VT = InstPatNode->getChild(0)->getType(0);
|
||||
VT = InstPatNode->getChild(0)->getSimpleType(0);
|
||||
}
|
||||
|
||||
// For now, filter out any instructions with predicates.
|
||||
|
|
|
@ -1893,7 +1893,7 @@ private:
|
|||
void gatherNodeEquivs();
|
||||
const CodeGenInstruction *findNodeEquiv(Record *N) const;
|
||||
|
||||
Error importRulePredicates(RuleMatcher &M, ArrayRef<Init *> Predicates);
|
||||
Error importRulePredicates(RuleMatcher &M, ArrayRef<Predicate> Predicates);
|
||||
Expected<InstructionMatcher &>
|
||||
createAndImportSelDAGMatcher(InstructionMatcher &InsnMatcher,
|
||||
const TreePatternNode *Src,
|
||||
|
@ -1940,17 +1940,19 @@ const CodeGenInstruction *GlobalISelEmitter::findNodeEquiv(Record *N) const {
|
|||
}
|
||||
|
||||
GlobalISelEmitter::GlobalISelEmitter(RecordKeeper &RK)
|
||||
: RK(RK), CGP(RK), Target(CGP.getTargetInfo()), CGRegs(RK) {}
|
||||
: RK(RK), CGP(RK), Target(CGP.getTargetInfo()),
|
||||
CGRegs(RK, Target.getHwModes()) {}
|
||||
|
||||
//===- Emitter ------------------------------------------------------------===//
|
||||
|
||||
Error
|
||||
GlobalISelEmitter::importRulePredicates(RuleMatcher &M,
|
||||
ArrayRef<Init *> Predicates) {
|
||||
for (const Init *Predicate : Predicates) {
|
||||
const DefInit *PredicateDef = static_cast<const DefInit *>(Predicate);
|
||||
declareSubtargetFeature(PredicateDef->getDef());
|
||||
M.addRequiredFeature(PredicateDef->getDef());
|
||||
ArrayRef<Predicate> Predicates) {
|
||||
for (const Predicate &P : Predicates) {
|
||||
if (!P.Def)
|
||||
continue;
|
||||
declareSubtargetFeature(P.Def);
|
||||
M.addRequiredFeature(P.Def);
|
||||
}
|
||||
|
||||
return Error::success();
|
||||
|
@ -1986,9 +1988,10 @@ GlobalISelEmitter::createAndImportSelDAGMatcher(InstructionMatcher &InsnMatcher,
|
|||
}
|
||||
|
||||
unsigned OpIdx = 0;
|
||||
for (const EEVT::TypeSet &Ty : Src->getExtTypes()) {
|
||||
auto OpTyOrNone = MVTToLLT(Ty.getConcrete());
|
||||
|
||||
for (const TypeSetByHwMode &VTy : Src->getExtTypes()) {
|
||||
auto OpTyOrNone = VTy.isMachineValueType()
|
||||
? MVTToLLT(VTy.getMachineValueType().SimpleTy)
|
||||
: None;
|
||||
if (!OpTyOrNone)
|
||||
return failedImport(
|
||||
"Result of Src pattern operator has an unsupported type");
|
||||
|
@ -2064,7 +2067,7 @@ Error GlobalISelEmitter::importChildMatcher(InstructionMatcher &InsnMatcher,
|
|||
OperandMatcher &OM =
|
||||
InsnMatcher.addOperand(OpIdx, SrcChild->getName(), TempOpIdx);
|
||||
|
||||
ArrayRef<EEVT::TypeSet> ChildTypes = SrcChild->getExtTypes();
|
||||
ArrayRef<TypeSetByHwMode> ChildTypes = SrcChild->getExtTypes();
|
||||
if (ChildTypes.size() != 1)
|
||||
return failedImport("Src pattern child has multiple results");
|
||||
|
||||
|
@ -2079,7 +2082,9 @@ Error GlobalISelEmitter::importChildMatcher(InstructionMatcher &InsnMatcher,
|
|||
}
|
||||
}
|
||||
|
||||
auto OpTyOrNone = MVTToLLT(ChildTypes.front().getConcrete());
|
||||
Optional<LLTCodeGen> OpTyOrNone = None;
|
||||
if (ChildTypes.front().isMachineValueType())
|
||||
OpTyOrNone = MVTToLLT(ChildTypes.front().getMachineValueType().SimpleTy);
|
||||
if (!OpTyOrNone)
|
||||
return failedImport("Src operand has an unsupported type (" + to_string(*SrcChild) + ")");
|
||||
OM.addPredicate<LLTOperandMatcher>(*OpTyOrNone);
|
||||
|
@ -2177,11 +2182,13 @@ Error GlobalISelEmitter::importExplicitUseRenderer(
|
|||
if (auto *ChildDefInit = dyn_cast<DefInit>(DstChild->getLeafValue())) {
|
||||
auto *ChildRec = ChildDefInit->getDef();
|
||||
|
||||
ArrayRef<EEVT::TypeSet> ChildTypes = DstChild->getExtTypes();
|
||||
ArrayRef<TypeSetByHwMode> ChildTypes = DstChild->getExtTypes();
|
||||
if (ChildTypes.size() != 1)
|
||||
return failedImport("Dst pattern child has multiple results");
|
||||
|
||||
auto OpTyOrNone = MVTToLLT(ChildTypes.front().getConcrete());
|
||||
Optional<LLTCodeGen> OpTyOrNone = None;
|
||||
if (ChildTypes.front().isMachineValueType())
|
||||
OpTyOrNone = MVTToLLT(ChildTypes.front().getMachineValueType().SimpleTy);
|
||||
if (!OpTyOrNone)
|
||||
return failedImport("Dst operand has an unsupported type");
|
||||
|
||||
|
@ -2360,7 +2367,7 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
|
|||
RuleMatcher M;
|
||||
M.addAction<DebugCommentAction>(P);
|
||||
|
||||
if (auto Error = importRulePredicates(M, P.getPredicates()->getValues()))
|
||||
if (auto Error = importRulePredicates(M, P.getPredicates()))
|
||||
return std::move(Error);
|
||||
|
||||
// Next, analyze the pattern operators.
|
||||
|
@ -2426,8 +2433,8 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
|
|||
// The root of the match also has constraints on the register bank so that it
|
||||
// matches the result instruction.
|
||||
unsigned OpIdx = 0;
|
||||
for (const EEVT::TypeSet &Ty : Src->getExtTypes()) {
|
||||
(void)Ty;
|
||||
for (const TypeSetByHwMode &VTy : Src->getExtTypes()) {
|
||||
(void)VTy;
|
||||
|
||||
const auto &DstIOperand = DstI.Operands[OpIdx];
|
||||
Record *DstIOpRec = DstIOperand.Rec;
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
//===--- InfoByHwMode.cpp -------------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Classes that implement data parameterized by HW modes for instruction
|
||||
// selection. Currently it is ValueTypeByHwMode (parameterized ValueType),
|
||||
// and RegSizeInfoByHwMode (parameterized register/spill size and alignment
|
||||
// data).
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "CodeGenTarget.h"
|
||||
#include "InfoByHwMode.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Support/Debug.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
std::string llvm::getModeName(unsigned Mode) {
|
||||
if (Mode == DefaultMode)
|
||||
return "*";
|
||||
return (Twine('m') + Twine(Mode)).str();
|
||||
}
|
||||
|
||||
ValueTypeByHwMode::ValueTypeByHwMode(Record *R, const CodeGenHwModes &CGH) {
|
||||
const HwModeSelect &MS = CGH.getHwModeSelect(R);
|
||||
for (const HwModeSelect::PairType &P : MS.Items) {
|
||||
auto I = Map.insert({P.first, MVT(llvm::getValueType(P.second))});
|
||||
assert(I.second && "Duplicate entry?");
|
||||
(void)I;
|
||||
}
|
||||
}
|
||||
|
||||
bool ValueTypeByHwMode::operator== (const ValueTypeByHwMode &T) const {
|
||||
assert(isValid() && T.isValid() && "Invalid type in assignment");
|
||||
bool Simple = isSimple();
|
||||
if (Simple != T.isSimple())
|
||||
return false;
|
||||
if (Simple)
|
||||
return getSimple() == T.getSimple();
|
||||
|
||||
return Map == T.Map;
|
||||
}
|
||||
|
||||
bool ValueTypeByHwMode::operator< (const ValueTypeByHwMode &T) const {
|
||||
assert(isValid() && T.isValid() && "Invalid type in comparison");
|
||||
// Default order for maps.
|
||||
return Map < T.Map;
|
||||
}
|
||||
|
||||
MVT &ValueTypeByHwMode::getOrCreateTypeForMode(unsigned Mode, MVT Type) {
|
||||
auto F = Map.find(Mode);
|
||||
if (F != Map.end())
|
||||
return F->second;
|
||||
// If Mode is not in the map, look up the default mode. If it exists,
|
||||
// make a copy of it for Mode and return it.
|
||||
auto D = Map.find(DefaultMode);
|
||||
if (D != Map.end())
|
||||
return Map.insert(std::make_pair(Mode, D->second)).first->second;
|
||||
// If default mode is not present either, use provided Type.
|
||||
return Map.insert(std::make_pair(Mode, Type)).first->second;
|
||||
}
|
||||
|
||||
std::string ValueTypeByHwMode::getMVTName(MVT T) {
|
||||
std::string N = llvm::getEnumName(T.SimpleTy);
|
||||
if (N.substr(0,5) == "MVT::")
|
||||
N = N.substr(5);
|
||||
return N;
|
||||
}
|
||||
|
||||
std::string ValueTypeByHwMode::getAsString() const {
|
||||
if (isSimple())
|
||||
return getMVTName(getSimple());
|
||||
|
||||
std::vector<const PairType*> Pairs;
|
||||
for (const auto &P : Map)
|
||||
Pairs.push_back(&P);
|
||||
std::sort(Pairs.begin(), Pairs.end(), deref<std::less<PairType>>());
|
||||
|
||||
std::stringstream str;
|
||||
str << '{';
|
||||
for (unsigned i = 0, e = Pairs.size(); i != e; ++i) {
|
||||
const PairType *P = Pairs[i];
|
||||
str << '(' << getModeName(P->first)
|
||||
<< ':' << getMVTName(P->second) << ')';
|
||||
if (i != e-1)
|
||||
str << ',';
|
||||
}
|
||||
str << '}';
|
||||
return str.str();
|
||||
}
|
||||
|
||||
LLVM_DUMP_METHOD
|
||||
void ValueTypeByHwMode::dump() const {
|
||||
dbgs() << "size=" << Map.size() << '\n';
|
||||
for (const auto &P : Map)
|
||||
dbgs() << " " << P.first << " -> "
|
||||
<< llvm::getEnumName(P.second.SimpleTy) << '\n';
|
||||
}
|
||||
|
||||
ValueTypeByHwMode llvm::getValueTypeByHwMode(Record *Rec,
|
||||
const CodeGenHwModes &CGH) {
|
||||
#ifndef NDEBUG
|
||||
if (!Rec->isSubClassOf("ValueType"))
|
||||
Rec->dump();
|
||||
#endif
|
||||
assert(Rec->isSubClassOf("ValueType") &&
|
||||
"Record must be derived from ValueType");
|
||||
if (Rec->isSubClassOf("HwModeSelect"))
|
||||
return ValueTypeByHwMode(Rec, CGH);
|
||||
return ValueTypeByHwMode(llvm::getValueType(Rec));
|
||||
}
|
||||
|
||||
RegSizeInfo::RegSizeInfo(Record *R, const CodeGenHwModes &CGH) {
|
||||
RegSize = R->getValueAsInt("RegSize");
|
||||
SpillSize = R->getValueAsInt("SpillSize");
|
||||
SpillAlignment = R->getValueAsInt("SpillAlignment");
|
||||
}
|
||||
|
||||
bool RegSizeInfo::operator< (const RegSizeInfo &I) const {
|
||||
return std::tie(RegSize, SpillSize, SpillAlignment) <
|
||||
std::tie(I.RegSize, I.SpillSize, I.SpillAlignment);
|
||||
}
|
||||
|
||||
bool RegSizeInfo::isSubClassOf(const RegSizeInfo &I) const {
|
||||
return RegSize <= I.RegSize &&
|
||||
SpillAlignment && I.SpillAlignment % SpillAlignment == 0 &&
|
||||
SpillSize <= I.SpillSize;
|
||||
}
|
||||
|
||||
std::string RegSizeInfo::getAsString() const {
|
||||
std::stringstream str;
|
||||
str << "[R=" << RegSize << ",S=" << SpillSize
|
||||
<< ",A=" << SpillAlignment << ']';
|
||||
return str.str();
|
||||
}
|
||||
|
||||
RegSizeInfoByHwMode::RegSizeInfoByHwMode(Record *R,
|
||||
const CodeGenHwModes &CGH) {
|
||||
const HwModeSelect &MS = CGH.getHwModeSelect(R);
|
||||
for (const HwModeSelect::PairType &P : MS.Items) {
|
||||
auto I = Map.insert({P.first, RegSizeInfo(P.second, CGH)});
|
||||
assert(I.second && "Duplicate entry?");
|
||||
(void)I;
|
||||
}
|
||||
}
|
||||
|
||||
bool RegSizeInfoByHwMode::operator< (const RegSizeInfoByHwMode &I) const {
|
||||
unsigned M0 = Map.begin()->first;
|
||||
return get(M0) < I.get(M0);
|
||||
}
|
||||
|
||||
bool RegSizeInfoByHwMode::operator== (const RegSizeInfoByHwMode &I) const {
|
||||
unsigned M0 = Map.begin()->first;
|
||||
return get(M0) == I.get(M0);
|
||||
}
|
||||
|
||||
bool RegSizeInfoByHwMode::isSubClassOf(const RegSizeInfoByHwMode &I) const {
|
||||
unsigned M0 = Map.begin()->first;
|
||||
return get(M0).isSubClassOf(I.get(M0));
|
||||
}
|
||||
|
||||
bool RegSizeInfoByHwMode::hasStricterSpillThan(const RegSizeInfoByHwMode &I)
|
||||
const {
|
||||
unsigned M0 = Map.begin()->first;
|
||||
const RegSizeInfo &A0 = get(M0);
|
||||
const RegSizeInfo &B0 = I.get(M0);
|
||||
return std::tie(A0.SpillSize, A0.SpillAlignment) >
|
||||
std::tie(B0.SpillSize, B0.SpillAlignment);
|
||||
}
|
||||
|
||||
std::string RegSizeInfoByHwMode::getAsString() const {
|
||||
typedef typename decltype(Map)::value_type PairType;
|
||||
std::vector<const PairType*> Pairs;
|
||||
for (const auto &P : Map)
|
||||
Pairs.push_back(&P);
|
||||
std::sort(Pairs.begin(), Pairs.end(), deref<std::less<PairType>>());
|
||||
|
||||
std::stringstream str;
|
||||
str << '{';
|
||||
for (unsigned i = 0, e = Pairs.size(); i != e; ++i) {
|
||||
const PairType *P = Pairs[i];
|
||||
str << '(' << getModeName(P->first)
|
||||
<< ':' << P->second.getAsString() << ')';
|
||||
if (i != e-1)
|
||||
str << ',';
|
||||
}
|
||||
str << '}';
|
||||
return str.str();
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
//===--- InfoByHwMode.h -----------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Classes that implement data parameterized by HW modes for instruction
|
||||
// selection. Currently it is ValueTypeByHwMode (parameterized ValueType),
|
||||
// and RegSizeInfoByHwMode (parameterized register/spill size and alignment
|
||||
// data).
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_UTILS_TABLEGEN_INFOBYHWMODE_H
|
||||
#define LLVM_UTILS_TABLEGEN_INFOBYHWMODE_H
|
||||
|
||||
#include "CodeGenHwModes.h"
|
||||
#include "llvm/CodeGen/MachineValueType.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
struct CodeGenHwModes;
|
||||
class Record;
|
||||
|
||||
template <typename InfoT> struct InfoByHwMode;
|
||||
|
||||
std::string getModeName(unsigned Mode);
|
||||
|
||||
enum : unsigned {
|
||||
DefaultMode = CodeGenHwModes::DefaultMode,
|
||||
};
|
||||
|
||||
template <typename InfoT>
|
||||
std::vector<unsigned> union_modes(const InfoByHwMode<InfoT> &A,
|
||||
const InfoByHwMode<InfoT> &B) {
|
||||
std::vector<unsigned> V;
|
||||
std::set<unsigned> U;
|
||||
for (const auto &P : A)
|
||||
U.insert(P.first);
|
||||
for (const auto &P : B)
|
||||
U.insert(P.first);
|
||||
// Make sure that the default mode is last on the list.
|
||||
bool HasDefault = U.count(DefaultMode);
|
||||
for (unsigned M : U)
|
||||
if (M != DefaultMode)
|
||||
V.push_back(M);
|
||||
if (HasDefault)
|
||||
V.push_back(DefaultMode);
|
||||
return V;
|
||||
}
|
||||
|
||||
template <typename InfoT>
|
||||
struct InfoByHwMode {
|
||||
typedef std::map<unsigned,InfoT> MapType;
|
||||
typedef typename MapType::value_type PairType;
|
||||
typedef typename MapType::iterator iterator;
|
||||
typedef typename MapType::const_iterator const_iterator;
|
||||
|
||||
InfoByHwMode() = default;
|
||||
InfoByHwMode(const MapType &&M) : Map(M) {}
|
||||
|
||||
iterator begin() { return Map.begin(); }
|
||||
iterator end() { return Map.end(); }
|
||||
const_iterator begin() const { return Map.begin(); }
|
||||
const_iterator end() const { return Map.end(); }
|
||||
bool empty() const { return Map.empty(); }
|
||||
|
||||
bool hasMode(unsigned M) const { return Map.find(M) != Map.end(); }
|
||||
bool hasDefault() const { return hasMode(DefaultMode); }
|
||||
|
||||
InfoT &get(unsigned Mode) {
|
||||
if (!hasMode(Mode)) {
|
||||
assert(hasMode(DefaultMode));
|
||||
Map[Mode] = Map[DefaultMode];
|
||||
}
|
||||
return Map[Mode];
|
||||
}
|
||||
const InfoT &get(unsigned Mode) const {
|
||||
auto F = Map.find(Mode);
|
||||
if (Mode != DefaultMode && F == Map.end())
|
||||
F = Map.find(DefaultMode);
|
||||
assert(F != Map.end());
|
||||
return F->second;
|
||||
}
|
||||
|
||||
bool isSimple() const {
|
||||
return Map.size() == 1 && Map.begin()->first == DefaultMode;
|
||||
}
|
||||
InfoT getSimple() const {
|
||||
assert(isSimple());
|
||||
return Map.begin()->second;
|
||||
}
|
||||
void makeSimple(unsigned Mode) {
|
||||
assert(hasMode(Mode) || hasDefault());
|
||||
InfoT I = get(Mode);
|
||||
Map.clear();
|
||||
Map.insert(std::make_pair(DefaultMode, I));
|
||||
}
|
||||
|
||||
MapType Map;
|
||||
};
|
||||
|
||||
struct ValueTypeByHwMode : public InfoByHwMode<MVT> {
|
||||
ValueTypeByHwMode(Record *R, const CodeGenHwModes &CGH);
|
||||
ValueTypeByHwMode(MVT T) { Map.insert({DefaultMode,T}); }
|
||||
ValueTypeByHwMode() = default;
|
||||
|
||||
bool operator== (const ValueTypeByHwMode &T) const;
|
||||
bool operator< (const ValueTypeByHwMode &T) const;
|
||||
|
||||
bool isValid() const {
|
||||
return !Map.empty();
|
||||
}
|
||||
MVT getType(unsigned Mode) const { return get(Mode); }
|
||||
MVT &getOrCreateTypeForMode(unsigned Mode, MVT Type);
|
||||
|
||||
static std::string getMVTName(MVT T);
|
||||
std::string getAsString() const;
|
||||
void dump() const;
|
||||
};
|
||||
|
||||
ValueTypeByHwMode getValueTypeByHwMode(Record *Rec,
|
||||
const CodeGenHwModes &CGH);
|
||||
|
||||
struct RegSizeInfo {
|
||||
unsigned RegSize;
|
||||
unsigned SpillSize;
|
||||
unsigned SpillAlignment;
|
||||
|
||||
RegSizeInfo(Record *R, const CodeGenHwModes &CGH);
|
||||
RegSizeInfo() = default;
|
||||
bool operator< (const RegSizeInfo &I) const;
|
||||
bool operator== (const RegSizeInfo &I) const {
|
||||
return std::tie(RegSize, SpillSize, SpillAlignment) ==
|
||||
std::tie(I.RegSize, I.SpillSize, I.SpillAlignment);
|
||||
}
|
||||
bool operator!= (const RegSizeInfo &I) const {
|
||||
return !(*this == I);
|
||||
}
|
||||
|
||||
bool isSubClassOf(const RegSizeInfo &I) const;
|
||||
std::string getAsString() const;
|
||||
};
|
||||
|
||||
struct RegSizeInfoByHwMode : public InfoByHwMode<RegSizeInfo> {
|
||||
RegSizeInfoByHwMode(Record *R, const CodeGenHwModes &CGH);
|
||||
RegSizeInfoByHwMode() = default;
|
||||
bool operator< (const RegSizeInfoByHwMode &VI) const;
|
||||
bool operator== (const RegSizeInfoByHwMode &VI) const;
|
||||
bool operator!= (const RegSizeInfoByHwMode &VI) const {
|
||||
return !(*this == VI);
|
||||
}
|
||||
|
||||
bool isSubClassOf(const RegSizeInfoByHwMode &I) const;
|
||||
bool hasStricterSpillThan(const RegSizeInfoByHwMode &I) const;
|
||||
|
||||
std::string getAsString() const;
|
||||
};
|
||||
} // namespace llvm
|
||||
|
||||
#endif // LLVM_UTILS_TABLEGEN_INFOBYHWMODE_H
|
|
@ -18,6 +18,7 @@
|
|||
#include "llvm/TableGen/Record.h"
|
||||
#include "llvm/TableGen/TableGenBackend.h"
|
||||
|
||||
#include "CodeGenHwModes.h"
|
||||
#include "CodeGenRegisters.h"
|
||||
|
||||
#define DEBUG_TYPE "register-bank-emitter"
|
||||
|
@ -84,7 +85,8 @@ public:
|
|||
// the VT's reliably due to Untyped.
|
||||
if (RCWithLargestRegsSize == nullptr)
|
||||
RCWithLargestRegsSize = RC;
|
||||
else if (RCWithLargestRegsSize->SpillSize < RC->SpillSize)
|
||||
else if (RCWithLargestRegsSize->RSI.get(DefaultMode).SpillSize <
|
||||
RC->RSI.get(DefaultMode).SpillSize)
|
||||
RCWithLargestRegsSize = RC;
|
||||
assert(RCWithLargestRegsSize && "RC was nullptr?");
|
||||
|
||||
|
@ -115,7 +117,7 @@ private:
|
|||
|
||||
public:
|
||||
RegisterBankEmitter(RecordKeeper &R)
|
||||
: Records(R), RegisterClassHierarchy(Records) {}
|
||||
: Records(R), RegisterClassHierarchy(Records, CodeGenHwModes(R)) {}
|
||||
|
||||
void run(raw_ostream &OS);
|
||||
};
|
||||
|
@ -241,7 +243,8 @@ void RegisterBankEmitter::emitBaseClassImplementation(
|
|||
for (const auto &Bank : Banks) {
|
||||
std::string QualifiedBankID =
|
||||
(TargetName + "::" + Bank.getEnumeratorName()).str();
|
||||
unsigned Size = Bank.getRCWithLargestRegsSize()->SpillSize;
|
||||
const CodeGenRegisterClass &RC = *Bank.getRCWithLargestRegsSize();
|
||||
unsigned Size = RC.RSI.get(DefaultMode).SpillSize;
|
||||
OS << "RegisterBank " << Bank.getInstanceVarName() << "(/* ID */ "
|
||||
<< QualifiedBankID << ", /* Name */ \"" << Bank.getName()
|
||||
<< "\", /* Size */ " << Size << ", "
|
||||
|
|
|
@ -1037,13 +1037,14 @@ RegisterInfoEmitter::runMCDesc(raw_ostream &OS, CodeGenTarget &Target,
|
|||
|
||||
for (const auto &RC : RegisterClasses) {
|
||||
assert(isInt<8>(RC.CopyCost) && "Copy cost too large.");
|
||||
// Register size and spill size will become independent, but are not at
|
||||
// the moment. For now use SpillSize as the register size.
|
||||
uint32_t RegSize = 0;
|
||||
if (RC.RSI.isSimple())
|
||||
RegSize = RC.RSI.getSimple().RegSize;
|
||||
OS << " { " << RC.getName() << ", " << RC.getName() << "Bits, "
|
||||
<< RegClassStrings.get(RC.getName()) << ", "
|
||||
<< RC.getOrder().size() << ", sizeof(" << RC.getName() << "Bits), "
|
||||
<< RC.getQualifiedName() + "RegClassID" << ", "
|
||||
<< RC.SpillSize/8 << ", "
|
||||
<< RegSize/8 << ", "
|
||||
<< RC.CopyCost << ", "
|
||||
<< ( RC.Allocatable ? "true" : "false" ) << " },\n";
|
||||
}
|
||||
|
@ -1111,7 +1112,8 @@ RegisterInfoEmitter::runTargetHeader(raw_ostream &OS, CodeGenTarget &Target,
|
|||
|
||||
OS << "struct " << ClassName << " : public TargetRegisterInfo {\n"
|
||||
<< " explicit " << ClassName
|
||||
<< "(unsigned RA, unsigned D = 0, unsigned E = 0, unsigned PC = 0);\n";
|
||||
<< "(unsigned RA, unsigned D = 0, unsigned E = 0,\n"
|
||||
<< " unsigned PC = 0, unsigned HwMode = 0);\n";
|
||||
if (!RegBank.getSubRegIndices().empty()) {
|
||||
OS << " unsigned composeSubRegIndicesImpl"
|
||||
<< "(unsigned, unsigned) const override;\n"
|
||||
|
@ -1190,10 +1192,19 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target,
|
|||
AllocatableRegs.insert(Order.begin(), Order.end());
|
||||
}
|
||||
|
||||
const CodeGenHwModes &CGH = Target.getHwModes();
|
||||
unsigned NumModes = CGH.getNumModeIds();
|
||||
|
||||
// Build a shared array of value types.
|
||||
SequenceToOffsetTable<SmallVector<MVT::SimpleValueType, 4> > VTSeqs;
|
||||
for (const auto &RC : RegisterClasses)
|
||||
VTSeqs.add(RC.VTs);
|
||||
SequenceToOffsetTable<std::vector<MVT::SimpleValueType>> VTSeqs;
|
||||
for (unsigned M = 0; M < NumModes; ++M) {
|
||||
for (const auto &RC : RegisterClasses) {
|
||||
std::vector<MVT::SimpleValueType> S;
|
||||
for (const ValueTypeByHwMode &VVT : RC.VTs)
|
||||
S.push_back(VVT.get(M).SimpleTy);
|
||||
VTSeqs.add(S);
|
||||
}
|
||||
}
|
||||
VTSeqs.layout();
|
||||
OS << "\nstatic const MVT::SimpleValueType VTLists[] = {\n";
|
||||
VTSeqs.emit(OS, printSimpleValueType, "MVT::Other");
|
||||
|
@ -1221,6 +1232,31 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target,
|
|||
|
||||
// Now that all of the structs have been emitted, emit the instances.
|
||||
if (!RegisterClasses.empty()) {
|
||||
OS << "\nstatic const TargetRegisterInfo::RegClassInfo RegClassInfos[]"
|
||||
<< " = {\n";
|
||||
for (unsigned M = 0; M < NumModes; ++M) {
|
||||
unsigned EV = 0;
|
||||
OS << " // Mode = " << M << " (";
|
||||
if (M == 0)
|
||||
OS << "Default";
|
||||
else
|
||||
OS << CGH.getMode(M).Name;
|
||||
OS << ")\n";
|
||||
for (const auto &RC : RegisterClasses) {
|
||||
assert(RC.EnumValue == EV++ && "Unexpected order of register classes");
|
||||
const RegSizeInfo &RI = RC.RSI.get(M);
|
||||
OS << " { " << RI.RegSize << ", " << RI.SpillSize << ", "
|
||||
<< RI.SpillAlignment;
|
||||
std::vector<MVT::SimpleValueType> VTs;
|
||||
for (const ValueTypeByHwMode &VVT : RC.VTs)
|
||||
VTs.push_back(VVT.get(M).SimpleTy);
|
||||
OS << ", VTLists+" << VTSeqs.get(VTs) << " }, // "
|
||||
<< RC.getName() << '\n';
|
||||
}
|
||||
}
|
||||
OS << "};\n";
|
||||
|
||||
|
||||
OS << "\nstatic const TargetRegisterClass *const "
|
||||
<< "NullRegClasses[] = { nullptr };\n\n";
|
||||
|
||||
|
@ -1327,15 +1363,10 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target,
|
|||
<< " { // Register class instances\n";
|
||||
|
||||
for (const auto &RC : RegisterClasses) {
|
||||
assert(isUInt<16>(RC.SpillSize/8) && "SpillSize too large.");
|
||||
assert(isUInt<16>(RC.SpillAlignment/8) && "SpillAlignment too large.");
|
||||
OS << " extern const TargetRegisterClass " << RC.getName()
|
||||
<< "RegClass = {\n " << '&' << Target.getName()
|
||||
<< "MCRegisterClasses[" << RC.getName() << "RegClassID],\n "
|
||||
<< RC.SpillSize/8 << ", /* SpillSize */\n "
|
||||
<< RC.SpillAlignment/8 << ", /* SpillAlignment */\n "
|
||||
<< "VTLists + " << VTSeqs.get(RC.VTs) << ",\n " << RC.getName()
|
||||
<< "SubClassMask,\n SuperRegIdxSeqs + "
|
||||
<< RC.getName() << "SubClassMask,\n SuperRegIdxSeqs + "
|
||||
<< SuperRegIdxSeqs.get(SuperRegIdxLists[RC.EnumValue]) << ",\n ";
|
||||
printMask(OS, RC.LaneMask);
|
||||
OS << ",\n " << (unsigned)RC.AllocationPriority << ",\n "
|
||||
|
@ -1439,12 +1470,14 @@ RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target,
|
|||
EmitRegMappingTables(OS, Regs, true);
|
||||
|
||||
OS << ClassName << "::\n" << ClassName
|
||||
<< "(unsigned RA, unsigned DwarfFlavour, unsigned EHFlavour, unsigned PC)\n"
|
||||
<< "(unsigned RA, unsigned DwarfFlavour, unsigned EHFlavour,\n"
|
||||
" unsigned PC, unsigned HwMode)\n"
|
||||
<< " : TargetRegisterInfo(" << TargetName << "RegInfoDesc"
|
||||
<< ", RegisterClasses, RegisterClasses+" << RegisterClasses.size() <<",\n"
|
||||
<< " SubRegIndexNameTable, SubRegIndexLaneMaskTable, ";
|
||||
<< ", RegisterClasses, RegisterClasses+" << RegisterClasses.size() << ",\n"
|
||||
<< " SubRegIndexNameTable, SubRegIndexLaneMaskTable,\n"
|
||||
<< " ";
|
||||
printMask(OS, RegBank.CoveringLanes);
|
||||
OS << ") {\n"
|
||||
OS << ", RegClassInfos, HwMode) {\n"
|
||||
<< " InitMCRegisterInfo(" << TargetName << "RegDesc, " << Regs.size() + 1
|
||||
<< ", RA, PC,\n " << TargetName
|
||||
<< "MCRegisterClasses, " << RegisterClasses.size() << ",\n"
|
||||
|
@ -1547,12 +1580,23 @@ void RegisterInfoEmitter::run(raw_ostream &OS) {
|
|||
|
||||
void RegisterInfoEmitter::debugDump(raw_ostream &OS) {
|
||||
CodeGenRegBank &RegBank = Target.getRegBank();
|
||||
const CodeGenHwModes &CGH = Target.getHwModes();
|
||||
unsigned NumModes = CGH.getNumModeIds();
|
||||
auto getModeName = [CGH] (unsigned M) -> StringRef {
|
||||
if (M == 0)
|
||||
return "Default";
|
||||
return CGH.getMode(M).Name;
|
||||
};
|
||||
|
||||
for (const CodeGenRegisterClass &RC : RegBank.getRegClasses()) {
|
||||
OS << "RegisterClass " << RC.getName() << ":\n";
|
||||
OS << "\tSpillSize: " << RC.SpillSize << '\n';
|
||||
OS << "\tSpillAlignment: " << RC.SpillAlignment << '\n';
|
||||
OS << "\tNumRegs: " << RC.getMembers().size() << '\n';
|
||||
OS << "\tSpillSize: {";
|
||||
for (unsigned M = 0; M != NumModes; ++M)
|
||||
OS << ' ' << getModeName(M) << ':' << RC.RSI.get(M).SpillSize;
|
||||
OS << " }\n\tSpillAlignment: {";
|
||||
for (unsigned M = 0; M != NumModes; ++M)
|
||||
OS << ' ' << getModeName(M) << ':' << RC.RSI.get(M).SpillAlignment;
|
||||
OS << " }\n\tNumRegs: " << RC.getMembers().size() << '\n';
|
||||
OS << "\tLaneMask: " << PrintLaneMask(RC.LaneMask) << '\n';
|
||||
OS << "\tHasDisjunctSubRegs: " << RC.HasDisjunctSubRegs << '\n';
|
||||
OS << "\tCoveredBySubRegs: " << RC.CoveredBySubRegs << '\n';
|
||||
|
|
Loading…
Reference in New Issue