Index: include/llvm/CodeGen/GlobalISel/GISelAccessor.h =================================================================== --- include/llvm/CodeGen/GlobalISel/GISelAccessor.h +++ include/llvm/CodeGen/GlobalISel/GISelAccessor.h @@ -17,6 +17,7 @@ namespace llvm { class CallLowering; +class InstructionSelector; class RegisterBankInfo; /// The goal of this helper class is to gather the accessor to all @@ -27,6 +28,9 @@ struct GISelAccessor { virtual ~GISelAccessor() {} virtual const CallLowering *getCallLowering() const { return nullptr;} + virtual const InstructionSelector *getInstructionSelector() const { + return nullptr; + } virtual const RegisterBankInfo *getRegBankInfo() const { return nullptr;} }; } // End namespace llvm; Index: include/llvm/CodeGen/GlobalISel/InstructionSelect.h =================================================================== --- /dev/null +++ include/llvm/CodeGen/GlobalISel/InstructionSelect.h @@ -0,0 +1,39 @@ +//== llvm/CodeGen/GlobalISel/InstructionSelect.h -----------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file This file describes the interface of the MachineFunctionPass +/// responsible for selecting (possibly generic) machine instructions to +/// target-specific instructions. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECT_H +#define LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECT_H + +#include "llvm/CodeGen/GlobalISel/InstructionSelector.h" +#include "llvm/CodeGen/MachineFunctionPass.h" + +namespace llvm { +/// This pass is responsible for selecting generic machine instructions to +/// target-specific instructions. It relies on the InstructionSelector provided +/// by the target. +/// Selection is done by examining blocks in post-order, and instructions in +/// reverse order. +/// +/// \post for all inst in MF: not isPreISelGenericOpcode(inst.opcode) +class InstructionSelect : public MachineFunctionPass { +public: + static char ID; + const char *getPassName() const override { return "InstructionSelect"; } + + InstructionSelect(); + + bool runOnMachineFunction(MachineFunction &MF) override; +}; +} // End namespace llvm. + +#endif Index: include/llvm/CodeGen/GlobalISel/InstructionSelector.h =================================================================== --- /dev/null +++ include/llvm/CodeGen/GlobalISel/InstructionSelector.h @@ -0,0 +1,44 @@ +//==-- llvm/CodeGen/GlobalISel/InstructionSelector.h -------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// \file This file declares the API for the instruction selector. +/// This class is responsible for selecting machine instructions. +/// It's implemented by the target. It's used by the InstructionSelect pass. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTOR_H +#define LLVM_CODEGEN_GLOBALISEL_INSTRUCTIONSELECTOR_H + +namespace llvm { +class MachineInstr; +class TargetInstrInfo; +class TargetRegisterInfo; + +/// Provides the logic to select generic machine instructions. +class InstructionSelector { +public: + virtual ~InstructionSelector() {} + + /// 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. + /// + /// \returns whether selection succeeded. + /// \pre I.getParent() && I.getParent()->getParent() + /// \post if returns true: !isPreISelGenericOpcode(I.getOpcode()) + virtual bool select(MachineInstr &I) const = 0; + +protected: + InstructionSelector(); +}; + +} // End namespace llvm. + +#endif Index: include/llvm/CodeGen/GlobalISel/RegisterBankInfo.h =================================================================== --- include/llvm/CodeGen/GlobalISel/RegisterBankInfo.h +++ include/llvm/CodeGen/GlobalISel/RegisterBankInfo.h @@ -479,6 +479,15 @@ return &A != &B; } + /// Constrain the (possibly generic) virtual register \p Reg to \p RC. + /// + /// \pre \p Reg is a virtual register that either has a bank or a class. + /// \returns The constrained register class, or nullptr if there is none. + /// \note This is a generic variant of MachineRegisterInfo::constrainRegClass + static const TargetRegisterClass * + constrainGenericRegister(unsigned Reg, const TargetRegisterClass &RC, + MachineRegisterInfo &MRI); + /// Identifier used when the related instruction mapping instance /// is generated by target independent code. /// Make sure not to use that identifier to avoid possible collision. Index: include/llvm/CodeGen/TargetPassConfig.h =================================================================== --- include/llvm/CodeGen/TargetPassConfig.h +++ include/llvm/CodeGen/TargetPassConfig.h @@ -226,6 +226,8 @@ /// class or register banks. virtual bool addRegBankSelect() { return true; } + virtual bool addGlobalInstructionSelect() { return true; } + /// Add the complete, standard set of LLVM CodeGen passes. /// Fully developed targets will not generally override this. virtual void addMachinePasses(); Index: include/llvm/InitializePasses.h =================================================================== --- include/llvm/InitializePasses.h +++ include/llvm/InitializePasses.h @@ -155,6 +155,7 @@ void initializeInstSimplifierPass(PassRegistry&); void initializeInstrProfilingLegacyPassPass(PassRegistry &); void initializeInstructionCombiningPassPass(PassRegistry&); +void initializeInstructionSelectPass(PassRegistry &); void initializeInterleavedAccessPass(PassRegistry &); void initializeInternalizeLegacyPassPass(PassRegistry&); void initializeIntervalPartitionPass(PassRegistry&); Index: include/llvm/Target/TargetSubtargetInfo.h =================================================================== --- include/llvm/Target/TargetSubtargetInfo.h +++ include/llvm/Target/TargetSubtargetInfo.h @@ -25,6 +25,7 @@ class CallLowering; class DataLayout; +class InstructionSelector; class MachineFunction; class MachineInstr; class RegisterBankInfo; @@ -88,6 +89,11 @@ return nullptr; } virtual const CallLowering *getCallLowering() const { return nullptr; } + + virtual const InstructionSelector *getInstructionSelector() const { + return nullptr; + } + /// Target can subclass this hook to select a different DAG scheduler. virtual RegisterScheduler::FunctionPassCtor getDAGScheduler(CodeGenOpt::Level) const { Index: lib/CodeGen/GlobalISel/CMakeLists.txt =================================================================== --- lib/CodeGen/GlobalISel/CMakeLists.txt +++ lib/CodeGen/GlobalISel/CMakeLists.txt @@ -1,6 +1,8 @@ # List of all GlobalISel files. set(GLOBAL_ISEL_FILES IRTranslator.cpp + InstructionSelect.cpp + InstructionSelector.cpp MachineIRBuilder.cpp RegBankSelect.cpp RegisterBank.cpp Index: lib/CodeGen/GlobalISel/GlobalISel.cpp =================================================================== --- lib/CodeGen/GlobalISel/GlobalISel.cpp +++ lib/CodeGen/GlobalISel/GlobalISel.cpp @@ -26,5 +26,6 @@ void llvm::initializeGlobalISel(PassRegistry &Registry) { initializeIRTranslatorPass(Registry); initializeRegBankSelectPass(Registry); + initializeInstructionSelectPass(Registry); } #endif // LLVM_BUILD_GLOBAL_ISEL Index: lib/CodeGen/GlobalISel/InstructionSelect.cpp =================================================================== --- /dev/null +++ lib/CodeGen/GlobalISel/InstructionSelect.cpp @@ -0,0 +1,81 @@ +//===- llvm/CodeGen/GlobalISel/InstructionSelect.cpp - InstructionSelect ---==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file +/// This file implements the InstructionSelect class. +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/GlobalISel/InstructionSelect.h" +#include "llvm/ADT/PostOrderIterator.h" +#include "llvm/ADT/Twine.h" +#include "llvm/CodeGen/GlobalISel/InstructionSelector.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/IR/Function.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Target/TargetSubtargetInfo.h" + +#define DEBUG_TYPE "instruction-select" + +using namespace llvm; + +char InstructionSelect::ID = 0; +INITIALIZE_PASS(InstructionSelect, DEBUG_TYPE, + "Select target instructions out of generic instructions", + false, false); + +InstructionSelect::InstructionSelect() : MachineFunctionPass(ID) { + initializeInstructionSelectPass(*PassRegistry::getPassRegistry()); +} + +static void reportSelectionError(const MachineInstr &MI, const Twine &Message) { + const MachineFunction &MF = *MI.getParent()->getParent(); + std::string ErrStorage; + raw_string_ostream Err(ErrStorage); + Err << Message << ":\nIn function: " << MF.getName() << '\n' << MI << '\n'; + report_fatal_error(Err.str()); +} + +bool InstructionSelect::runOnMachineFunction(MachineFunction &MF) { + DEBUG(dbgs() << "Selecting function: " << MF.getName() << '\n'); + + const InstructionSelector *ISel = MF.getSubtarget().getInstructionSelector(); + assert(ISel && "Cannot work without InstructionSelector"); + + // FIXME: freezeReservedRegs is now done in IRTranslator, but there are many + // other MF/MFI fields we need to initialize. + // FIXME: We could introduce new blocks and will need to fix the outer loop. + for (MachineBasicBlock *MBB : post_order(&MF)) { + for (MachineBasicBlock::reverse_iterator MII = MBB->rbegin(), + End = MBB->rend(); + MII != End;) { + MachineInstr &MI = *MII++; + DEBUG(dbgs() << "Selecting: " << MI << '\n'); + if (!ISel->select(MI)) + reportSelectionError(MI, "Cannot select"); + // FIXME: It would be nice to dump all inserted instructions. It's not + // obvious how, esp. considering select() can insert after MI. + } + } + + // Check that we did select everything. Do this separately to make sure we + // didn't miss any newly inserted instructions. + // FIXME: This (and other checks) should move into a verifier. + // FIXME: That would also let us selectively enable it. + for (MachineBasicBlock &MBB : MF) { + for (MachineInstr &MI : MBB) { + if (isPreISelGenericOpcode(MI.getOpcode())) { + reportSelectionError( + MI, "Generic instruction survived instruction selection"); + } + } + } + + // FIXME: Should we accurately track changes? + return true; +} Index: lib/CodeGen/GlobalISel/InstructionSelector.cpp =================================================================== --- /dev/null +++ lib/CodeGen/GlobalISel/InstructionSelector.cpp @@ -0,0 +1,19 @@ +//===- llvm/CodeGen/GlobalISel/InstructionSelector.cpp -----------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file +/// This file implements the InstructionSelector class. +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/GlobalISel/InstructionSelector.h" + +#define DEBUG_TYPE "instructionselector" + +using namespace llvm; + +InstructionSelector::InstructionSelector() {} Index: lib/CodeGen/GlobalISel/RegisterBankInfo.cpp =================================================================== --- lib/CodeGen/GlobalISel/RegisterBankInfo.cpp +++ lib/CodeGen/GlobalISel/RegisterBankInfo.cpp @@ -197,6 +197,25 @@ return &RegBank; } +const TargetRegisterClass *RegisterBankInfo::constrainGenericRegister( + unsigned Reg, const TargetRegisterClass &RC, MachineRegisterInfo &MRI) { + + // If the register already has a class, fallback to MRI::constrainRegClass. + auto &RegClassOrBank = MRI.getRegClassOrRegBank(Reg); + if (RegClassOrBank.is()) + return MRI.constrainRegClass(Reg, &RC); + + const RegisterBank *RB = RegClassOrBank.get(); + assert(RB && "Generic register does not have a register bank"); + + // Otherwise, all we can do is ensure the bank covers the class, and set it. + if (!RB->covers(RC)) + return nullptr; + + MRI.setRegClass(Reg, &RC); + return &RC; +} + RegisterBankInfo::InstructionMapping RegisterBankInfo::getInstrMappingImpl(const MachineInstr &MI) const { RegisterBankInfo::InstructionMapping Mapping(DefaultMappingID, /*Cost*/ 1, Index: lib/CodeGen/LLVMTargetMachine.cpp =================================================================== --- lib/CodeGen/LLVMTargetMachine.cpp +++ lib/CodeGen/LLVMTargetMachine.cpp @@ -172,6 +172,9 @@ if (PassConfig->addRegBankSelect()) return nullptr; + if (PassConfig->addGlobalInstructionSelect()) + return nullptr; + } else if (PassConfig->addInstSelector()) return nullptr; Index: lib/CodeGen/MachineRegisterInfo.cpp =================================================================== --- lib/CodeGen/MachineRegisterInfo.cpp +++ lib/CodeGen/MachineRegisterInfo.cpp @@ -38,6 +38,27 @@ void MachineRegisterInfo::setRegClass(unsigned Reg, const TargetRegisterClass *RC) { assert(RC && RC->isAllocatable() && "Invalid RC for virtual register"); + +#ifdef LLVM_BUILD_GLOBAL_ISEL + // FIXME: This is completely unnecessary post-isel. Perhaps we should use + // a different vreg numbering scheme for generic vregs? That would make + // this kind of check very cheap. + // Alternatively, we could introduce separate methods for gvregs. The naive + // way (different names) can easily become awkward to use, so maybe this is + // a good opportunity to introduce dedicated vreg types, that cast away to + // unsigned, but let us overload on the type? + + // If Reg was generic and we were tracking a size, we don't need to anymore. + auto &SizeMap = getVRegToSize(); + auto SizeIt = SizeMap.find(Reg); + if (SizeIt != SizeMap.end()) { + // FIXME: Can banks have sizes that aren't multiples of 8? + assert(SizeIt->second == (RC->getSize() * 8) && + "Can't change virtual register size only by giving it a class"); + SizeMap.erase(SizeIt); + } +#endif + VRegInfo[Reg].first = RC; } Index: lib/Target/AArch64/AArch64InstructionSelector.h =================================================================== --- /dev/null +++ lib/Target/AArch64/AArch64InstructionSelector.h @@ -0,0 +1,40 @@ +//===- AArch64InstructionSelector --------------------------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file +/// This file declares the targeting of the InstructionSelector class for +/// AArch64. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_AARCH64_AARCH64INSTRUCTIONSELECTOR_H +#define LLVM_LIB_TARGET_AARCH64_AARCH64INSTRUCTIONSELECTOR_H + +#include "llvm/CodeGen/GlobalISel/InstructionSelector.h" + +namespace llvm { +class AArch64InstrInfo; +class AArch64RegisterBankInfo; +// FIXME: Why on earth is this a struct? +struct AArch64RegisterInfo; +class AArch64Subtarget; + +class AArch64InstructionSelector : public InstructionSelector { +public: + AArch64InstructionSelector(const AArch64Subtarget &STI, + const AArch64RegisterBankInfo &RBI); + + virtual bool select(MachineInstr &I) const override; + +private: + const AArch64InstrInfo &TII; + const AArch64RegisterInfo &TRI; + const AArch64RegisterBankInfo &RBI; +}; + +} // End llvm namespace. +#endif Index: lib/Target/AArch64/AArch64InstructionSelector.cpp =================================================================== --- /dev/null +++ lib/Target/AArch64/AArch64InstructionSelector.cpp @@ -0,0 +1,186 @@ +//===- AArch64InstructionSelector.cpp ----------------------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file +/// This file implements the targeting of the InstructionSelector class for +/// AArch64. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#include "AArch64InstructionSelector.h" +#include "AArch64InstrInfo.h" +#include "AArch64RegisterBankInfo.h" +#include "AArch64RegisterInfo.h" +#include "AArch64Subtarget.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/IR/Type.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +#define DEBUG_TYPE "aarch64-isel" + +using namespace llvm; + +#ifndef LLVM_BUILD_GLOBAL_ISEL +#error "You shouldn't build this" +#endif + +AArch64InstructionSelector::AArch64InstructionSelector( + const AArch64Subtarget &STI, const AArch64RegisterBankInfo &RBI) + : InstructionSelector(), TII(*STI.getInstrInfo()), + TRI(*STI.getRegisterInfo()), RBI(RBI) {} + +/// Select the AArch64 opcode for the basic binary operation \p GenericOpc +/// (such as G_OR or G_ADD), appropriate for the register bank \p RegBankID +/// and of size \p OpSize. +/// \returns \p GenericOpc if the combination is unsupported. +static unsigned selectBinaryOp(unsigned GenericOpc, unsigned RegBankID, + unsigned OpSize) { + switch (RegBankID) { + case AArch64::GPRRegBankID: + switch (OpSize) { + case 32: + switch (GenericOpc) { + case TargetOpcode::G_OR: + return AArch64::ORRWrr; + case TargetOpcode::G_ADD: + return AArch64::ADDWrr; + default: + return GenericOpc; + } + case 64: + switch (GenericOpc) { + case TargetOpcode::G_OR: + return AArch64::ORRXrr; + case TargetOpcode::G_ADD: + return AArch64::ADDXrr; + default: + return GenericOpc; + } + } + }; + return GenericOpc; +} + +/// Mutate the newly-selected instruction \p I to constrain its (possibly +/// generic) virtual register operands to the instruction's register class. +/// This could involve inserting COPYs before (for uses) or after (for defs). +/// This requires the number of operands to match the instruction description. +/// +/// FIXME: We need to somehow generalize this logic, but it's not obvious +/// how. For instance, it's true for the simplest instructions only that the +/// number and order of operands is the same pre- and post-isel. +static void fixupSelectedInstRegOperands(MachineInstr &I, + const AArch64InstrInfo &TII, + const AArch64RegisterInfo &TRI, + const AArch64RegisterBankInfo &RBI) { + MachineBasicBlock &MBB = *I.getParent(); + MachineFunction &MF = *MBB.getParent(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + + for (unsigned OpI = 0, OpE = I.getNumExplicitOperands(); OpI != OpE; ++OpI) { + MachineOperand &MO = I.getOperand(OpI); + DEBUG(dbgs() << "Converting operand: " << MO << '\n'); + + assert(MO.isReg() && "Unsupported binop non-reg operand"); + + const TargetRegisterClass *RC = TII.getRegClass(I.getDesc(), OpI, &TRI, MF); + assert(RC && "Selected inst should have regclass operand"); + + // If the operand is a vreg, we should constrain its regclass, and only + // insert COPYs if that's impossible. + // If the operand is a physreg, we only insert COPYs if the register class + // doesn't contain the register. + if (TRI.isVirtualRegister(MO.getReg())) { + if (RBI.constrainGenericRegister(MO.getReg(), *RC, MRI)) + continue; + } else { + if (RC->contains(MO.getReg())) + continue; + } + + const unsigned NewReg = MRI.createVirtualRegister(RC); + if (MO.isDef()) { + BuildMI(MBB, std::next(MachineBasicBlock::iterator(I)), I.getDebugLoc(), + TII.get(TargetOpcode::COPY), MO.getReg()) + .addReg(NewReg); + MO.setReg(NewReg); + } else { + BuildMI(MBB, I, I.getDebugLoc(), TII.get(TargetOpcode::COPY), NewReg) + .addReg(MO.getReg()); + } + } +} + +bool AArch64InstructionSelector::select(MachineInstr &I) const { + // FIXME: Is this check really necessary? + assert(I.getParent() && "Instruction should be in a basic block!"); + assert(I.getParent()->getParent() && "Instruction should be in a function!"); + + MachineBasicBlock &MBB = *I.getParent(); + MachineFunction &MF = *MBB.getParent(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + + // FIXME: Is there *really* nothing to be done here? This assumes that + // no upstream pass introduces things like generic vreg on copies or + // target-specific instructions. + // We should document (and verify) that assumption. + if (!isPreISelGenericOpcode(I.getOpcode())) + return true; + + assert(I.getNumOperands() == I.getNumExplicitOperands() && + "Generic instruction has unexpected implicit operands"); + + // FIXME: This goes away when we stop using Type. + const Type *Ty = I.getType(); + assert(Ty && "Generic instruction doesn't have a type"); + assert(!Ty->isAggregateType() && + "Generic instruction still has an aggregate type at selection time"); + + // The size (in bits) of the operation, or 0 for the label type. + const unsigned OpSize = Ty->getPrimitiveSizeInBits(); + + switch (I.getOpcode()) { + case TargetOpcode::G_OR: + case TargetOpcode::G_ADD: { + DEBUG(dbgs() << "AArch64: Selecting: binop\n"); + + const unsigned DefReg = I.getOperand(0).getReg(); + // FIXME: Can generic operations have physical registers operands? If so, + // this will need to be taught about that, and we'll need to get the bank + // out of the minimal class for the register. + // Either way, this needs to be documented (and possibly verified). + assert(TRI.isVirtualRegister(DefReg) && "Only supports vregs"); + + const RegisterBank *RBPtr = RBI.getRegBank(DefReg, MRI, TRI); + assert(RBPtr && "Generic register hasn't been selected to a bank or class"); + const RegisterBank &RB = *RBPtr; + + assert(OpSize && "Generic binary instruction type isn't primitive"); + const unsigned NewOpc = selectBinaryOp(I.getOpcode(), RB.getID(), OpSize); + + if (NewOpc == I.getOpcode()) + return false; + + I.setDesc(TII.get(NewOpc)); + // FIXME: Should the type be always reset in setDesc? + I.setType(nullptr); + + // Now that we selected an opcode, we need to fixup the register operands + // to use appropriate classes, inserting COPYs if necessary. + fixupSelectedInstRegOperands(I, TII, TRI, RBI); + return true; + } + } + + return false; +} Index: lib/Target/AArch64/AArch64Subtarget.h =================================================================== --- lib/Target/AArch64/AArch64Subtarget.h +++ lib/Target/AArch64/AArch64Subtarget.h @@ -17,6 +17,7 @@ #include "AArch64FrameLowering.h" #include "AArch64ISelLowering.h" #include "AArch64InstrInfo.h" +#include "AArch64RegisterBankInfo.h" #include "AArch64RegisterInfo.h" #include "AArch64SelectionDAGInfo.h" #include "llvm/CodeGen/GlobalISel/GISelAccessor.h" @@ -147,7 +148,8 @@ return &getInstrInfo()->getRegisterInfo(); } const CallLowering *getCallLowering() const override; - const RegisterBankInfo *getRegBankInfo() const override; + const InstructionSelector *getInstructionSelector() const override; + const AArch64RegisterBankInfo *getRegBankInfo() const override; const Triple &getTargetTriple() const { return TargetTriple; } bool enableMachineScheduler() const override { return true; } bool enablePostRAScheduler() const override { Index: lib/Target/AArch64/AArch64Subtarget.cpp =================================================================== --- lib/Target/AArch64/AArch64Subtarget.cpp +++ lib/Target/AArch64/AArch64Subtarget.cpp @@ -98,9 +98,14 @@ return GISel->getCallLowering(); } -const RegisterBankInfo *AArch64Subtarget::getRegBankInfo() const { +const InstructionSelector *AArch64Subtarget::getInstructionSelector() const { assert(GISel && "Access to GlobalISel APIs not set"); - return GISel->getRegBankInfo(); + return GISel->getInstructionSelector(); +} + +const AArch64RegisterBankInfo *AArch64Subtarget::getRegBankInfo() const { + assert(GISel && "Access to GlobalISel APIs not set"); + return static_cast(GISel->getRegBankInfo()); } /// Find the target operand flags that describe how a global value should be Index: lib/Target/AArch64/AArch64TargetMachine.cpp =================================================================== --- lib/Target/AArch64/AArch64TargetMachine.cpp +++ lib/Target/AArch64/AArch64TargetMachine.cpp @@ -12,11 +12,13 @@ #include "AArch64.h" #include "AArch64CallLowering.h" +#include "AArch64InstructionSelector.h" #include "AArch64RegisterBankInfo.h" #include "AArch64TargetMachine.h" #include "AArch64TargetObjectFile.h" #include "AArch64TargetTransformInfo.h" #include "llvm/CodeGen/GlobalISel/IRTranslator.h" +#include "llvm/CodeGen/GlobalISel/InstructionSelect.h" #include "llvm/CodeGen/GlobalISel/RegBankSelect.h" #include "llvm/CodeGen/Passes.h" #include "llvm/CodeGen/RegAllocRegistry.h" @@ -195,10 +197,14 @@ namespace { struct AArch64GISelActualAccessor : public GISelAccessor { std::unique_ptr CallLoweringInfo; + std::unique_ptr InstSelector; std::unique_ptr RegBankInfo; const CallLowering *getCallLowering() const override { return CallLoweringInfo.get(); } + const InstructionSelector *getInstructionSelector() const override { + return InstSelector.get(); + } const RegisterBankInfo *getRegBankInfo() const override { return RegBankInfo.get(); } @@ -233,8 +239,15 @@ new AArch64GISelActualAccessor(); GISel->CallLoweringInfo.reset( new AArch64CallLowering(*I->getTargetLowering())); - GISel->RegBankInfo.reset( - new AArch64RegisterBankInfo(*I->getRegisterInfo())); + + auto *RBI = new AArch64RegisterBankInfo(*I->getRegisterInfo()); + + // FIXME: At this point, we can't rely on Subtarget having RBI. + // It's awkward to mix passing RBI and the Subtarget; should we pass + // TII/TRI as well? + GISel->InstSelector.reset(new AArch64InstructionSelector(*I, *RBI)); + + GISel->RegBankInfo.reset(RBI); #endif I->setGISelAccessor(*GISel); } @@ -277,6 +290,7 @@ #ifdef LLVM_BUILD_GLOBAL_ISEL bool addIRTranslator() override; bool addRegBankSelect() override; + bool addGlobalInstructionSelect() override; #endif bool addILPOpts() override; void addPreRegAlloc() override; @@ -378,6 +392,10 @@ addPass(new RegBankSelect()); return false; } +bool AArch64PassConfig::addGlobalInstructionSelect() { + addPass(new InstructionSelect()); + return false; +} #endif bool AArch64PassConfig::addILPOpts() { Index: lib/Target/AArch64/CMakeLists.txt =================================================================== --- lib/Target/AArch64/CMakeLists.txt +++ lib/Target/AArch64/CMakeLists.txt @@ -19,6 +19,7 @@ # List of all GlobalISel files. set(GLOBAL_ISEL_FILES AArch64CallLowering.cpp + AArch64InstructionSelector.cpp AArch64RegisterBankInfo.cpp ) Index: lib/Target/AMDGPU/AMDGPUTargetMachine.cpp =================================================================== --- lib/Target/AMDGPU/AMDGPUTargetMachine.cpp +++ lib/Target/AMDGPU/AMDGPUTargetMachine.cpp @@ -315,6 +315,7 @@ #ifdef LLVM_BUILD_GLOBAL_ISEL bool addIRTranslator() override; bool addRegBankSelect() override; + bool addGlobalInstructionSelect() override; #endif void addFastRegAlloc(FunctionPass *RegAllocPass) override; void addOptimizedRegAlloc(FunctionPass *RegAllocPass) override; @@ -515,6 +516,10 @@ bool GCNPassConfig::addRegBankSelect() { return false; } + +bool GCNPassConfig::addGlobalInstructionSelect() { + return false; +} #endif void GCNPassConfig::addPreRegAlloc() { Index: test/CodeGen/AArch64/GlobalISel/arm64-instructionselect.mir =================================================================== --- /dev/null +++ test/CodeGen/AArch64/GlobalISel/arm64-instructionselect.mir @@ -0,0 +1,151 @@ +# RUN: llc -O0 -run-pass=instruction-select -global-isel %s -o - | FileCheck %s +# REQUIRES: global-isel + +# Test the instruction selector. +# As we support more instructions, we need to split this up. + +--- | + target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128" + target triple = "aarch64-apple-ios" + + define void @add_i32_gpr() { ret void } + define void @add_i64_gpr() { ret void } + + define void @or_i32_gpr() { ret void } + define void @or_i64_gpr() { ret void } + + define void @or_i64_gpr_copy_fpr_use() { ret void } + +... + +--- +# Check that we select a 32-bit GPR G_ADD into ADDWrr on GPR32. +# Also check that we constrain the register class of the COPY to GPR32. +# CHECK-LABEL: name: add_i32_gpr +name: add_i32_gpr +alignment: 2 +isSSA: true + +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gpr32 } +# CHECK-NEXT: - { id: 1, class: gpr32 } +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } + +# CHECK: body: +# CHECK: %0 = COPY %w0 +# CHECK: %1 = ADDWrr %0, %0 +body: | + bb.0: + liveins: %w0 + + %0(32) = COPY %w0 + %1(32) = G_ADD i32 %0, %0 +... + +--- +# Same as add_i32_gpr, for 64-bit operations. +# CHECK-LABEL: name: add_i64_gpr +name: add_i64_gpr +alignment: 2 +isSSA: true + +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gpr64 } +# CHECK-NEXT: - { id: 1, class: gpr64 } +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } + +# CHECK: body: +# CHECK: %0 = COPY %x0 +# CHECK: %1 = ADDXrr %0, %0 +body: | + bb.0: + liveins: %x0 + + %0(64) = COPY %x0 + %1(64) = G_ADD i64 %0, %0 +... + +--- +# Same as add_i32_gpr, for G_OR operations. +# CHECK-LABEL: name: or_i32_gpr +name: or_i32_gpr +alignment: 2 +isSSA: true + +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gpr32 } +# CHECK-NEXT: - { id: 1, class: gpr32 } +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } + +# CHECK: body: +# CHECK: %0 = COPY %w0 +# CHECK: %1 = ORRWrr %0, %0 +body: | + bb.0: + liveins: %w0 + + %0(32) = COPY %w0 + %1(32) = G_OR i32 %0, %0 +... + +--- +# Same as add_i64_gpr, for G_OR operations. +# CHECK-LABEL: name: or_i64_gpr +name: or_i64_gpr +alignment: 2 +isSSA: true + +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gpr64 } +# CHECK-NEXT: - { id: 1, class: gpr64 } +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } + +# CHECK: body: +# CHECK: %0 = COPY %x0 +# CHECK: %1 = ORRXrr %0, %0 +body: | + bb.0: + liveins: %x0 + + %0(64) = COPY %x0 + %1(64) = G_OR i64 %0, %0 +... + +--- +# Check that we emit a FPR64->GPR64 COPY for a GPR G_OR with an FPR use. +# CHECK-LABEL: name: or_i64_gpr_copy_fpr_use +name: or_i64_gpr_copy_fpr_use +alignment: 2 +isSSA: true + +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gpr64 } +# CHECK-NEXT: - { id: 1, class: fpr64 } +# CHECK-NEXT: - { id: 2, class: gpr64 } +# CHECK-NEXT: - { id: 3, class: gpr64 } +registers: + - { id: 0, class: gpr } + - { id: 1, class: fpr64 } + - { id: 2, class: gpr } + +# CHECK: body: +# CHECK: %0 = COPY %x0 +# CHECK: %1 = COPY %d0 +# CHECK: %3 = COPY %1 +# CHECK: %2 = ORRXrr %0, %1 +body: | + bb.0: + liveins: %x0, %d0 + + %0(64) = COPY %x0 + %1 = COPY %d0 + %2(64) = G_OR i64 %0, %1 +...