mirror of
https://github.com/RPCSX/llvm.git
synced 2025-04-12 21:18:23 +00:00
[GlobalISel] Introduce an instruction selector.
And implement it for AArch64, supporting x/w ADD/OR. Differential Revision: https://reviews.llvm.org/D22373 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@276875 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
cd0d4b0055
commit
f15a020711
@ -17,6 +17,7 @@
|
||||
|
||||
namespace llvm {
|
||||
class CallLowering;
|
||||
class InstructionSelector;
|
||||
class MachineLegalizer;
|
||||
class RegisterBankInfo;
|
||||
|
||||
@ -28,6 +29,9 @@ class RegisterBankInfo;
|
||||
struct GISelAccessor {
|
||||
virtual ~GISelAccessor() {}
|
||||
virtual const CallLowering *getCallLowering() const { return nullptr;}
|
||||
virtual const InstructionSelector *getInstructionSelector() const {
|
||||
return nullptr;
|
||||
}
|
||||
virtual const MachineLegalizer *getMachineLegalizer() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
39
include/llvm/CodeGen/GlobalISel/InstructionSelect.h
Normal file
39
include/llvm/CodeGen/GlobalISel/InstructionSelect.h
Normal file
@ -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
|
63
include/llvm/CodeGen/GlobalISel/InstructionSelector.h
Normal file
63
include/llvm/CodeGen/GlobalISel/InstructionSelector.h
Normal file
@ -0,0 +1,63 @@
|
||||
//==-- 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 RegisterBankInfo;
|
||||
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:
|
||||
/// for I in all mutated/inserted instructions:
|
||||
/// !isPreISelGenericOpcode(I.getOpcode())
|
||||
///
|
||||
virtual bool select(MachineInstr &I) const = 0;
|
||||
|
||||
protected:
|
||||
InstructionSelector();
|
||||
|
||||
/// 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.
|
||||
/// \returns whether operand regclass constraining succeeded.
|
||||
///
|
||||
// FIXME: Not all instructions have the same number of operands. We should
|
||||
// probably expose a constrain helper per operand and let the target selector
|
||||
// constrain individual registers, like fast-isel.
|
||||
bool constrainSelectedInstRegOperands(MachineInstr &I,
|
||||
const TargetInstrInfo &TII,
|
||||
const TargetRegisterInfo &TRI,
|
||||
const RegisterBankInfo &RBI) const;
|
||||
};
|
||||
|
||||
} // End namespace llvm.
|
||||
|
||||
#endif
|
@ -437,6 +437,15 @@ public:
|
||||
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.
|
||||
|
@ -658,6 +658,10 @@ public:
|
||||
/// \pre Size > 0.
|
||||
unsigned createGenericVirtualRegister(unsigned Size);
|
||||
|
||||
/// Remove all sizes associated to virtual registers (after instruction
|
||||
/// selection and constraining of all generic virtual registers).
|
||||
void clearVirtRegSizes();
|
||||
|
||||
/// getNumVirtRegs - Return the number of virtual registers created.
|
||||
///
|
||||
unsigned getNumVirtRegs() const { return VRegInfo.size(); }
|
||||
|
@ -234,6 +234,16 @@ public:
|
||||
/// class or register banks.
|
||||
virtual bool addRegBankSelect() { return true; }
|
||||
|
||||
/// This method may be implemented by targets that want to run passes
|
||||
/// immediately before the (global) instruction selection.
|
||||
virtual void addPreGlobalInstructionSelect() {}
|
||||
|
||||
/// This method should install a (global) instruction selector pass, which
|
||||
/// converts possibly generic instructions to fully target-specific
|
||||
/// instructions, thereby constraining all generic virtual registers to
|
||||
/// register classes.
|
||||
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();
|
||||
|
@ -155,6 +155,7 @@ void initializeInstNamerPass(PassRegistry&);
|
||||
void initializeInstSimplifierPass(PassRegistry&);
|
||||
void initializeInstrProfilingLegacyPassPass(PassRegistry &);
|
||||
void initializeInstructionCombiningPassPass(PassRegistry&);
|
||||
void initializeInstructionSelectPass(PassRegistry &);
|
||||
void initializeInterleavedAccessPass(PassRegistry &);
|
||||
void initializeInternalizeLegacyPassPass(PassRegistry&);
|
||||
void initializeIntervalPartitionPass(PassRegistry&);
|
||||
|
@ -25,6 +25,7 @@ namespace llvm {
|
||||
|
||||
class CallLowering;
|
||||
class DataLayout;
|
||||
class InstructionSelector;
|
||||
class MachineFunction;
|
||||
class MachineInstr;
|
||||
class MachineLegalizer;
|
||||
@ -89,6 +90,15 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
virtual const CallLowering *getCallLowering() const { return nullptr; }
|
||||
|
||||
// FIXME: This lets targets specialize the selector by subtarget (which lets
|
||||
// us do things like a dedicated avx512 selector). However, we might want
|
||||
// to also specialize selectors by MachineFunction, which would let us be
|
||||
// aware of optsize/optnone and such.
|
||||
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 {
|
||||
|
@ -1,6 +1,8 @@
|
||||
# List of all GlobalISel files.
|
||||
set(GLOBAL_ISEL_FILES
|
||||
IRTranslator.cpp
|
||||
InstructionSelect.cpp
|
||||
InstructionSelector.cpp
|
||||
MachineIRBuilder.cpp
|
||||
MachineLegalizeHelper.cpp
|
||||
MachineLegalizePass.cpp
|
||||
|
@ -27,5 +27,6 @@ void llvm::initializeGlobalISel(PassRegistry &Registry) {
|
||||
initializeIRTranslatorPass(Registry);
|
||||
initializeMachineLegalizePassPass(Registry);
|
||||
initializeRegBankSelectPass(Registry);
|
||||
initializeInstructionSelectPass(Registry);
|
||||
}
|
||||
#endif // LLVM_BUILD_GLOBAL_ISEL
|
||||
|
99
lib/CodeGen/GlobalISel/InstructionSelect.cpp
Normal file
99
lib/CodeGen/GlobalISel/InstructionSelect.cpp
Normal file
@ -0,0 +1,99 @@
|
||||
//===- 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.
|
||||
|
||||
#ifndef NDEBUG
|
||||
// FIXME: We could introduce new blocks and will need to fix the outer loop.
|
||||
// Until then, keep track of the number of blocks to assert that we don't.
|
||||
const size_t NumBlocks = MF.size();
|
||||
#endif
|
||||
|
||||
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.
|
||||
}
|
||||
}
|
||||
|
||||
assert(MF.size() == NumBlocks && "Inserting blocks is not supported yet");
|
||||
|
||||
// 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, predicated on
|
||||
// a "post-isel" MachineFunction property. That would also let us selectively
|
||||
// enable it depending on build configuration.
|
||||
for (MachineBasicBlock &MBB : MF) {
|
||||
for (MachineInstr &MI : MBB) {
|
||||
if (isPreISelGenericOpcode(MI.getOpcode())) {
|
||||
reportSelectionError(
|
||||
MI, "Generic instruction survived instruction selection");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now that selection is complete, there are no more generic vregs.
|
||||
// FIXME: We're still discussing what to do with the vreg->size map:
|
||||
// it's somewhat redundant (with the def MIs type size), but having to
|
||||
// examine MIs is also awkward. Another alternative is to track the type on
|
||||
// the vreg instead, but that's not ideal either, because it's saying that
|
||||
// vregs have types, which they really don't. But then again, LLT is just
|
||||
// a size and a "shape": it's probably the same information as regbank info.
|
||||
MF.getRegInfo().clearVirtRegSizes();
|
||||
|
||||
// FIXME: Should we accurately track changes?
|
||||
return true;
|
||||
}
|
52
lib/CodeGen/GlobalISel/InstructionSelector.cpp
Normal file
52
lib/CodeGen/GlobalISel/InstructionSelector.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
//===- 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"
|
||||
#include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h"
|
||||
#include "llvm/CodeGen/MachineInstr.h"
|
||||
#include "llvm/Target/TargetInstrInfo.h"
|
||||
#include "llvm/Target/TargetRegisterInfo.h"
|
||||
|
||||
#define DEBUG_TYPE "instructionselector"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
InstructionSelector::InstructionSelector() {}
|
||||
|
||||
bool InstructionSelector::constrainSelectedInstRegOperands(
|
||||
MachineInstr &I, const TargetInstrInfo &TII, const TargetRegisterInfo &TRI,
|
||||
const RegisterBankInfo &RBI) const {
|
||||
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 (RBI.constrainGenericRegister(MO.getReg(), *RC, MRI))
|
||||
continue;
|
||||
|
||||
DEBUG(dbgs() << "Constraining with COPYs isn't implemented yet");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
@ -189,6 +189,25 @@ const RegisterBank *RegisterBankInfo::getRegBankFromConstraints(
|
||||
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<const TargetRegisterClass *>())
|
||||
return MRI.constrainRegClass(Reg, &RC);
|
||||
|
||||
const RegisterBank *RB = RegClassOrBank.get<const RegisterBank *>();
|
||||
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,
|
||||
|
@ -177,6 +177,11 @@ addPassesToGenerateCode(LLVMTargetMachine *TM, PassManagerBase &PM,
|
||||
if (PassConfig->addRegBankSelect())
|
||||
return nullptr;
|
||||
|
||||
PassConfig->addPreGlobalInstructionSelect();
|
||||
|
||||
if (PassConfig->addGlobalInstructionSelect())
|
||||
return nullptr;
|
||||
|
||||
} else if (PassConfig->addInstSelector())
|
||||
return nullptr;
|
||||
|
||||
|
@ -136,6 +136,20 @@ MachineRegisterInfo::createGenericVirtualRegister(unsigned Size) {
|
||||
return Reg;
|
||||
}
|
||||
|
||||
void MachineRegisterInfo::clearVirtRegSizes() {
|
||||
#ifndef NDEBUG
|
||||
// Verify that the size of the now-constrained vreg is unchanged.
|
||||
for (auto &VRegToSize : getVRegToSize()) {
|
||||
auto *RC = getRegClass(VRegToSize.first);
|
||||
if (VRegToSize.second != (RC->getSize() * 8))
|
||||
llvm_unreachable(
|
||||
"Virtual register has explicit size different from its class size");
|
||||
}
|
||||
#endif
|
||||
|
||||
getVRegToSize().clear();
|
||||
}
|
||||
|
||||
/// clearVirtRegs - Remove all virtual registers (after physreg assignment).
|
||||
void MachineRegisterInfo::clearVirtRegs() {
|
||||
#ifndef NDEBUG
|
||||
|
162
lib/Target/AArch64/AArch64InstructionSelector.cpp
Normal file
162
lib/Target/AArch64/AArch64InstructionSelector.cpp
Normal file
@ -0,0 +1,162 @@
|
||||
//===- 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;
|
||||
}
|
||||
|
||||
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!");
|
||||
|
||||
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;
|
||||
|
||||
if (I.getNumOperands() != I.getNumExplicitOperands()) {
|
||||
DEBUG(dbgs() << "Generic instruction has unexpected implicit operands\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
LLT Ty = I.getType();
|
||||
assert(Ty.isValid() && "Generic instruction doesn't have a type");
|
||||
|
||||
// FIXME: Support unsized instructions (e.g., G_BR).
|
||||
if (!Ty.isSized()) {
|
||||
DEBUG(dbgs() << "Unsized generic instructions are unsupported\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// The size (in bits) of the operation, or 0 for the label type.
|
||||
const unsigned OpSize = Ty.getSizeInBits();
|
||||
|
||||
switch (I.getOpcode()) {
|
||||
case TargetOpcode::G_OR:
|
||||
case TargetOpcode::G_ADD: {
|
||||
DEBUG(dbgs() << "AArch64: Selecting: binop\n");
|
||||
|
||||
// Reject the various things we don't support yet.
|
||||
{
|
||||
const RegisterBank *PrevOpBank = nullptr;
|
||||
for (auto &MO : I.operands()) {
|
||||
// FIXME: Support non-register operands.
|
||||
if (!MO.isReg()) {
|
||||
DEBUG(dbgs() << "Generic inst non-reg operands are unsupported\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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).
|
||||
if (!TargetRegisterInfo::isVirtualRegister(MO.getReg())) {
|
||||
DEBUG(dbgs() << "Generic inst has physical register operand\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const RegisterBank *OpBank = RBI.getRegBank(MO.getReg(), MRI, TRI);
|
||||
if (!OpBank) {
|
||||
DEBUG(dbgs() << "Generic register has no bank or class\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PrevOpBank && OpBank != PrevOpBank) {
|
||||
DEBUG(dbgs() << "Generic inst operands have different banks\n");
|
||||
return false;
|
||||
}
|
||||
PrevOpBank = OpBank;
|
||||
}
|
||||
}
|
||||
|
||||
const unsigned DefReg = I.getOperand(0).getReg();
|
||||
const RegisterBank &RB = *RBI.getRegBank(DefReg, MRI, TRI);
|
||||
|
||||
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(LLT());
|
||||
|
||||
// Now that we selected an opcode, we need to constrain the register
|
||||
// operands to use appropriate classes.
|
||||
return constrainSelectedInstRegOperands(I, TII, TRI, RBI);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
39
lib/Target/AArch64/AArch64InstructionSelector.h
Normal file
39
lib/Target/AArch64/AArch64InstructionSelector.h
Normal file
@ -0,0 +1,39 @@
|
||||
//===- 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;
|
||||
class 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
|
@ -98,6 +98,11 @@ const CallLowering *AArch64Subtarget::getCallLowering() const {
|
||||
return GISel->getCallLowering();
|
||||
}
|
||||
|
||||
const InstructionSelector *AArch64Subtarget::getInstructionSelector() const {
|
||||
assert(GISel && "Access to GlobalISel APIs not set");
|
||||
return GISel->getInstructionSelector();
|
||||
}
|
||||
|
||||
const MachineLegalizer *AArch64Subtarget::getMachineLegalizer() const {
|
||||
assert(GISel && "Access to GlobalISel APIs not set");
|
||||
return GISel->getMachineLegalizer();
|
||||
|
@ -147,6 +147,7 @@ public:
|
||||
return &getInstrInfo()->getRegisterInfo();
|
||||
}
|
||||
const CallLowering *getCallLowering() const override;
|
||||
const InstructionSelector *getInstructionSelector() const override;
|
||||
const MachineLegalizer *getMachineLegalizer() const override;
|
||||
const RegisterBankInfo *getRegBankInfo() const override;
|
||||
const Triple &getTargetTriple() const { return TargetTriple; }
|
||||
|
@ -12,12 +12,14 @@
|
||||
|
||||
#include "AArch64.h"
|
||||
#include "AArch64CallLowering.h"
|
||||
#include "AArch64InstructionSelector.h"
|
||||
#include "AArch64MachineLegalizer.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/MachineLegalizePass.h"
|
||||
#include "llvm/CodeGen/GlobalISel/RegBankSelect.h"
|
||||
#include "llvm/CodeGen/Passes.h"
|
||||
@ -198,11 +200,15 @@ AArch64TargetMachine::~AArch64TargetMachine() {}
|
||||
namespace {
|
||||
struct AArch64GISelActualAccessor : public GISelAccessor {
|
||||
std::unique_ptr<CallLowering> CallLoweringInfo;
|
||||
std::unique_ptr<InstructionSelector> InstSelector;
|
||||
std::unique_ptr<MachineLegalizer> Legalizer;
|
||||
std::unique_ptr<RegisterBankInfo> RegBankInfo;
|
||||
const CallLowering *getCallLowering() const override {
|
||||
return CallLoweringInfo.get();
|
||||
}
|
||||
const InstructionSelector *getInstructionSelector() const override {
|
||||
return InstSelector.get();
|
||||
}
|
||||
const class MachineLegalizer *getMachineLegalizer() const override {
|
||||
return Legalizer.get();
|
||||
}
|
||||
@ -241,8 +247,15 @@ AArch64TargetMachine::getSubtargetImpl(const Function &F) const {
|
||||
GISel->CallLoweringInfo.reset(
|
||||
new AArch64CallLowering(*I->getTargetLowering()));
|
||||
GISel->Legalizer.reset(new AArch64MachineLegalizer());
|
||||
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);
|
||||
}
|
||||
@ -286,6 +299,7 @@ public:
|
||||
bool addIRTranslator() override;
|
||||
bool addLegalizeMachineIR() override;
|
||||
bool addRegBankSelect() override;
|
||||
bool addGlobalInstructionSelect() override;
|
||||
#endif
|
||||
bool addILPOpts() override;
|
||||
void addPreRegAlloc() override;
|
||||
@ -391,6 +405,10 @@ bool AArch64PassConfig::addRegBankSelect() {
|
||||
addPass(new RegBankSelect());
|
||||
return false;
|
||||
}
|
||||
bool AArch64PassConfig::addGlobalInstructionSelect() {
|
||||
addPass(new InstructionSelect());
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool AArch64PassConfig::addILPOpts() {
|
||||
|
@ -19,6 +19,7 @@ add_public_tablegen_target(AArch64CommonTableGen)
|
||||
# List of all GlobalISel files.
|
||||
set(GLOBAL_ISEL_FILES
|
||||
AArch64CallLowering.cpp
|
||||
AArch64InstructionSelector.cpp
|
||||
AArch64MachineLegalizer.cpp
|
||||
AArch64RegisterBankInfo.cpp
|
||||
)
|
||||
|
@ -317,6 +317,7 @@ public:
|
||||
bool addIRTranslator() override;
|
||||
bool addLegalizeMachineIR() override;
|
||||
bool addRegBankSelect() override;
|
||||
bool addGlobalInstructionSelect() override;
|
||||
#endif
|
||||
void addFastRegAlloc(FunctionPass *RegAllocPass) override;
|
||||
void addOptimizedRegAlloc(FunctionPass *RegAllocPass) override;
|
||||
@ -528,6 +529,10 @@ bool GCNPassConfig::addLegalizeMachineIR() {
|
||||
bool GCNPassConfig::addRegBankSelect() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GCNPassConfig::addGlobalInstructionSelect() {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
void GCNPassConfig::addPreRegAlloc() {
|
||||
|
118
test/CodeGen/AArch64/GlobalISel/arm64-instructionselect.mir
Normal file
118
test/CodeGen/AArch64/GlobalISel/arm64-instructionselect.mir
Normal file
@ -0,0 +1,118 @@
|
||||
# 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_s32_gpr() { ret void }
|
||||
define void @add_s64_gpr() { ret void }
|
||||
|
||||
define void @or_s32_gpr() { ret void }
|
||||
define void @or_s64_gpr() { 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_s32_gpr
|
||||
name: add_s32_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 s32 %0, %0
|
||||
...
|
||||
|
||||
---
|
||||
# Same as add_s32_gpr, for 64-bit operations.
|
||||
# CHECK-LABEL: name: add_s64_gpr
|
||||
name: add_s64_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 s64 %0, %0
|
||||
...
|
||||
|
||||
---
|
||||
# Same as add_s32_gpr, for G_OR operations.
|
||||
# CHECK-LABEL: name: or_s32_gpr
|
||||
name: or_s32_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 s32 %0, %0
|
||||
...
|
||||
|
||||
---
|
||||
# Same as add_s64_gpr, for G_OR operations.
|
||||
# CHECK-LABEL: name: or_s64_gpr
|
||||
name: or_s64_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 s64 %0, %0
|
||||
...
|
Loading…
x
Reference in New Issue
Block a user