Index: include/llvm/CodeGen/GlobalISel/InstructionSelector.h =================================================================== --- include/llvm/CodeGen/GlobalISel/InstructionSelector.h +++ include/llvm/CodeGen/GlobalISel/InstructionSelector.h @@ -61,9 +61,6 @@ public: virtual ~InstructionSelector() {} - /// This is executed before selecting a function. - virtual void beginFunction(const MachineFunction &MF) {} - /// Select the (possibly generic) instruction \p I to only use target-specific /// opcodes. It is OK to insert multiple instructions, but they cannot be /// generic pre-isel instructions. Index: include/llvm/Target/Target.td =================================================================== --- include/llvm/Target/Target.td +++ include/llvm/Target/Target.td @@ -530,6 +530,12 @@ /// PredicateName - User-level name to use for the predicate. Mainly for use /// in diagnostics such as missing feature errors in the asm matcher. string PredicateName = ""; + + /// Setting this to '1' indicates that the predicate must be recomputed on + /// every function change. Most predicates can leave this at '0'. + /// + /// Ignored by SelectionDAG, it always recomputes the predicate on every use. + bit RecomputePerFunction = 0; } /// NoHonorSignDependentRounding - This predicate is true if support for Index: lib/CodeGen/GlobalISel/InstructionSelector.cpp =================================================================== --- lib/CodeGen/GlobalISel/InstructionSelector.cpp +++ lib/CodeGen/GlobalISel/InstructionSelector.cpp @@ -58,10 +58,11 @@ MO.setReg(constrainOperandRegClass(MF, TRI, MRI, TII, RBI, I, I.getDesc(), Reg, OpI)); - // Tie uses to defs as indicated in MCInstrDesc. + // Tie uses to defs as indicated in MCInstrDesc if this hasn't already been + // done. if (MO.isUse()) { int DefIdx = I.getDesc().getOperandConstraint(OpI, MCOI::TIED_TO); - if (DefIdx != -1) + if (DefIdx != -1 && !I.isRegTiedToUseOperand(DefIdx)) I.tieOperands(DefIdx, OpI); } } Index: lib/Target/AArch64/AArch64InstrInfo.td =================================================================== --- lib/Target/AArch64/AArch64InstrInfo.td +++ lib/Target/AArch64/AArch64InstrInfo.td @@ -314,8 +314,8 @@ // AArch64 Instruction Predicate Definitions. def IsDarwin : Predicate<"Subtarget->isTargetDarwin()">; def IsNotDarwin: Predicate<"!Subtarget->isTargetDarwin()">; -def ForCodeSize : Predicate<"ForCodeSize">; -def NotForCodeSize : Predicate<"!ForCodeSize">; +def ForCodeSize : Predicate<"Subtarget->getForCodeSize()">; +def NotForCodeSize : Predicate<"!Subtarget->getForCodeSize()">; include "AArch64InstrFormats.td" Index: lib/Target/AArch64/AArch64InstructionSelector.cpp =================================================================== --- lib/Target/AArch64/AArch64InstructionSelector.cpp +++ lib/Target/AArch64/AArch64InstructionSelector.cpp @@ -51,7 +51,6 @@ const AArch64Subtarget &STI, const AArch64RegisterBankInfo &RBI); - void beginFunction(const MachineFunction &MF) override; bool select(MachineInstr &I) const override; private: @@ -74,12 +73,10 @@ const AArch64InstrInfo &TII; const AArch64RegisterInfo &TRI; const AArch64RegisterBankInfo &RBI; - bool ForCodeSize; - PredicateBitset AvailableFeatures; - PredicateBitset - computeAvailableFeatures(const MachineFunction *MF, - const AArch64Subtarget *Subtarget) const; +#define GET_GLOBALISEL_PREDICATES_DECL +#include "AArch64GenGlobalISel.inc" +#undef GET_GLOBALISEL_PREDICATES_DECL // We declare the temporaries used by selectImpl() in the class to minimize the // cost of constructing placeholder values. @@ -98,7 +95,10 @@ const AArch64TargetMachine &TM, const AArch64Subtarget &STI, const AArch64RegisterBankInfo &RBI) : InstructionSelector(), TM(TM), STI(STI), TII(*STI.getInstrInfo()), - TRI(*STI.getRegisterInfo()), RBI(RBI), ForCodeSize(), AvailableFeatures() + TRI(*STI.getRegisterInfo()), RBI(RBI), +#define GET_GLOBALISEL_PREDICATES_INIT +#include "AArch64GenGlobalISel.inc" +#undef GET_GLOBALISEL_PREDICATES_INIT #define GET_GLOBALISEL_TEMPORARIES_INIT #include "AArch64GenGlobalISel.inc" #undef GET_GLOBALISEL_TEMPORARIES_INIT @@ -577,12 +577,6 @@ return true; } -void AArch64InstructionSelector::beginFunction( - const MachineFunction &MF) { - ForCodeSize = MF.getFunction()->optForSize(); - AvailableFeatures = computeAvailableFeatures(&MF, &STI); -} - bool AArch64InstructionSelector::select(MachineInstr &I) const { assert(I.getParent() && "Instruction should be in a basic block!"); assert(I.getParent()->getParent() && "Instruction should be in a function!"); Index: lib/Target/AArch64/AArch64Subtarget.h =================================================================== --- lib/Target/AArch64/AArch64Subtarget.h +++ lib/Target/AArch64/AArch64Subtarget.h @@ -124,6 +124,8 @@ /// an optional library. std::unique_ptr GISel; + bool ForCodeSize; + private: /// initializeSubtargetDependencies - Initializes using CPUString and the /// passed in feature string so that we can use initializer lists for @@ -139,7 +141,7 @@ /// of the specified triple. AArch64Subtarget(const Triple &TT, const std::string &CPU, const std::string &FS, const TargetMachine &TM, - bool LittleEndian); + bool LittleEndian, bool ForCodeSize); /// This object will take onwership of \p GISelAccessor. void setGISelAccessor(GISelAccessor &GISel) { @@ -262,6 +264,8 @@ } } + bool getForCodeSize() const { return ForCodeSize; } + /// ParseSubtargetFeatures - Parses features string setting specified /// subtarget options. Definition of function is auto generated by tblgen. void ParseSubtargetFeatures(StringRef CPU, StringRef FS); Index: lib/Target/AArch64/AArch64Subtarget.cpp =================================================================== --- lib/Target/AArch64/AArch64Subtarget.cpp +++ lib/Target/AArch64/AArch64Subtarget.cpp @@ -113,11 +113,12 @@ AArch64Subtarget::AArch64Subtarget(const Triple &TT, const std::string &CPU, const std::string &FS, - const TargetMachine &TM, bool LittleEndian) + const TargetMachine &TM, bool LittleEndian, + bool ForCodeSize) : AArch64GenSubtargetInfo(TT, CPU, FS), ReserveX18(TT.isOSDarwin()), IsLittle(LittleEndian), TargetTriple(TT), FrameLowering(), InstrInfo(initializeSubtargetDependencies(FS, CPU)), TSInfo(), - TLInfo(TM, *this), GISel() {} + TLInfo(TM, *this), GISel(), ForCodeSize(ForCodeSize) {} const CallLowering *AArch64Subtarget::getCallLowering() const { assert(GISel && "Access to GlobalISel APIs not set"); Index: lib/Target/AArch64/AArch64TargetMachine.cpp =================================================================== --- lib/Target/AArch64/AArch64TargetMachine.cpp +++ lib/Target/AArch64/AArch64TargetMachine.cpp @@ -255,6 +255,7 @@ AArch64TargetMachine::getSubtargetImpl(const Function &F) const { Attribute CPUAttr = F.getFnAttribute("target-cpu"); Attribute FSAttr = F.getFnAttribute("target-features"); + bool ForCodeSize = F.optForSize(); std::string CPU = !CPUAttr.hasAttribute(Attribute::None) ? CPUAttr.getValueAsString().str() @@ -262,15 +263,17 @@ std::string FS = !FSAttr.hasAttribute(Attribute::None) ? FSAttr.getValueAsString().str() : TargetFS; + std::string ForCodeSizeStr = + std::string(ForCodeSize ? "+" : "-") + "forcodesize"; - auto &I = SubtargetMap[CPU + FS]; + auto &I = SubtargetMap[CPU + FS + ForCodeSizeStr]; if (!I) { // This needs to be done before we create a new subtarget since any // creation will depend on the TM and the code generation flags on the // function that reside in TargetOptions. resetTargetOptions(F); I = llvm::make_unique(TargetTriple, CPU, FS, *this, - isLittle); + isLittle, ForCodeSize); #ifndef LLVM_BUILD_GLOBAL_ISEL GISelAccessor *GISel = new GISelAccessor(); #else Index: lib/Target/X86/X86InstrInfo.td =================================================================== --- lib/Target/X86/X86InstrInfo.td +++ lib/Target/X86/X86InstrInfo.td @@ -877,7 +877,9 @@ def IsWin64 : Predicate<"Subtarget->isTargetWin64()">; def NotWin64 : Predicate<"!Subtarget->isTargetWin64()">; def NotWin64WithoutFP : Predicate<"!Subtarget->isTargetWin64() ||" - "Subtarget->getFrameLowering()->hasFP(*MF)">; + "Subtarget->getFrameLowering()->hasFP(*MF)"> { + let RecomputePerFunction = 1; +} def IsPS4 : Predicate<"Subtarget->isTargetPS4()">; def NotPS4 : Predicate<"!Subtarget->isTargetPS4()">; def IsNaCl : Predicate<"Subtarget->isTargetNaCl()">; @@ -887,9 +889,9 @@ def NearData : Predicate<"TM.getCodeModel() == CodeModel::Small ||" "TM.getCodeModel() == CodeModel::Kernel">; def IsNotPIC : Predicate<"!TM.isPositionIndependent()">; -def OptForSize : Predicate<"OptForSize">; -def OptForMinSize : Predicate<"OptForMinSize">; -def OptForSpeed : Predicate<"!OptForSize">; +def OptForSize : Predicate<"Subtarget->getOptForSize()">; +def OptForMinSize : Predicate<"Subtarget->getOptForMinSize()">; +def OptForSpeed : Predicate<"!Subtarget->getOptForSize()">; def FastBTMem : Predicate<"!Subtarget->isBTMemSlow()">; def CallImmAddr : Predicate<"Subtarget->isLegalToCallImmediateAddr()">; def FavorMemIndirectCall : Predicate<"!Subtarget->callRegIndirect()">; Index: lib/Target/X86/X86InstructionSelector.cpp =================================================================== --- lib/Target/X86/X86InstructionSelector.cpp +++ lib/Target/X86/X86InstructionSelector.cpp @@ -48,7 +48,6 @@ X86InstructionSelector(const X86TargetMachine &TM, const X86Subtarget &STI, const X86RegisterBankInfo &RBI); - void beginFunction(const MachineFunction &MF) override; bool select(MachineInstr &I) const override; private: @@ -80,12 +79,10 @@ const X86InstrInfo &TII; const X86RegisterInfo &TRI; const X86RegisterBankInfo &RBI; - bool OptForSize; - bool OptForMinSize; - PredicateBitset AvailableFeatures; - PredicateBitset computeAvailableFeatures(const MachineFunction *MF, - const X86Subtarget *Subtarget) const; +#define GET_GLOBALISEL_PREDICATES_DECL +#include "X86GenGlobalISel.inc" +#undef GET_GLOBALISEL_PREDICATES_DECL #define GET_GLOBALISEL_TEMPORARIES_DECL #include "X86GenGlobalISel.inc" @@ -102,8 +99,10 @@ const X86Subtarget &STI, const X86RegisterBankInfo &RBI) : InstructionSelector(), TM(TM), STI(STI), TII(*STI.getInstrInfo()), - TRI(*STI.getRegisterInfo()), RBI(RBI), OptForSize(false), - OptForMinSize(false), AvailableFeatures() + TRI(*STI.getRegisterInfo()), RBI(RBI), +#define GET_GLOBALISEL_PREDICATES_INIT +#include "X86GenGlobalISel.inc" +#undef GET_GLOBALISEL_PREDICATES_INIT #define GET_GLOBALISEL_TEMPORARIES_INIT #include "X86GenGlobalISel.inc" #undef GET_GLOBALISEL_TEMPORARIES_INIT @@ -195,12 +194,6 @@ return true; } -void X86InstructionSelector::beginFunction(const MachineFunction &MF) { - OptForSize = MF.getFunction()->optForSize(); - OptForMinSize = MF.getFunction()->optForMinSize(); - AvailableFeatures = computeAvailableFeatures(&MF, &STI); -} - bool X86InstructionSelector::select(MachineInstr &I) const { assert(I.getParent() && "Instruction should be in a basic block!"); assert(I.getParent()->getParent() && "Instruction should be in a function!"); Index: lib/Target/X86/X86Subtarget.h =================================================================== --- lib/Target/X86/X86Subtarget.h +++ lib/Target/X86/X86Subtarget.h @@ -328,12 +328,16 @@ X86TargetLowering TLInfo; X86FrameLowering FrameLowering; + bool OptForSize; + bool OptForMinSize; + public: /// This constructor initializes the data members to match that /// of the specified triple. /// X86Subtarget(const Triple &TT, StringRef CPU, StringRef FS, - const X86TargetMachine &TM, unsigned StackAlignOverride); + const X86TargetMachine &TM, unsigned StackAlignOverride, + bool OptForSize, bool OptForMinSize); /// This object will take onwership of \p GISelAccessor. void setGISelAccessor(GISelAccessor &GISel) { this->GISel.reset(&GISel); } @@ -499,6 +503,9 @@ bool isSLM() const { return X86ProcFamily == IntelSLM; } bool useSoftFloat() const { return UseSoftFloat; } + bool getOptForSize() const { return OptForSize; } + bool getOptForMinSize() const { return OptForMinSize; } + /// Use mfence if we have SSE2 or we're on x86-64 (even if we asked for /// no-sse2). There isn't any reason to disable it if the target processor /// supports it. Index: lib/Target/X86/X86Subtarget.cpp =================================================================== --- lib/Target/X86/X86Subtarget.cpp +++ lib/Target/X86/X86Subtarget.cpp @@ -326,7 +326,8 @@ X86Subtarget::X86Subtarget(const Triple &TT, StringRef CPU, StringRef FS, const X86TargetMachine &TM, - unsigned StackAlignOverride) + unsigned StackAlignOverride, bool OptForSize, + bool OptForMinSize) : X86GenSubtargetInfo(TT, CPU, FS), X86ProcFamily(Others), PICStyle(PICStyles::None), TM(TM), TargetTriple(TT), StackAlignOverride(StackAlignOverride), @@ -335,8 +336,9 @@ TargetTriple.getEnvironment() != Triple::CODE16), In16BitMode(TargetTriple.getArch() == Triple::x86 && TargetTriple.getEnvironment() == Triple::CODE16), - InstrInfo(initializeSubtargetDependencies(CPU, FS)), - TLInfo(TM, *this), FrameLowering(*this, getStackAlignment()) { + InstrInfo(initializeSubtargetDependencies(CPU, FS)), TLInfo(TM, *this), + FrameLowering(*this, getStackAlignment()), OptForSize(OptForSize), + OptForMinSize(OptForMinSize) { // Determine the PICStyle based on the target selected. if (!isPositionIndependent()) setPICStyle(PICStyles::None); Index: lib/Target/X86/X86TargetMachine.cpp =================================================================== --- lib/Target/X86/X86TargetMachine.cpp +++ lib/Target/X86/X86TargetMachine.cpp @@ -268,6 +268,12 @@ FS = Key.substr(CPU.size()); + bool OptForSize = F.optForSize(); + bool OptForMinSize = F.optForMinSize(); + + Key += std::string(OptForSize ? "+" : "-") + "optforsize"; + Key += std::string(OptForMinSize ? "+" : "-") + "optforminsize"; + auto &I = SubtargetMap[Key]; if (!I) { // This needs to be done before we create a new subtarget since any @@ -275,7 +281,8 @@ // function that reside in TargetOptions. resetTargetOptions(F); I = llvm::make_unique(TargetTriple, CPU, FS, *this, - Options.StackAlignmentOverride); + Options.StackAlignmentOverride, + OptForSize, OptForMinSize); #ifndef LLVM_BUILD_GLOBAL_ISEL GISelAccessor *GISel = new GISelAccessor(); #else @@ -286,7 +293,8 @@ auto *RBI = new X86RegisterBankInfo(*I->getRegisterInfo()); GISel->RegBankInfo.reset(RBI); - GISel->InstSelector.reset(createX86InstructionSelector(*this, *I, *RBI)); + GISel->InstSelector.reset(createX86InstructionSelector( + *this, *I, *RBI)); #endif I->setGISelAccessor(*GISel); } Index: test/CodeGen/X86/GlobalISel/select-inc.mir =================================================================== --- /dev/null +++ test/CodeGen/X86/GlobalISel/select-inc.mir @@ -0,0 +1,37 @@ +# RUN: llc -mtriple=x86_64-linux-gnu -global-isel -run-pass=instruction-select -verify-machineinstrs %s -o - | FileCheck %s --check-prefixes=ALL,INC +# RUN: llc -mtriple=x86_64-linux-gnu -mattr=+slow-incdec -global-isel -run-pass=instruction-select -verify-machineinstrs %s -o - | FileCheck %s --check-prefixes=ALL,ADD + +--- | + define i8 @test_add_i8(i8 %arg1) { + %ret = add i8 %arg1, 1 + ret i8 %ret + } +... + +--- +name: test_add_i8 +legalized: true +regBankSelected: true +# ALL: registers: +# ALL-NEXT: - { id: 0, class: gr8 } +# INC-NEXT: - { id: 1, class: gpr } +# ADD-NEXT: - { id: 1, class: gr8 } +# ALL-NEXT: - { id: 2, class: gr8 } +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } + - { id: 2, class: gpr } +# ALL: %0 = COPY %al +# INC-NEXT: %2 = INC8r %0 +# ADD-NEXT: %1 = MOV8ri 1 +# ADD-NEXT: %2 = ADD8rr %0, %1 +body: | + bb.1 (%ir-block.0): + liveins: %al + + %0(s8) = COPY %al + %1(s8) = G_CONSTANT i8 1 + %2(s8) = G_ADD %0, %1 + %al = COPY %2(s8) + +... Index: test/CodeGen/X86/GlobalISel/select-leaf-constant.mir =================================================================== --- /dev/null +++ test/CodeGen/X86/GlobalISel/select-leaf-constant.mir @@ -0,0 +1,100 @@ +# RUN: llc -mtriple=i586-linux-gnu -global-isel -run-pass=instruction-select %s -o - | FileCheck %s --check-prefix=CHECK +# +# This is necessary to test that attribute-based rule predicates work and that +# they properly reset between functions. However, X86's GlobalISel prioritizes +# C++ instruction selection over Tablegen-erated instruction selection so this +# test is testing unreachable code. +# +# XFAIL: * + +--- | + define i32 @const_i32_1() { + ret i32 1 + } + + define i32 @const_i32_1_optsize() #0 { + ret i32 1 + } + + define i32 @const_i32_1b() { + ret i32 1 + } + + define i32 @const_i32_1_optsizeb() #0 { + ret i32 1 + } + + attributes #0 = { optsize } +... +--- +name: const_i32_1 +legalized: true +regBankSelected: true +selected: false +# CHECK-LABEL: name: const_i32_1 +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gr32 } +registers: + - { id: 0, class: gpr } +# CHECK: body: +# CHECK: %0 = MOV32ri 1 +body: | + bb.1 (%ir-block.0): + %0(s32) = G_CONSTANT i32 1 + %eax = COPY %0(s32) + RET 0, implicit %eax +... +--- +name: const_i32_1_optsize +legalized: true +regBankSelected: true +selected: false +# CHECK-LABEL: name: const_i32_1_optsize +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gr32 } +registers: + - { id: 0, class: gpr } +# CHECK: body: +# CHECK: %0 = MOV32r1 +body: | + bb.1 (%ir-block.0): + %0(s32) = G_CONSTANT i32 1 + %eax = COPY %0(s32) + RET 0, implicit %eax +... +--- +name: const_i32_1b +legalized: true +regBankSelected: true +selected: false +# CHECK-LABEL: name: const_i32_1b +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gr32 } +registers: + - { id: 0, class: gpr } +# CHECK: body: +# CHECK: %0 = MOV32ri 1 +body: | + bb.1 (%ir-block.0): + %0(s32) = G_CONSTANT i32 1 + %eax = COPY %0(s32) + RET 0, implicit %eax +... +--- +name: const_i32_1_optsizeb +legalized: true +regBankSelected: true +selected: false +# CHECK-LABEL: name: const_i32_1_optsizeb +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gr32 } +registers: + - { id: 0, class: gpr } +# CHECK: body: +# CHECK: %0 = MOV32r1 +body: | + bb.1 (%ir-block.0): + %0(s32) = G_CONSTANT i32 1 + %eax = COPY %0(s32) + RET 0, implicit %eax +... Index: test/TableGen/GlobalISelEmitter.td =================================================================== --- test/TableGen/GlobalISelEmitter.td +++ test/TableGen/GlobalISelEmitter.td @@ -32,22 +32,25 @@ def HasA : Predicate<"Subtarget->hasA()">; def HasB : Predicate<"Subtarget->hasB()">; +def HasC : Predicate<"Subtarget->hasC()"> { let RecomputePerFunction = 1; } //===- Test the function boilerplate. -------------------------------------===// // CHECK-LABEL: enum SubtargetFeatureBits : uint8_t { // CHECK-NEXT: Feature_HasABit = 0, // CHECK-NEXT: Feature_HasBBit = 1, +// CHECK-NEXT: Feature_HasCBit = 2, // CHECK-NEXT: }; // CHECK-LABEL: static const char *SubtargetFeatureNames[] = { // CHECK-NEXT: "Feature_HasA", // CHECK-NEXT: "Feature_HasB", +// CHECK-NEXT: "Feature_HasC", // CHECK-NEXT: nullptr // CHECK-NEXT: }; // CHECK-LABEL: PredicateBitset MyTargetInstructionSelector:: -// CHECK-NEXT: computeAvailableFeatures(const MachineFunction *MF, const MyTargetSubtarget *Subtarget) const { +// CHECK-NEXT: computeAvailableModuleFeatures(const MyTargetSubtarget *Subtarget) const { // CHECK-NEXT: PredicateBitset Features; // CHECK-NEXT: if (Subtarget->hasA()) // CHECK-NEXT: Features[Feature_HasABit] = 1; @@ -56,6 +59,14 @@ // CHECK-NEXT: return Features; // CHECK-NEXT: } +// CHECK-LABEL: PredicateBitset MyTargetInstructionSelector:: +// CHECK-NEXT: computeAvailableFunctionFeatures(const MyTargetSubtarget *Subtarget, const MachineFunction *MF) const { +// CHECK-NEXT: PredicateBitset Features; +// CHECK-NEXT: if (Subtarget->hasC()) +// CHECK-NEXT: Features[Feature_HasCBit] = 1; +// CHECK-NEXT: return Features; +// CHECK-NEXT: } + // CHECK: bool MyTargetInstructionSelector::selectImpl(MachineInstr &I) const { // CHECK: MachineFunction &MF = *I.getParent()->getParent(); // CHECK: const MachineRegisterInfo &MRI = MF.getRegInfo(); @@ -216,7 +227,7 @@ //===- Test another simple pattern with regclass operands. ----------------===// // CHECK-LABEL: if ([&]() { -// CHECK-NEXT: PredicateBitset ExpectedFeatures = {Feature_HasABit, Feature_HasBBit}; +// CHECK-NEXT: PredicateBitset ExpectedFeatures = {Feature_HasABit, Feature_HasBBit, Feature_HasCBit}; // CHECK-NEXT: if ((AvailableFeatures & ExpectedFeatures) != ExpectedFeatures) // CHECK-NEXT: return false; // CHECK-NEXT: MachineInstr &MI0 = I; @@ -247,7 +258,7 @@ def MUL : I<(outs GPR32:$dst), (ins GPR32:$src2, GPR32:$src1), [(set GPR32:$dst, (mul GPR32:$src1, GPR32:$src2))]>, - Requires<[HasA, HasB]>; + Requires<[HasA, HasB, HasC]>; //===- Test a pattern with ComplexPattern operands. -----------------------===// // @@ -418,6 +429,32 @@ def ORN : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2), []>; def : Pat<(not GPR32:$Wm), (ORN R0, GPR32:$Wm)>; +//===- Test a simple pattern with just a leaf immediate. ------------------===// + +// CHECK-LABEL: if ([&]() { +// CHECK-NEXT: MachineInstr &MI0 = I; +// CHECK-NEXT: if (MI0.getNumOperands() < 2) +// CHECK-NEXT: return false; +// CHECK-NEXT: if ((MI0.getOpcode() == TargetOpcode::G_CONSTANT) && +// CHECK-NEXT: ((/* dst */ (MRI.getType(MI0.getOperand(0).getReg()) == (LLT::scalar(32))) && +// CHECK-NEXT: ((&RBI.getRegBankFromRegClass(MyTarget::GPR32RegClass) == RBI.getRegBank(MI0.getOperand(0).getReg(), MRI, TRI))))) && +// CHECK-NEXT: ((/* Operand 1 */ (MI0.getOperand(1).isCImm() && MI0.getOperand(1).getCImm()->equalsInt(1))))) { +// CHECK-NEXT: // 1:i32 => (MOV1:i32) +// CHECK-NEXT: MachineInstrBuilder MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(MyTarget::MOV1)); +// CHECK-NEXT: MIB.add(MI0.getOperand(0)/*dst*/); +// CHECK-NEXT: for (const auto *FromMI : {&MI0, }) +// CHECK-NEXT: for (const auto &MMO : FromMI->memoperands()) +// CHECK-NEXT: MIB.addMemOperand(MMO); +// CHECK-NEXT: I.eraseFromParent(); +// CHECK-NEXT: MachineInstr &NewI = *MIB; +// CHECK-NEXT: constrainSelectedInstRegOperands(NewI, TII, TRI, RBI); +// CHECK-NEXT: return true; +// CHECK-NEXT: } +// CHECK-NEXT: return false; +// CHECK-NEXT: }()) { return true; } + +def MOV1 : I<(outs GPR32:$dst), (ins), [(set GPR32:$dst, 1)]>; + //===- Test a pattern with an MBB operand. --------------------------------===// // CHECK-LABEL: if ([&]() { Index: unittests/Target/AArch64/InstSizes.cpp =================================================================== --- unittests/Target/AArch64/InstSizes.cpp +++ unittests/Target/AArch64/InstSizes.cpp @@ -30,7 +30,8 @@ std::unique_ptr createInstrInfo(TargetMachine *TM) { AArch64Subtarget ST(TM->getTargetTriple(), TM->getTargetCPU(), - TM->getTargetFeatureString(), *TM, /* isLittle */ false); + TM->getTargetFeatureString(), *TM, /* isLittle */ false, + /* ForCodeSize */ false); return llvm::make_unique(ST); } Index: utils/TableGen/GlobalISelEmitter.cpp =================================================================== --- utils/TableGen/GlobalISelEmitter.cpp +++ utils/TableGen/GlobalISelEmitter.cpp @@ -135,6 +135,9 @@ std::string Explanation = ""; std::string Separator = ""; if (N->isLeaf()) { + if (IntInit *Int = dyn_cast(N->getLeafValue())) + return Error::success(); + Explanation = "Is a leaf"; Separator = ", "; } @@ -199,21 +202,19 @@ void emitCxxCapturedInsnList(raw_ostream &OS); void emitCxxCaptureStmts(raw_ostream &OS, StringRef Expr); - void emit(raw_ostream &OS, - std::map - SubtargetFeatures); +void emit(raw_ostream &OS, SubtargetFeatureInfoMap SubtargetFeatures); - /// Compare the priority of this object and B. - /// - /// Returns true if this object is more important than B. - bool isHigherPriorityThan(const RuleMatcher &B) const; +/// Compare the priority of this object and B. +/// +/// Returns true if this object is more important than B. +bool isHigherPriorityThan(const RuleMatcher &B) const; - /// Report the maximum number of temporary operands needed by the rule - /// matcher. - unsigned countRendererFns() const; +/// Report the maximum number of temporary operands needed by the rule +/// matcher. +unsigned countRendererFns() const; - // FIXME: Remove this as soon as possible - InstructionMatcher &insnmatcher_front() const { return *Matchers.front(); } +// FIXME: Remove this as soon as possible +InstructionMatcher &insnmatcher_front() const { return *Matchers.front(); } }; template class PredicateListMatcher { @@ -274,6 +275,7 @@ OPM_ComplexPattern, OPM_Instruction, OPM_Int, + OPM_LiteralInt, OPM_LLT, OPM_RegBank, OPM_MBB, @@ -408,13 +410,14 @@ } }; -/// Generates code to check that an operand is a particular int. -class IntOperandMatcher : public OperandPredicateMatcher { +/// Generates code to check that an operand is a G_CONSTANT with a particular +/// int. +class ConstantIntOperandMatcher : public OperandPredicateMatcher { protected: int64_t Value; public: - IntOperandMatcher(int64_t Value) + ConstantIntOperandMatcher(int64_t Value) : OperandPredicateMatcher(OPM_Int), Value(Value) {} static bool classof(const OperandPredicateMatcher *P) { @@ -427,6 +430,27 @@ } }; +/// Generates code to check that an operand is a raw int (where MO.isImm() or +/// MO.isCImm() is true). +class LiteralIntOperandMatcher : public OperandPredicateMatcher { +protected: + int64_t Value; + +public: + LiteralIntOperandMatcher(int64_t Value) + : OperandPredicateMatcher(OPM_LiteralInt), Value(Value) {} + + static bool classof(const OperandPredicateMatcher *P) { + return P->getKind() == OPM_LiteralInt; + } + + void emitCxxPredicateExpr(raw_ostream &OS, RuleMatcher &Rule, + StringRef OperandExpr) const override { + OS << OperandExpr << ".isCImm() && " << OperandExpr + << ".getCImm()->equalsInt(" << Value << ")"; + } +}; + /// Generates code to check that a set of predicates match for a particular /// operand. class OperandMatcher : public PredicateListMatcher { @@ -951,6 +975,9 @@ /// True if the instruction can be built solely by mutating the opcode. bool canMutate() const { + if (OperandRenderers.size() != Matched.getNumOperands()) + return false; + for (const auto &Renderer : enumerate(OperandRenderers)) { if (const auto *Copy = dyn_cast(&*Renderer.value())) { const OperandMatcher &OM = Matched.getOperand(Copy->getSymbolicName()); @@ -1072,8 +1099,7 @@ } void RuleMatcher::emit(raw_ostream &OS, - std::map - SubtargetFeatures) { + SubtargetFeatureInfoMap SubtargetFeatures) { if (Matchers.empty()) llvm_unreachable("Unexpected empty matcher!"); @@ -1218,7 +1244,7 @@ DenseMap ComplexPatternEquivs; // Map of predicates to their subtarget features. - std::map SubtargetFeatures; + SubtargetFeatureInfoMap SubtargetFeatures; void gatherNodeEquivs(); const CodeGenInstruction *findNodeEquiv(Record *N) const; @@ -1228,7 +1254,7 @@ createAndImportSelDAGMatcher(InstructionMatcher &InsnMatcher, const TreePatternNode *Src) const; Error importChildMatcher(InstructionMatcher &InsnMatcher, - TreePatternNode *SrcChild, unsigned OpIdx, + const TreePatternNode *SrcChild, unsigned OpIdx, unsigned &TempOpIdx) const; Expected createAndImportInstructionRenderer( RuleMatcher &M, const TreePatternNode *Dst, @@ -1289,14 +1315,23 @@ if (Src->getExtTypes().size() > 1) return failedImport("Src pattern has multiple results"); - auto SrcGIOrNull = findNodeEquiv(Src->getOperator()); - if (!SrcGIOrNull) - return failedImport("Pattern operator lacks an equivalent Instruction" + - explainOperator(Src->getOperator())); - auto &SrcGI = *SrcGIOrNull; + if (Src->isLeaf()) { + Init *SrcInit = Src->getLeafValue(); + if (IntInit *SrcIntInit = dyn_cast(SrcInit)) { + InsnMatcher.addPredicate( + &Target.getInstruction(RK.getDef("G_CONSTANT"))); + } else + return failedImport("Unable to deduce gMIR opcode to handle Src (which is a leaf)"); + } else { + auto SrcGIOrNull = findNodeEquiv(Src->getOperator()); + if (!SrcGIOrNull) + return failedImport("Pattern operator lacks an equivalent Instruction" + + explainOperator(Src->getOperator())); + auto &SrcGI = *SrcGIOrNull; - // The operators look good: match the opcode and mutate it to the new one. - InsnMatcher.addPredicate(&SrcGI); + // The operators look good: match the opcode + InsnMatcher.addPredicate(&SrcGI); + } unsigned OpIdx = 0; unsigned TempOpIdx = 0; @@ -1313,18 +1348,27 @@ OM.addPredicate(*OpTyOrNone); } - // Match the used operands (i.e. the children of the operator). - for (unsigned i = 0, e = Src->getNumChildren(); i != e; ++i) { - if (auto Error = importChildMatcher(InsnMatcher, Src->getChild(i), OpIdx++, - TempOpIdx)) - return std::move(Error); + if (Src->isLeaf()) { + Init *SrcInit = Src->getLeafValue(); + if (IntInit *SrcIntInit = dyn_cast(SrcInit)) { + OperandMatcher &OM = InsnMatcher.addOperand(OpIdx++, "", TempOpIdx); + OM.addPredicate(SrcIntInit->getValue()); + } else + return failedImport("Unable to deduce gMIR opcode to handle Src (which is a leaf)"); + } else { + // Match the used operands (i.e. the children of the operator). + for (unsigned i = 0, e = Src->getNumChildren(); i != e; ++i) { + if (auto Error = importChildMatcher(InsnMatcher, Src->getChild(i), + OpIdx++, TempOpIdx)) + return std::move(Error); + } } return InsnMatcher; } Error GlobalISelEmitter::importChildMatcher(InstructionMatcher &InsnMatcher, - TreePatternNode *SrcChild, + const TreePatternNode *SrcChild, unsigned OpIdx, unsigned &TempOpIdx) const { OperandMatcher &OM = @@ -1369,7 +1413,7 @@ // Check for constant immediates. if (auto *ChildInt = dyn_cast(SrcChild->getLeafValue())) { - OM.addPredicate(ChildInt->getValue()); + OM.addPredicate(ChildInt->getValue()); return Error::success(); } @@ -1596,6 +1640,10 @@ return failedImport("Src pattern root isn't a trivial operator (" + toString(std::move(Err)) + ")"); + if (Dst->isLeaf()) { + return failedImport("Dst pattern root isn't a known leaf"); + } + // Start with the defined operands (i.e., the results of the root operator). Record *DstOp = Dst->getOperator(); if (!DstOp->isSubClassOf("Instruction")) @@ -1713,14 +1761,36 @@ SubtargetFeatureInfo::emitSubtargetFeatureBitEnumeration(SubtargetFeatures, OS); SubtargetFeatureInfo::emitNameTable(SubtargetFeatures, OS); + + // Separate subtarget features by how often they must be recomputed. + SubtargetFeatureInfoMap ModuleFeatures; + std::copy_if(SubtargetFeatures.begin(), SubtargetFeatures.end(), + std::inserter(ModuleFeatures, ModuleFeatures.end()), + [](const SubtargetFeatureInfoMap::value_type &X) { + return !X.second.mustRecomputePerFunction(); + }); + SubtargetFeatureInfoMap FunctionFeatures; + std::copy_if(SubtargetFeatures.begin(), SubtargetFeatures.end(), + std::inserter(FunctionFeatures, FunctionFeatures.end()), + [](const SubtargetFeatureInfoMap::value_type &X) { + return X.second.mustRecomputePerFunction(); + }); + + SubtargetFeatureInfo::emitComputeAvailableFeatures( + Target.getName(), "InstructionSelector", "computeAvailableModuleFeatures", + ModuleFeatures, OS); SubtargetFeatureInfo::emitComputeAvailableFeatures( - Target.getName(), "InstructionSelector", "computeAvailableFeatures", - SubtargetFeatures, OS); + Target.getName(), "InstructionSelector", + "computeAvailableFunctionFeatures", FunctionFeatures, OS, + "const MachineFunction *MF"); OS << "bool " << Target.getName() << "InstructionSelector::selectImpl(MachineInstr &I) const {\n" << " MachineFunction &MF = *I.getParent()->getParent();\n" - << " const MachineRegisterInfo &MRI = MF.getRegInfo();\n"; + << " const MachineRegisterInfo &MRI = MF.getRegInfo();\n" + << " // FIXME: This should be computed on a per-function basis rather than per-insn.\n" + << " AvailableFunctionFeatures = computeAvailableFunctionFeatures(&STI, &MF);\n" + << " const PredicateBitset AvailableFeatures = getAvailableFeatures();\n"; for (auto &Rule : Rules) { Rule.emit(OS, SubtargetFeatures); @@ -1730,6 +1800,26 @@ OS << " return false;\n" << "}\n" << "#endif // ifdef GET_GLOBALISEL_IMPL\n"; + + OS << "#ifdef GET_GLOBALISEL_PREDICATES_DECL\n" + << "PredicateBitset AvailableModuleFeatures;\n" + << "mutable PredicateBitset AvailableFunctionFeatures;\n" + << "PredicateBitset getAvailableFeatures() const {\n" + << " return AvailableModuleFeatures | AvailableFunctionFeatures;\n" + << "}\n" + << "PredicateBitset\n" + << "computeAvailableModuleFeatures(const " << Target.getName() + << "Subtarget *Subtarget) const;\n" + << "PredicateBitset\n" + << "computeAvailableFunctionFeatures(const " << Target.getName() + << "Subtarget *Subtarget,\n" + << " const MachineFunction *MF) const;\n" + << "#endif // ifdef GET_GLOBALISEL_PREDICATES_DECL\n"; + + OS << "#ifdef GET_GLOBALISEL_PREDICATES_INIT\n" + << "AvailableModuleFeatures(computeAvailableModuleFeatures(&STI)),\n" + << "AvailableFunctionFeatures()\n" + << "#endif // ifdef GET_GLOBALISEL_PREDICATES_INIT\n"; } void GlobalISelEmitter::declareSubtargetFeature(Record *Predicate) { Index: utils/TableGen/SubtargetFeatureInfo.h =================================================================== --- utils/TableGen/SubtargetFeatureInfo.h +++ utils/TableGen/SubtargetFeatureInfo.h @@ -21,6 +21,9 @@ class Record; class RecordKeeper; +struct SubtargetFeatureInfo; +using SubtargetFeatureInfoMap = std::map; + /// Helper class for storing information on a subtarget feature which /// participates in instruction matching. struct SubtargetFeatureInfo { @@ -43,6 +46,10 @@ return "Feature_" + TheDef->getName().str() + "Bit"; } + bool mustRecomputePerFunction() const { + return TheDef->getValueAsBit("RecomputePerFunction"); + } + void dump() const; static std::vector> getAll(const RecordKeeper &Records); @@ -52,21 +59,17 @@ /// This version emits the bit value for the feature and is therefore limited /// to 64 feature bits. static void emitSubtargetFeatureFlagEnumeration( - std::map - &SubtargetFeatures, - raw_ostream &OS); + SubtargetFeatureInfoMap &SubtargetFeatures, raw_ostream &OS); /// Emit the subtarget feature flag definitions. /// /// This version emits the bit index for the feature and can therefore support /// more than 64 feature bits. - static void emitSubtargetFeatureBitEnumeration( - std::map - &SubtargetFeatures, - raw_ostream &OS); + static void + emitSubtargetFeatureBitEnumeration(SubtargetFeatureInfoMap &SubtargetFeatures, + raw_ostream &OS); - static void emitNameTable(std::map &SubtargetFeatures, + static void emitNameTable(SubtargetFeatureInfoMap &SubtargetFeatures, raw_ostream &OS); /// Emit the function to compute the list of available features given a @@ -82,11 +85,12 @@ /// \param FuncName The name of the function to emit. /// \param SubtargetFeatures A map of TableGen records to the /// SubtargetFeatureInfo equivalent. - static void emitComputeAvailableFeatures( - StringRef TargetName, StringRef ClassName, StringRef FuncName, - std::map - &SubtargetFeatures, - raw_ostream &OS); + /// \param ExtraParams Additional arguments to the generated function. + static void + emitComputeAvailableFeatures(StringRef TargetName, StringRef ClassName, + StringRef FuncName, + SubtargetFeatureInfoMap &SubtargetFeatures, + raw_ostream &OS, StringRef ExtraParams = ""); /// Emit the function to compute the list of available features given a /// subtarget. @@ -103,9 +107,7 @@ /// SubtargetFeatureInfo equivalent. static void emitComputeAssemblerAvailableFeatures( StringRef TargetName, StringRef ClassName, StringRef FuncName, - std::map - &SubtargetFeatures, - raw_ostream &OS); + SubtargetFeatureInfoMap &SubtargetFeatures, raw_ostream &OS); }; } // end namespace llvm Index: utils/TableGen/SubtargetFeatureInfo.cpp =================================================================== --- utils/TableGen/SubtargetFeatureInfo.cpp +++ utils/TableGen/SubtargetFeatureInfo.cpp @@ -45,8 +45,7 @@ } void SubtargetFeatureInfo::emitSubtargetFeatureFlagEnumeration( - std::map &SubtargetFeatures, - raw_ostream &OS) { + SubtargetFeatureInfoMap &SubtargetFeatures, raw_ostream &OS) { OS << "// Flags for subtarget features that participate in " << "instruction matching.\n"; OS << "enum SubtargetFeatureFlag : " @@ -60,8 +59,7 @@ } void SubtargetFeatureInfo::emitSubtargetFeatureBitEnumeration( - std::map &SubtargetFeatures, - raw_ostream &OS) { + SubtargetFeatureInfoMap &SubtargetFeatures, raw_ostream &OS) { OS << "// Bits for subtarget features that participate in " << "instruction matching.\n"; OS << "enum SubtargetFeatureBits : " @@ -74,8 +72,7 @@ } void SubtargetFeatureInfo::emitNameTable( - std::map &SubtargetFeatures, - raw_ostream &OS) { + SubtargetFeatureInfoMap &SubtargetFeatures, raw_ostream &OS) { // Need to sort the name table so that lookup by the log of the enum value // gives the proper name. More specifically, for a feature of value 1< &SubtargetFeatures, - raw_ostream &OS) { + SubtargetFeatureInfoMap &SubtargetFeatures, raw_ostream &OS, + StringRef ExtraParams) { OS << "PredicateBitset " << TargetName << ClassName << "::\n" - << FuncName << "(const MachineFunction *MF, const " << TargetName - << "Subtarget *Subtarget) const {\n"; + << FuncName << "(const " << TargetName << "Subtarget *Subtarget"; + if (!ExtraParams.empty()) + OS << ", " << ExtraParams; + OS << ") const {\n"; OS << " PredicateBitset Features;\n"; for (const auto &SF : SubtargetFeatures) { const SubtargetFeatureInfo &SFI = SF.second; @@ -120,8 +119,7 @@ void SubtargetFeatureInfo::emitComputeAssemblerAvailableFeatures( StringRef TargetName, StringRef ClassName, StringRef FuncName, - std::map &SubtargetFeatures, - raw_ostream &OS) { + SubtargetFeatureInfoMap &SubtargetFeatures, raw_ostream &OS) { OS << "uint64_t " << TargetName << ClassName << "::\n" << FuncName << "(const FeatureBitset& FB) const {\n"; OS << " uint64_t Features = 0;\n";