Index: llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelector.h =================================================================== --- llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelector.h +++ llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelector.h @@ -117,6 +117,11 @@ /// - 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 Index: llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h =================================================================== --- llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h +++ llvm/trunk/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h @@ -244,7 +244,21 @@ } 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++]; Index: llvm/trunk/test/CodeGen/AArch64/GlobalISel/select-load.mir =================================================================== --- llvm/trunk/test/CodeGen/AArch64/GlobalISel/select-load.mir +++ llvm/trunk/test/CodeGen/AArch64/GlobalISel/select-load.mir @@ -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 @@ %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>) +... Index: llvm/trunk/test/TableGen/GlobalISelEmitter.td =================================================================== --- llvm/trunk/test/TableGen/GlobalISelEmitter.td +++ llvm/trunk/test/TableGen/GlobalISelEmitter.td @@ -814,9 +814,29 @@ def fpimmz : FPImmLeafisExactlyValue(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)<><> => (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 @@ // 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)]>; Index: llvm/trunk/utils/TableGen/GlobalISelEmitter.cpp =================================================================== --- llvm/trunk/utils/TableGen/GlobalISelEmitter.cpp +++ llvm/trunk/utils/TableGen/GlobalISelEmitter.cpp @@ -103,6 +103,12 @@ 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 @@ << 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 @@ /// MVTs that don't map cleanly to an LLT (e.g., iPTR, *any, ...). static Optional 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 @@ 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 @@ OPM_Int, OPM_LiteralInt, OPM_LLT, + OPM_PointerToAny, OPM_RegBank, OPM_MBB, }; @@ -748,6 +767,37 @@ std::set 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 @@ 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( + OpTyOrNone->get().getSizeInBits()); + else + addPredicate(*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 @@ 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 createAndImportInstructionRenderer(RuleMatcher &M, const TreePatternNode *Dst, @@ -2164,17 +2231,12 @@ 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(*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 @@ 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 MemTyOrNone = + MVTToLLT(getValueType(Predicate.getMemoryVT())); + + if (!MemTyOrNone) + return failedImport("MemVT could not be converted to LLT"); + + InsnMatcher.getOperand(0).addPredicate(MemTyOrNone.getValue()); + continue; + } + return failedImport("Src pattern child has predicate (" + explainPredicates(Src) + ")"); } @@ -2217,6 +2298,13 @@ 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 @@ 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::importChildMatcher(RuleMatcher &Rule, InstructionMatcher &InsnMatcher, const TreePatternNode *SrcChild, + bool OperandIsAPointer, unsigned OpIdx, unsigned &TempOpIdx) const { OperandMatcher &OM = @@ -2278,12 +2368,10 @@ } } - Optional 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(*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()) {