1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 11:02:59 +02:00

[globalisel][tablegen] Implement unindexed load, non-extending load, and MemVT checks

Summary:
This includes some context-sensitivity in the MVT to LLT conversion so that
pointer types are tested correctly.
FIXME: I'm not happy with the way this is done since everything is a
       special-case. I've yet to find a reasonable way to implement it.

select-load.mir fails because <1 x s64> loads in tablegen get priority over s64
loads. This is fixed in the next patch and as such they should be committed
together, I've posted them separately to help with the review.

Depends on D37456

Reviewers: ab, qcolombet, t.p.northover, rovka, aditya_nandakumar

Subscribers: kristof.beyls, javed.absar, llvm-commits, igorb

Differential Revision: https://reviews.llvm.org/D37457

llvm-svn: 315884
This commit is contained in:
Daniel Sanders 2017-10-16 00:56:30 +00:00
parent 1b4ad9961f
commit 74e6f55971
5 changed files with 177 additions and 20 deletions

View File

@ -117,6 +117,11 @@ enum {
/// - OpIdx - Operand index
/// - Expected type
GIM_CheckType,
/// Check the type of a pointer to any address space.
/// - InsnID - Instruction ID
/// - OpIdx - Operand index
/// - SizeInBits - The size of the pointer value in bits.
GIM_CheckPointerToAny,
/// Check the register bank for the specified operand
/// - InsnID - Instruction ID
/// - OpIdx - Operand index

View File

@ -244,7 +244,21 @@ bool InstructionSelector::executeMatchTable(
}
break;
}
case GIM_CheckPointerToAny: {
int64_t InsnID = MatchTable[CurrentIdx++];
int64_t OpIdx = MatchTable[CurrentIdx++];
int64_t SizeInBits = MatchTable[CurrentIdx++];
DEBUG(dbgs() << CurrentIdx << ": GIM_CheckPointerToAny(MIs[" << InsnID
<< "]->getOperand(" << OpIdx
<< "), SizeInBits=" << SizeInBits << ")\n");
assert(State.MIs[InsnID] != nullptr && "Used insn before defined");
const LLT &Ty = MRI.getType(State.MIs[InsnID]->getOperand(OpIdx).getReg());
if (!Ty.isPointer() || Ty.getSizeInBits() != SizeInBits) {
if (handleReject() == RejectAndGiveUp)
return false;
}
break;
}
case GIM_CheckRegBankForClass: {
int64_t InsnID = MatchTable[CurrentIdx++];
int64_t OpIdx = MatchTable[CurrentIdx++];

View File

@ -1,5 +1,9 @@
# RUN: llc -mtriple=aarch64-- -run-pass=instruction-select -verify-machineinstrs -global-isel %s -o - | FileCheck %s
# This patch temporarily causes LD1Onev1d to match instead of LDRDui on a
# couple functions. A patch to support iPTR will follow that fixes this.
# XFAIL: *
--- |
target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
@ -28,6 +32,7 @@
define void @load_gep_64_s16_fpr(i16* %addr) { ret void }
define void @load_gep_32_s8_fpr(i8* %addr) { ret void }
define void @load_v2s32(i64 *%addr) { ret void }
...
---
@ -513,3 +518,28 @@ body: |
%3(s8) = G_LOAD %2 :: (load 1 from %ir.addr)
%b0 = COPY %3
...
---
# CHECK-LABEL: name: load_v2s32
name: load_v2s32
legalized: true
regBankSelected: true
# CHECK: registers:
# CHECK-NEXT: - { id: 0, class: gpr64sp, preferred-register: '' }
# CHECK-NEXT: - { id: 1, class: fpr64, preferred-register: '' }
registers:
- { id: 0, class: gpr }
- { id: 1, class: fpr }
# CHECK: body:
# CHECK: %0 = COPY %x0
# CHECK: %1 = LD1Onev2s %0
# CHECK: %d0 = COPY %1
body: |
bb.0:
liveins: %x0
%0(p0) = COPY %x0
%1(<2 x s32>) = G_LOAD %0 :: (load 4 from %ir.addr)
%d0 = COPY %1(<2 x s32>)
...

View File

@ -814,9 +814,29 @@ def MOVimm : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, imm:$imm)
def fpimmz : FPImmLeaf<f32, [{ return Imm->isExactlyValue(0.0); }]>;
def MOVfpimmz : I<(outs FPR32:$dst), (ins f32imm:$imm), [(set FPR32:$dst, fpimmz:$imm)]>;
//===- Test a pattern with an MBB operand. --------------------------------===//
//===- Test a simple pattern with inferred pointer operands. ---------------===//
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 22*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/2,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_LOAD,
// CHECK-NEXT: GIM_CheckNonAtomic, /*MI*/0,
// CHECK-NEXT: // MIs[0] dst
// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/0, /*Type*/GILLT_s32,
// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID,
// CHECK-NEXT: // MIs[0] src1
// CHECK-NEXT: GIM_CheckPointerToAny, /*MI*/0, /*Op*/1, /*SizeInBits*/32,
// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/1, /*RC*/MyTarget::GPR32RegClassID,
// CHECK-NEXT: // (ld:{ *:[i32] } GPR32:{ *:[i32] }:$src1)<<P:Predicate_unindexedload>><<P:Predicate_load>> => (LOAD:{ *:[i32] } GPR32:{ *:[i32] }:$src1)
// CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/MyTarget::LOAD,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 22: @[[LABEL]]
def LOAD : I<(outs GPR32:$dst), (ins GPR32:$src1),
[(set GPR32:$dst, (load GPR32:$src1))]>;
//===- Test a pattern with an MBB operand. --------------------------------===//
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 23*/ [[LABEL:[0-9]+]],
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/1,
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_BR,
// CHECK-NEXT: // MIs[0] target
@ -825,7 +845,7 @@ def MOVfpimmz : I<(outs FPR32:$dst), (ins f32imm:$imm), [(set FPR32:$dst, fpimmz
// CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/MyTarget::BR,
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
// CHECK-NEXT: GIR_Done,
// CHECK-NEXT: // Label 22: @[[LABEL]]
// CHECK-NEXT: // Label 23: @[[LABEL]]
def BR : I<(outs), (ins unknown:$target),
[(br bb:$target)]>;

View File

@ -103,6 +103,12 @@ public:
OS << "GILLT_v" << Ty.getNumElements() << "s" << Ty.getScalarSizeInBits();
return;
}
if (Ty.isPointer()) {
OS << "GILLT_p" << Ty.getAddressSpace();
if (Ty.getSizeInBits() > 0)
OS << "s" << Ty.getSizeInBits();
return;
}
llvm_unreachable("Unhandled LLT");
}
@ -116,6 +122,11 @@ public:
<< Ty.getScalarSizeInBits() << ")";
return;
}
if (Ty.isPointer() && Ty.getSizeInBits() > 0) {
OS << "LLT::pointer(" << Ty.getAddressSpace() << ", "
<< Ty.getSizeInBits() << ")";
return;
}
llvm_unreachable("Unhandled LLT");
}
@ -152,9 +163,11 @@ class InstructionMatcher;
/// MVTs that don't map cleanly to an LLT (e.g., iPTR, *any, ...).
static Optional<LLTCodeGen> MVTToLLT(MVT::SimpleValueType SVT) {
MVT VT(SVT);
if (VT.isVector() && VT.getVectorNumElements() != 1)
return LLTCodeGen(
LLT::vector(VT.getVectorNumElements(), VT.getScalarSizeInBits()));
if (VT.isInteger() || VT.isFloatingPoint())
return LLTCodeGen(LLT::scalar(VT.getSizeInBits()));
return None;
@ -228,6 +241,11 @@ static Error isTrivialOperatorNode(const TreePatternNode *N) {
if (Predicate.isImmediatePattern())
continue;
if (Predicate.isLoad() && Predicate.isUnindexed())
continue;
if (Predicate.isNonExtLoad())
continue;
HasUnsupportedPredicate = true;
Explanation = Separator + "Has a predicate (" + explainPredicates(N) + ")";
Separator = ", ";
@ -661,6 +679,7 @@ public:
OPM_Int,
OPM_LiteralInt,
OPM_LLT,
OPM_PointerToAny,
OPM_RegBank,
OPM_MBB,
};
@ -748,6 +767,37 @@ public:
std::set<LLTCodeGen> LLTOperandMatcher::KnownTypes;
/// Generates code to check that an operand is a pointer to any address space.
///
/// In SelectionDAG, the types did not describe pointers or address spaces. As a
/// result, iN is used to describe a pointer of N bits to any address space and
/// PatFrag predicates are typically used to constrain the address space. There's
/// no reliable means to derive the missing type information from the pattern so
/// imported rules must test the components of a pointer separately.
///
/// SizeInBits must be non-zero and the matched pointer must be that size.
/// TODO: Add support for iPTR via SizeInBits==0 and a subtarget query.
class PointerToAnyOperandMatcher : public OperandPredicateMatcher {
protected:
unsigned SizeInBits;
public:
PointerToAnyOperandMatcher(unsigned SizeInBits)
: OperandPredicateMatcher(OPM_PointerToAny), SizeInBits(SizeInBits) {}
static bool classof(const OperandPredicateMatcher *P) {
return P->getKind() == OPM_PointerToAny;
}
void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule,
unsigned InsnVarID, unsigned OpIdx) const override {
Table << MatchTable::Opcode("GIM_CheckPointerToAny") << MatchTable::Comment("MI")
<< MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Op")
<< MatchTable::IntValue(OpIdx) << MatchTable::Comment("SizeInBits")
<< MatchTable::IntValue(SizeInBits) << MatchTable::LineBreak;
}
};
/// Generates code to check that an operand is a particular target constant.
class ComplexPatternOperandMatcher : public OperandPredicateMatcher {
protected:
@ -927,6 +977,22 @@ public:
InstructionMatcher &getInstructionMatcher() const { return Insn; }
Error addTypeCheckPredicate(const TypeSetByHwMode &VTy,
bool OperandIsAPointer) {
auto OpTyOrNone = VTy.isMachineValueType()
? MVTToLLT(VTy.getMachineValueType().SimpleTy)
: None;
if (!OpTyOrNone)
return failedImport("unsupported type");
if (OperandIsAPointer)
addPredicate<PointerToAnyOperandMatcher>(
OpTyOrNone->get().getSizeInBits());
else
addPredicate<LLTOperandMatcher>(*OpTyOrNone);
return Error::success();
}
/// Emit MatchTable opcodes to capture instructions into the MIs table.
void emitCaptureOpcodes(MatchTable &Table, RuleMatcher &Rule,
unsigned InsnVarID) const {
@ -2070,7 +2136,8 @@ private:
Error importComplexPatternOperandMatcher(OperandMatcher &OM, Record *R,
unsigned &TempOpIdx) const;
Error importChildMatcher(RuleMatcher &Rule, InstructionMatcher &InsnMatcher,
const TreePatternNode *SrcChild, unsigned OpIdx,
const TreePatternNode *SrcChild,
bool OperandIsAPointer, unsigned OpIdx,
unsigned &TempOpIdx) const;
Expected<BuildMIAction &>
createAndImportInstructionRenderer(RuleMatcher &M, const TreePatternNode *Dst,
@ -2164,17 +2231,12 @@ Expected<InstructionMatcher &> GlobalISelEmitter::createAndImportSelDAGMatcher(
unsigned OpIdx = 0;
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");
// Results don't have a name unless they are the root node. The caller will
// set the name if appropriate.
OperandMatcher &OM = InsnMatcher.addOperand(OpIdx++, "", TempOpIdx);
OM.addPredicate<LLTOperandMatcher>(*OpTyOrNone);
if (auto Error = OM.addTypeCheckPredicate(VTy, false /* OperandIsAPointer */))
return failedImport(toString(std::move(Error)) +
" for result of Src pattern operator");
}
for (const auto &Predicate : Src->getPredicateFns()) {
@ -2186,6 +2248,25 @@ Expected<InstructionMatcher &> GlobalISelEmitter::createAndImportSelDAGMatcher(
continue;
}
// No check required. A G_LOAD is an unindexed load.
if (Predicate.isLoad() && Predicate.isUnindexed())
continue;
// No check required. G_LOAD by itself is a non-extending load.
if (Predicate.isNonExtLoad())
continue;
if (Predicate.isLoad() && Predicate.getMemoryVT() != nullptr) {
Optional<LLTCodeGen> MemTyOrNone =
MVTToLLT(getValueType(Predicate.getMemoryVT()));
if (!MemTyOrNone)
return failedImport("MemVT could not be converted to LLT");
InsnMatcher.getOperand(0).addPredicate<LLTOperandMatcher>(MemTyOrNone.getValue());
continue;
}
return failedImport("Src pattern child has predicate (" +
explainPredicates(Src) + ")");
}
@ -2217,6 +2298,13 @@ Expected<InstructionMatcher &> GlobalISelEmitter::createAndImportSelDAGMatcher(
for (unsigned i = 0, e = Src->getNumChildren(); i != e; ++i) {
TreePatternNode *SrcChild = Src->getChild(i);
// SelectionDAG allows pointers to be represented with iN since it doesn't
// distinguish between pointers and integers but they are different types in GlobalISel.
// Coerce integers to pointers to address space 0 if the context indicates a pointer.
// TODO: Find a better way to do this, SDTCisPtrTy?
bool OperandIsAPointer =
SrcGIOrNull->TheDef->getName() == "G_LOAD" && i == 0;
// For G_INTRINSIC/G_INTRINSIC_W_SIDE_EFFECTS, the operand immediately
// following the defs is an intrinsic ID.
if ((SrcGIOrNull->TheDef->getName() == "G_INTRINSIC" ||
@ -2232,8 +2320,9 @@ Expected<InstructionMatcher &> GlobalISelEmitter::createAndImportSelDAGMatcher(
return failedImport("Expected IntInit containing instrinsic ID)");
}
if (auto Error = importChildMatcher(Rule, InsnMatcher, SrcChild, OpIdx++,
TempOpIdx))
if (auto Error =
importChildMatcher(Rule, InsnMatcher, SrcChild, OperandIsAPointer,
OpIdx++, TempOpIdx))
return std::move(Error);
}
}
@ -2256,6 +2345,7 @@ Error GlobalISelEmitter::importComplexPatternOperandMatcher(
Error GlobalISelEmitter::importChildMatcher(RuleMatcher &Rule,
InstructionMatcher &InsnMatcher,
const TreePatternNode *SrcChild,
bool OperandIsAPointer,
unsigned OpIdx,
unsigned &TempOpIdx) const {
OperandMatcher &OM =
@ -2278,12 +2368,10 @@ Error GlobalISelEmitter::importChildMatcher(RuleMatcher &Rule,
}
}
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);
if (auto Error =
OM.addTypeCheckPredicate(ChildTypes.front(), OperandIsAPointer))
return failedImport(toString(std::move(Error)) + " for Src operand (" +
to_string(*SrcChild) + ")");
// Check for nested instructions.
if (!SrcChild->isLeaf()) {