5 changes: 5 additions & 0 deletions llvm/lib/CodeGen/LLVMTargetMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
14 changes: 14 additions & 0 deletions llvm/lib/CodeGen/MachineRegisterInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
162 changes: 162 additions & 0 deletions llvm/lib/Target/AArch64/AArch64InstructionSelector.cpp
Original file line number Diff line number Diff line change
@@ -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 changes: 39 additions & 0 deletions llvm/lib/Target/AArch64/AArch64InstructionSelector.h
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions llvm/lib/Target/AArch64/AArch64Subtarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Target/AArch64/AArch64Subtarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ class AArch64Subtarget final : public AArch64GenSubtargetInfo {
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; }
Expand Down
22 changes: 20 additions & 2 deletions llvm/lib/Target/AArch64/AArch64TargetMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -286,6 +299,7 @@ class AArch64PassConfig : public TargetPassConfig {
bool addIRTranslator() override;
bool addLegalizeMachineIR() override;
bool addRegBankSelect() override;
bool addGlobalInstructionSelect() override;
#endif
bool addILPOpts() override;
void addPreRegAlloc() override;
Expand Down Expand Up @@ -391,6 +405,10 @@ bool AArch64PassConfig::addRegBankSelect() {
addPass(new RegBankSelect());
return false;
}
bool AArch64PassConfig::addGlobalInstructionSelect() {
addPass(new InstructionSelect());
return false;
}
#endif

bool AArch64PassConfig::addILPOpts() {
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Target/AArch64/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/Target/AMDGPU/AMDGPUTargetMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ class GCNPassConfig final : public AMDGPUPassConfig {
bool addIRTranslator() override;
bool addLegalizeMachineIR() override;
bool addRegBankSelect() override;
bool addGlobalInstructionSelect() override;
#endif
void addFastRegAlloc(FunctionPass *RegAllocPass) override;
void addOptimizedRegAlloc(FunctionPass *RegAllocPass) override;
Expand Down Expand Up @@ -528,6 +529,10 @@ bool GCNPassConfig::addLegalizeMachineIR() {
bool GCNPassConfig::addRegBankSelect() {
return false;
}

bool GCNPassConfig::addGlobalInstructionSelect() {
return false;
}
#endif

void GCNPassConfig::addPreRegAlloc() {
Expand Down
118 changes: 118 additions & 0 deletions llvm/test/CodeGen/AArch64/GlobalISel/arm64-instructionselect.mir
Original file line number Diff line number Diff line change
@@ -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
...