Skip to content

Commit

Permalink
[ARM] GlobalISel: Select add i32, i32
Browse files Browse the repository at this point in the history
Add the minimal support necessary to select a function that returns the sum of
two i32 values.

This includes some support for argument/return lowering of i32 values through
registers, as well as the handling of copy and add instructions throughout the
GlobalISel pipeline.

Differential Revision: https://reviews.llvm.org/D26677

llvm-svn: 289940
  • Loading branch information
rovka committed Dec 16, 2016
1 parent 85c9211 commit 812caee
Show file tree
Hide file tree
Showing 13 changed files with 429 additions and 9 deletions.
5 changes: 5 additions & 0 deletions llvm/lib/CodeGen/GlobalISel/InstructionSelector.cpp
Expand Up @@ -43,6 +43,11 @@ bool InstructionSelector::constrainSelectedInstRegOperands(
if (TRI.isPhysicalRegister(MO.getReg()))
continue;

// Register operands with a value of 0 (e.g. predicate operands) don't need
// to be constrained.
if (MO.getReg() == 0)
continue;

const TargetRegisterClass *RC = TII.getRegClass(I.getDesc(), OpI, &TRI, MF);
assert(RC && "Selected inst should have regclass operand");

Expand Down
139 changes: 134 additions & 5 deletions llvm/lib/Target/ARM/ARMCallLowering.cpp
Expand Up @@ -29,19 +29,148 @@ using namespace llvm;
ARMCallLowering::ARMCallLowering(const ARMTargetLowering &TLI)
: CallLowering(&TLI) {}

static bool isSupportedType(const DataLayout DL, const ARMTargetLowering &TLI,
Type *T) {
EVT VT = TLI.getValueType(DL, T);
return VT.isSimple() && VT.isInteger() &&
VT.getSimpleVT().getSizeInBits() == 32;
}

namespace {
struct FuncReturnHandler : public CallLowering::ValueHandler {
FuncReturnHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI,
MachineInstrBuilder &MIB)
: ValueHandler(MIRBuilder, MRI), MIB(MIB) {}

unsigned getStackAddress(uint64_t Size, int64_t Offset,
MachinePointerInfo &MPO) override {
llvm_unreachable("Don't know how to get a stack address yet");
}

void assignValueToReg(unsigned ValVReg, unsigned PhysReg,
CCValAssign &VA) override {
assert(VA.isRegLoc() && "Value shouldn't be assigned to reg");
assert(VA.getLocReg() == PhysReg && "Assigning to the wrong reg?");

assert(VA.getValVT().getSizeInBits() == 32 && "Unsupported value size");
assert(VA.getLocVT().getSizeInBits() == 32 && "Unsupported location size");

MIRBuilder.buildCopy(PhysReg, ValVReg);
MIB.addUse(PhysReg, RegState::Implicit);
}

void assignValueToAddress(unsigned ValVReg, unsigned Addr, uint64_t Size,
MachinePointerInfo &MPO, CCValAssign &VA) override {
llvm_unreachable("Don't know how to assign a value to an address yet");
}

MachineInstrBuilder &MIB;
};
} // End anonymous namespace.

/// Lower the return value for the already existing \p Ret. This assumes that
/// \p MIRBuilder's insertion point is correct.
bool ARMCallLowering::lowerReturnVal(MachineIRBuilder &MIRBuilder,
const Value *Val, unsigned VReg,
MachineInstrBuilder &Ret) const {
if (!Val)
// Nothing to do here.
return true;

auto &MF = MIRBuilder.getMF();
const auto &F = *MF.getFunction();

auto DL = MF.getDataLayout();
auto &TLI = *getTLI<ARMTargetLowering>();
if (!isSupportedType(DL, TLI, Val->getType()))
return false;

CCAssignFn *AssignFn =
TLI.CCAssignFnForReturn(F.getCallingConv(), F.isVarArg());

ArgInfo RetInfo(VReg, Val->getType());
setArgFlags(RetInfo, AttributeSet::ReturnIndex, DL, F);

FuncReturnHandler RetHandler(MIRBuilder, MF.getRegInfo(), Ret);
return handleAssignments(MIRBuilder, AssignFn, RetInfo, RetHandler);
}

