diff options
-rw-r--r-- | llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h | 7 | ||||
-rw-r--r-- | llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h | 17 | ||||
-rw-r--r-- | llvm/include/llvm/Target/Target.td | 6 | ||||
-rw-r--r-- | llvm/lib/Target/AArch64/AArch64InstrFormats.td | 17 | ||||
-rw-r--r-- | llvm/lib/Target/AArch64/AArch64InstrInfo.td | 16 | ||||
-rw-r--r-- | llvm/lib/Target/AArch64/AArch64RegisterInfo.td | 9 | ||||
-rw-r--r-- | llvm/test/CodeGen/AArch64/GlobalISel/select-store.mir | 28 | ||||
-rw-r--r-- | llvm/utils/TableGen/GlobalISelEmitter.cpp | 80 |
8 files changed, 171 insertions, 9 deletions
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h index 0fbd4b62c78..0a3f133d241 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h @@ -187,6 +187,13 @@ enum { /// - OldInsnID - Instruction ID to copy from /// - OpIdx - The operand to copy GIR_Copy, + /// Copy an operand to the specified instruction or add a zero register if the + /// operand is a zero immediate. + /// - NewInsnID - Instruction ID to modify + /// - OldInsnID - Instruction ID to copy from + /// - OpIdx - The operand to copy + /// - ZeroReg - The zero register to use + GIR_CopyOrAddZeroReg, /// Copy an operand to the specified instruction /// - NewInsnID - Instruction ID to modify /// - OldInsnID - Instruction ID to copy from diff --git a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h index 28537c73b9d..29cc9036401 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h @@ -440,6 +440,23 @@ bool InstructionSelector::executeMatchTable( break; } + case GIR_CopyOrAddZeroReg: { + int64_t NewInsnID = MatchTable[CurrentIdx++]; + int64_t OldInsnID = MatchTable[CurrentIdx++]; + int64_t OpIdx = MatchTable[CurrentIdx++]; + int64_t ZeroReg = MatchTable[CurrentIdx++]; + assert(OutMIs[NewInsnID] && "Attempted to add to undefined instruction"); + MachineOperand &MO = State.MIs[OldInsnID]->getOperand(OpIdx); + if (isOperandImmEqual(MO, 0, MRI)) + OutMIs[NewInsnID].addReg(ZeroReg); + else + OutMIs[NewInsnID].add(MO); + DEBUG(dbgs() << CurrentIdx << ": GIR_CopyOrAddZeroReg(OutMIs[" + << NewInsnID << "], MIs[" << OldInsnID << "], " << OpIdx + << ", " << ZeroReg << ")\n"); + break; + } + case GIR_CopySubReg: { int64_t NewInsnID = MatchTable[CurrentIdx++]; int64_t OldInsnID = MatchTable[CurrentIdx++]; diff --git a/llvm/include/llvm/Target/Target.td b/llvm/include/llvm/Target/Target.td index 955da7bd5c8..048bd1f2a0c 100644 --- a/llvm/include/llvm/Target/Target.td +++ b/llvm/include/llvm/Target/Target.td @@ -768,6 +768,12 @@ class RegisterOperand<RegisterClass regclass, string pm = "printOperand"> AsmOperandClass ParserMatchClass; string OperandType = "OPERAND_REGISTER"; + + // When referenced in the result of a CodeGen pattern, GlobalISel will + // normally copy the matched operand to the result. When this is set, it will + // emit a special copy that will replace zero-immediates with the specified + // zero-register. + Register GIZeroRegister = ?; } let OperandType = "OPERAND_IMMEDIATE" in { diff --git a/llvm/lib/Target/AArch64/AArch64InstrFormats.td b/llvm/lib/Target/AArch64/AArch64InstrFormats.td index 1946d5a14dc..80c5092a4ee 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrFormats.td +++ b/llvm/lib/Target/AArch64/AArch64InstrFormats.td @@ -2603,6 +2603,23 @@ multiclass StoreUI<bits<2> sz, bit V, bits<2> opc, RegisterClass regtype, (!cast<Instruction>(NAME # "ui") regtype:$Rt, GPR64sp:$Rn, 0)>; } +// Same as StoreUI, but take a RegisterOperand. This is used by GlobalISel to +// substitute zero-registers automatically. +// +// TODO: Roll out zero-register subtitution to GPR32/GPR64 and fold this back +// into StoreUI. +multiclass StoreUIz<bits<2> sz, bit V, bits<2> opc, RegisterOperand regtype, + Operand indextype, string asm, list<dag> pattern> { + let AddedComplexity = 10, mayLoad = 0, mayStore = 1, hasSideEffects = 0 in + def ui : BaseLoadStoreUI<sz, V, opc, (outs), + (ins regtype:$Rt, GPR64sp:$Rn, indextype:$offset), + asm, pattern>, + Sched<[WriteST]>; + + def : InstAlias<asm # "\t$Rt, [$Rn]", + (!cast<Instruction>(NAME # "ui") regtype:$Rt, GPR64sp:$Rn, 0)>; +} + def PrefetchOperand : AsmOperandClass { let Name = "Prefetch"; let ParserMethod = "tryParsePrefetch"; diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td index cb562026997..eabbc05a033 100644 --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -2249,11 +2249,11 @@ let AddedComplexity = 19 in { //--- // (unsigned immediate) -defm STRX : StoreUI<0b11, 0, 0b00, GPR64, uimm12s8, "str", - [(store GPR64:$Rt, +defm STRX : StoreUIz<0b11, 0, 0b00, GPR64z, uimm12s8, "str", + [(store GPR64z:$Rt, (am_indexed64 GPR64sp:$Rn, uimm12s8:$offset))]>; -defm STRW : StoreUI<0b10, 0, 0b00, GPR32, uimm12s4, "str", - [(store GPR32:$Rt, +defm STRW : StoreUIz<0b10, 0, 0b00, GPR32z, uimm12s4, "str", + [(store GPR32z:$Rt, (am_indexed32 GPR64sp:$Rn, uimm12s4:$offset))]>; defm STRB : StoreUI<0b00, 1, 0b00, FPR8, uimm12s1, "str", [(store FPR8:$Rt, @@ -2269,12 +2269,12 @@ defm STRD : StoreUI<0b11, 1, 0b00, FPR64, uimm12s8, "str", (am_indexed64 GPR64sp:$Rn, uimm12s8:$offset))]>; defm STRQ : StoreUI<0b00, 1, 0b10, FPR128, uimm12s16, "str", []>; -defm STRHH : StoreUI<0b01, 0, 0b00, GPR32, uimm12s2, "strh", - [(truncstorei16 GPR32:$Rt, +defm STRHH : StoreUIz<0b01, 0, 0b00, GPR32z, uimm12s2, "strh", + [(truncstorei16 GPR32z:$Rt, (am_indexed16 GPR64sp:$Rn, uimm12s2:$offset))]>; -defm STRBB : StoreUI<0b00, 0, 0b00, GPR32, uimm12s1, "strb", - [(truncstorei8 GPR32:$Rt, +defm STRBB : StoreUIz<0b00, 0, 0b00, GPR32z, uimm12s1, "strb", + [(truncstorei8 GPR32z:$Rt, (am_indexed8 GPR64sp:$Rn, uimm12s1:$offset))]>; diff --git a/llvm/lib/Target/AArch64/AArch64RegisterInfo.td b/llvm/lib/Target/AArch64/AArch64RegisterInfo.td index 7e29ee5e9ba..ee5d3547aaa 100644 --- a/llvm/lib/Target/AArch64/AArch64RegisterInfo.td +++ b/llvm/lib/Target/AArch64/AArch64RegisterInfo.td @@ -169,6 +169,15 @@ def GPR64sp0 : RegisterOperand<GPR64sp> { let ParserMatchClass = GPR64spPlus0Operand; } +// GPR32/GPR64 but with zero-register substitution enabled. +// TODO: Roll this out to GPR32/GPR64/GPR32all/GPR64all. +def GPR32z : RegisterOperand<GPR32> { + let GIZeroRegister = WZR; +} +def GPR64z : RegisterOperand<GPR64> { + let GIZeroRegister = XZR; +} + // GPR register classes which include WZR/XZR AND SP/WSP. This is not a // constraint used by any instructions, it is used as a common super-class. def GPR32all : RegisterClass<"AArch64", [i32], 32, (add GPR32common, WZR, WSP)>; diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/select-store.mir b/llvm/test/CodeGen/AArch64/GlobalISel/select-store.mir index 38eeb7f8060..4c4451187ae 100644 --- a/llvm/test/CodeGen/AArch64/GlobalISel/select-store.mir +++ b/llvm/test/CodeGen/AArch64/GlobalISel/select-store.mir @@ -27,6 +27,8 @@ define void @store_gep_8_s64_fpr(i64* %addr) { ret void } define void @store_gep_8_s32_fpr(i32* %addr) { ret void } + + define void @store_v2s32(i64 *%addr) { ret void } ... --- @@ -447,3 +449,29 @@ body: | %3(p0) = G_GEP %0, %2 G_STORE %1, %3 :: (store 4 into %ir.addr) ... +--- +# CHECK-LABEL: name: store_v2s32 +name: store_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 = COPY %d1 +# CHECK: STRDui %1, %0, 0 :: (store 8 into %ir.addr) +body: | + bb.0: + liveins: %x0, %d1 + + %0(p0) = COPY %x0 + %1(<2 x s32>) = COPY %d1 + G_STORE %1, %0 :: (store 8 into %ir.addr) + +... diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp index c14c675aff4..d04d232744a 100644 --- a/llvm/utils/TableGen/GlobalISelEmitter.cpp +++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -246,6 +246,13 @@ static Error isTrivialOperatorNode(const TreePatternNode *N) { if (Predicate.isNonExtLoad()) continue; + + if (Predicate.isStore() && Predicate.isUnindexed()) + continue; + + if (Predicate.isNonTruncStore()) + continue; + HasUnsupportedPredicate = true; Explanation = Separator + "Has a predicate (" + explainPredicates(N) + ")"; Separator = ", "; @@ -1419,6 +1426,7 @@ class OperandRenderer { public: enum RendererKind { OR_Copy, + OR_CopyOrAddZeroReg, OR_CopySubReg, OR_CopyConstantAsImm, OR_CopyFConstantAsFPImm, @@ -1477,6 +1485,48 @@ public: } }; +/// A CopyOrAddZeroRegRenderer emits code to copy a single operand from an +/// existing instruction to the one being built. If the operand turns out to be +/// a 'G_CONSTANT 0' then it replaces the operand with a zero register. +class CopyOrAddZeroRegRenderer : public OperandRenderer { +protected: + unsigned NewInsnID; + /// The name of the operand. + const StringRef SymbolicName; + const Record *ZeroRegisterDef; + +public: + CopyOrAddZeroRegRenderer(unsigned NewInsnID, + const InstructionMatcher &Matched, + StringRef SymbolicName, Record *ZeroRegisterDef) + : OperandRenderer(OR_CopyOrAddZeroReg), NewInsnID(NewInsnID), + SymbolicName(SymbolicName), ZeroRegisterDef(ZeroRegisterDef) { + assert(!SymbolicName.empty() && "Cannot copy from an unspecified source"); + } + + static bool classof(const OperandRenderer *R) { + return R->getKind() == OR_CopyOrAddZeroReg; + } + + const StringRef getSymbolicName() const { return SymbolicName; } + + void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override { + const OperandMatcher &Operand = Rule.getOperandMatcher(SymbolicName); + unsigned OldInsnVarID = Rule.getInsnVarID(Operand.getInstructionMatcher()); + Table << MatchTable::Opcode("GIR_CopyOrAddZeroReg") + << MatchTable::Comment("NewInsnID") << MatchTable::IntValue(NewInsnID) + << MatchTable::Comment("OldInsnID") + << MatchTable::IntValue(OldInsnVarID) << MatchTable::Comment("OpIdx") + << MatchTable::IntValue(Operand.getOperandIndex()) + << MatchTable::NamedValue( + (ZeroRegisterDef->getValue("Namespace") + ? ZeroRegisterDef->getValueAsString("Namespace") + : ""), + ZeroRegisterDef->getName()) + << MatchTable::Comment(SymbolicName) << MatchTable::LineBreak; + } +}; + /// A CopyConstantAsImmRenderer emits code to render a G_CONSTANT instruction to /// an extended immediate operand. class CopyConstantAsImmRenderer : public OperandRenderer { @@ -2275,6 +2325,25 @@ Expected<InstructionMatcher &> GlobalISelEmitter::createAndImportSelDAGMatcher( continue; } + // No check required. A G_STORE is an unindexed store. + if (Predicate.isStore() && Predicate.isUnindexed()) + continue; + + // No check required. G_STORE by itself is a non-extending store. + if (Predicate.isNonTruncStore()) + continue; + + if (Predicate.isStore() && 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) + ")"); } @@ -2311,7 +2380,8 @@ Expected<InstructionMatcher &> GlobalISelEmitter::createAndImportSelDAGMatcher( // 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; + (SrcGIOrNull->TheDef->getName() == "G_LOAD" && i == 0) || + (SrcGIOrNull->TheDef->getName() == "G_STORE" && i == 1); // For G_INTRINSIC/G_INTRINSIC_W_SIDE_EFFECTS, the operand immediately // following the defs is an intrinsic ID. @@ -2530,6 +2600,14 @@ Error GlobalISelEmitter::importExplicitUseRenderer( if (ChildRec->isSubClassOf("RegisterClass") || ChildRec->isSubClassOf("RegisterOperand") || ChildRec->isSubClassOf("ValueType")) { + if (ChildRec->isSubClassOf("RegisterOperand") && + !ChildRec->isValueUnset("GIZeroRegister")) { + DstMIBuilder.addRenderer<CopyOrAddZeroRegRenderer>( + 0, InsnMatcher, DstChild->getName(), + ChildRec->getValueAsDef("GIZeroRegister")); + return Error::success(); + } + DstMIBuilder.addRenderer<CopyRenderer>(0, InsnMatcher, DstChild->getName()); return Error::success(); |