bool ARMCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
const Value *Val, unsigned VReg) const {
// We're currently only handling void returns
if (Val != nullptr)
return false;
assert(!Val == !VReg && "Return value without a vreg");

AddDefaultPred(MIRBuilder.buildInstr(ARM::BX_RET));
auto Ret = AddDefaultPred(MIRBuilder.buildInstrNoInsert(ARM::BX_RET));

if (!lowerReturnVal(MIRBuilder, Val, VReg, Ret))
return false;

MIRBuilder.insertInstr(Ret);
return true;
}

namespace {
struct FormalArgHandler : public CallLowering::ValueHandler {
FormalArgHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI)
: ValueHandler(MIRBuilder, MRI) {}

unsigned getStackAddress(uint64_t Size, int64_t Offset,
MachinePointerInfo &MPO) override {
llvm_unreachable("Don't know how to get a stack address yet");
}

void assignValueToReg(unsigned ValVReg, unsigned PhysReg,
CCValAssign &VA) override {
assert(VA.isRegLoc() && "Value shouldn't be assigned to reg");
assert(VA.getLocReg() == PhysReg && "Assigning to the wrong reg?");

assert(VA.getValVT().getSizeInBits() == 32 && "Unsupported value size");
assert(VA.getLocVT().getSizeInBits() == 32 && "Unsupported location size");

MIRBuilder.getMBB().addLiveIn(PhysReg);
MIRBuilder.buildCopy(ValVReg, PhysReg);
}

void assignValueToAddress(unsigned ValVReg, unsigned Addr, uint64_t Size,
MachinePointerInfo &MPO, CCValAssign &VA) override {
llvm_unreachable("Don't know how to assign a value to an address yet");
}
};
} // End anonymous namespace

bool ARMCallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder,
const Function &F,
ArrayRef<unsigned> VRegs) const {
return F.arg_empty();
// Quick exit if there aren't any args
if (F.arg_empty())
return true;

// Stick to only 4 arguments for now
if (F.arg_size() > 4)
return false;

if (F.isVarArg())
return false;

auto DL = MIRBuilder.getMF().getDataLayout();
auto &TLI = *getTLI<ARMTargetLowering>();

auto &Args = F.getArgumentList();
for (auto &Arg : Args)
if (!isSupportedType(DL, TLI, Arg.getType()))
return false;

CCAssignFn *AssignFn =
TLI.CCAssignFnForCall(F.getCallingConv(), F.isVarArg());

SmallVector<ArgInfo, 8> ArgInfos;
unsigned Idx = 0;
for (auto &Arg : Args) {
ArgInfo AInfo(VRegs[Idx], Arg.getType());
setArgFlags(AInfo, Idx + 1, DL, F);
ArgInfos.push_back(AInfo);
Idx++;
}

FormalArgHandler ArgHandler(MIRBuilder, MIRBuilder.getMF().getRegInfo());
return handleAssignments(MIRBuilder, AssignFn, ArgInfos, ArgHandler);
}
5 changes: 5 additions & 0 deletions llvm/lib/Target/ARM/ARMCallLowering.h
Expand Up @@ -22,6 +22,7 @@
namespace llvm {

class ARMTargetLowering;
class MachineInstrBuilder;

class ARMCallLowering : public CallLowering {
public:
Expand All @@ -32,6 +33,10 @@ class ARMCallLowering : public CallLowering {

bool lowerFormalArguments(MachineIRBuilder &MIRBuilder, const Function &F,
ArrayRef<unsigned> VRegs) const override;

private:
bool lowerReturnVal(MachineIRBuilder &MIRBuilder, const Value *Val,
unsigned VReg, MachineInstrBuilder &Ret) const;
};
} // End of namespace llvm
#endif
57 changes: 54 additions & 3 deletions llvm/lib/Target/ARM/ARMInstructionSelector.cpp
Expand Up @@ -15,6 +15,7 @@
#include "ARMRegisterBankInfo.h"
#include "ARMSubtarget.h"
#include "ARMTargetMachine.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/Support/Debug.h"

#define DEBUG_TYPE "arm-isel"
Expand All @@ -28,8 +29,58 @@ using namespace llvm;
ARMInstructionSelector::ARMInstructionSelector(const ARMSubtarget &STI,
const ARMRegisterBankInfo &RBI)
: InstructionSelector(), TII(*STI.getInstrInfo()),
TRI(*STI.getRegisterInfo()) {}
TRI(*STI.getRegisterInfo()), RBI(RBI) {}

bool ARMInstructionSelector::select(llvm::MachineInstr &I) const {
return !isPreISelGenericOpcode(I.getOpcode());
static bool selectCopy(MachineInstr &I, const TargetInstrInfo &TII,
MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI,
const RegisterBankInfo &RBI) {
unsigned DstReg = I.getOperand(0).getReg();
if (TargetRegisterInfo::isPhysicalRegister(DstReg))
return true;

const RegisterBank *RegBank = RBI.getRegBank(DstReg, MRI, TRI);
assert(RegBank && "Can't get reg bank for virtual register");

const unsigned DstSize = MRI.getType(DstReg).getSizeInBits();
unsigned SrcReg = I.getOperand(1).getReg();
const unsigned SrcSize = RBI.getSizeInBits(SrcReg, MRI, TRI);
(void)SrcSize;
assert(DstSize == SrcSize && "Copy with different width?!");

assert(RegBank->getID() == ARM::GPRRegBankID && "Unsupported reg bank");
const TargetRegisterClass *RC = &ARM::GPRRegClass;

// No need to constrain SrcReg. It will get constrained when
// we hit another of its uses or its defs.
// Copies do not have constraints.
if (!RBI.constrainGenericRegister(DstReg, *RC, MRI)) {
DEBUG(dbgs() << "Failed to constrain " << TII.getName(I.getOpcode())
<< " operand\n");
return false;
}
return true;
}

bool ARMInstructionSelector::select(MachineInstr &I) const {
assert(I.getParent() && "Instruction should be in a basic block!");
assert(I.getParent()->getParent() && "Instruction should be in a function!");

auto &MBB = *I.getParent();
auto &MF = *MBB.getParent();
auto &MRI = MF.getRegInfo();

if (!isPreISelGenericOpcode(I.getOpcode())) {
if (I.isCopy())
return selectCopy(I, TII, MRI, TRI, RBI);

return true;
}

if (I.getOpcode() == TargetOpcode::G_ADD) {
I.setDesc(TII.get(ARM::ADDrr));
AddDefaultCC(AddDefaultPred(MachineInstrBuilder(MF, I)));
return constrainSelectedInstRegOperands(I, TII, TRI, RBI);
}

return false;
}
1 change: 1 addition & 0 deletions llvm/lib/Target/ARM/ARMInstructionSelector.h
Expand Up @@ -32,6 +32,7 @@ class ARMInstructionSelector : public InstructionSelector {
private:
const ARMBaseInstrInfo &TII;
const ARMBaseRegisterInfo &TRI;
const ARMRegisterBankInfo &RBI;
};

} // End llvm namespace.
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/Target/ARM/ARMLegalizerInfo.cpp
Expand Up @@ -24,5 +24,10 @@ using namespace llvm;
#endif

ARMLegalizerInfo::ARMLegalizerInfo() {
using namespace TargetOpcode;
const LLT s32 = LLT::scalar(32);

setAction({G_ADD, s32}, Legal);

computeTables();
}
89 changes: 88 additions & 1 deletion llvm/lib/Target/ARM/ARMRegisterBankInfo.cpp
Expand Up @@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//

#include "ARMRegisterBankInfo.h"
#include "ARMInstrInfo.h" // For the register classes
#include "llvm/CodeGen/GlobalISel/RegisterBank.h"
#include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
Expand All @@ -23,5 +24,91 @@ using namespace llvm;
#error "You shouldn't build this"
#endif

// FIXME: TableGen this.
// If it grows too much and TableGen still isn't ready to do the job, extract it
// into an ARMGenRegisterBankInfo.def (similar to AArch64).
namespace llvm {
namespace ARM {
RegisterBank GPRRegBank;
RegisterBank *RegBanks[] = {&GPRRegBank};

RegisterBankInfo::PartialMapping GPRPartialMapping{0, 32, GPRRegBank};

RegisterBankInfo::ValueMapping ValueMappings[] = {
{&GPRPartialMapping, 1}, {&GPRPartialMapping, 1}, {&GPRPartialMapping, 1}};
} // end namespace arm
} // end namespace llvm

ARMRegisterBankInfo::ARMRegisterBankInfo(const TargetRegisterInfo &TRI)
: RegisterBankInfo(nullptr, 0) {}
: RegisterBankInfo(ARM::RegBanks, ARM::NumRegisterBanks) {
static bool AlreadyInit = false;
// We have only one set of register banks, whatever the subtarget
// is. Therefore, the initialization of the RegBanks table should be
// done only once. Indeed the table of all register banks
// (ARM::RegBanks) is unique in the compiler. At some point, it
// will get tablegen'ed and the whole constructor becomes empty.
if (AlreadyInit)
return;
AlreadyInit = true;

// Initialize the GPR bank.
createRegisterBank(ARM::GPRRegBankID, "GPRB");

addRegBankCoverage(ARM::GPRRegBankID, ARM::GPRRegClassID, TRI);
addRegBankCoverage(ARM::GPRRegBankID, ARM::GPRwithAPSRRegClassID, TRI);
const RegisterBank &RBGPR = getRegBank(ARM::GPRRegBankID);
(void)RBGPR;
assert(&ARM::GPRRegBank == &RBGPR && "The order in RegBanks is messed up");
assert(RBGPR.covers(*TRI.getRegClass(ARM::GPRRegClassID)) &&
"Subclass not added?");
assert(RBGPR.covers(*TRI.getRegClass(ARM::GPRwithAPSRRegClassID)) &&
"Subclass not added?");
assert(RBGPR.covers(*TRI.getRegClass(ARM::GPRnopcRegClassID)) &&
"Subclass not added?");
assert(RBGPR.covers(*TRI.getRegClass(ARM::rGPRRegClassID)) &&
"Subclass not added?");
assert(RBGPR.covers(*TRI.getRegClass(ARM::tGPRRegClassID)) &&
"Subclass not added?");
assert(RBGPR.covers(*TRI.getRegClass(ARM::tcGPRRegClassID)) &&
"Subclass not added?");
assert(RBGPR.covers(*TRI.getRegClass(ARM::tGPR_and_tcGPRRegClassID)) &&
"Subclass not added?");
assert(RBGPR.getSize() == 32 && "GPRs should hold up to 32-bit");
}

const RegisterBank &ARMRegisterBankInfo::getRegBankFromRegClass(
const TargetRegisterClass &RC) const {
using namespace ARM;

switch (RC.getID()) {
case GPRRegClassID:
case tGPR_and_tcGPRRegClassID:
return getRegBank(ARM::GPRRegBankID);
default:
llvm_unreachable("Unsupported register kind");
}

llvm_unreachable("Switch should handle all register classes");
}

RegisterBankInfo::InstructionMapping
ARMRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
auto Opc = MI.getOpcode();

// Try the default logic for non-generic instructions that are either copies
// or already have some operands assigned to banks.
if (!isPreISelGenericOpcode(Opc)) {
InstructionMapping Mapping = getInstrMappingImpl(MI);
if (Mapping.isValid())
return Mapping;
}

if (Opc == TargetOpcode::G_ADD) {
unsigned NumOperands = MI.getNumOperands();
ValueMapping *OperandsMapping = &ARM::ValueMappings[0];
return InstructionMapping{DefaultMappingID, /*Cost=*/1, OperandsMapping,
NumOperands};
}

return InstructionMapping{};
}
12 changes: 12 additions & 0 deletions llvm/lib/Target/ARM/ARMRegisterBankInfo.h
Expand Up @@ -20,10 +20,22 @@ namespace llvm {

class TargetRegisterInfo;

namespace ARM {
enum {
GPRRegBankID = 0, // General purpose registers
NumRegisterBanks,
};
} // end namespace ARM

/// This class provides the information for the target register banks.
class ARMRegisterBankInfo final : public RegisterBankInfo {
public:
ARMRegisterBankInfo(const TargetRegisterInfo &TRI);

const RegisterBank &
getRegBankFromRegClass(const TargetRegisterClass &RC) const override;

InstructionMapping getInstrMapping(const MachineInstr &MI) const override;
};
} // End llvm namespace.
#endif

0 comments on commit 812caee

Please sign in to comment.