Skip to content

Commit 812caee

Browse files
committedDec 16, 2016
[ARM] GlobalISel: Select add i32, i32
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
1 parent 85c9211 commit 812caee

13 files changed

+429
-9
lines changed
 

‎llvm/lib/CodeGen/GlobalISel/InstructionSelector.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ bool InstructionSelector::constrainSelectedInstRegOperands(
4343
if (TRI.isPhysicalRegister(MO.getReg()))
4444
continue;
4545

46+
// Register operands with a value of 0 (e.g. predicate operands) don't need
47+
// to be constrained.
48+
if (MO.getReg() == 0)
49+
continue;
50+
4651
const TargetRegisterClass *RC = TII.getRegClass(I.getDesc(), OpI, &TRI, MF);
4752
assert(RC && "Selected inst should have regclass operand");
4853

‎llvm/lib/Target/ARM/ARMCallLowering.cpp

+134-5
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,148 @@ using namespace llvm;
2929
ARMCallLowering::ARMCallLowering(const ARMTargetLowering &TLI)
3030
: CallLowering(&TLI) {}
3131

32+
static bool isSupportedType(const DataLayout DL, const ARMTargetLowering &TLI,
33+
Type *T) {
34+
EVT VT = TLI.getValueType(DL, T);
35+
return VT.isSimple() && VT.isInteger() &&
36+
VT.getSimpleVT().getSizeInBits() == 32;
37+
}
38+
39+
namespace {
40+
struct FuncReturnHandler : public CallLowering::ValueHandler {
41+
FuncReturnHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI,
42+
MachineInstrBuilder &MIB)
43+
: ValueHandler(MIRBuilder, MRI), MIB(MIB) {}
44+
45+
unsigned getStackAddress(uint64_t Size, int64_t Offset,
46+
MachinePointerInfo &MPO) override {
47+
llvm_unreachable("Don't know how to get a stack address yet");
48+
}
49+
50+
void assignValueToReg(unsigned ValVReg, unsigned PhysReg,
51+
CCValAssign &VA) override {
52+
assert(VA.isRegLoc() && "Value shouldn't be assigned to reg");
53+
assert(VA.getLocReg() == PhysReg && "Assigning to the wrong reg?");
54+
55+
assert(VA.getValVT().getSizeInBits() == 32 && "Unsupported value size");
56+
assert(VA.getLocVT().getSizeInBits() == 32 && "Unsupported location size");
57+
58+
MIRBuilder.buildCopy(PhysReg, ValVReg);
59+
MIB.addUse(PhysReg, RegState::Implicit);
60+
}
61+
62+
void assignValueToAddress(unsigned ValVReg, unsigned Addr, uint64_t Size,
63+
MachinePointerInfo &MPO, CCValAssign &VA) override {
64+
llvm_unreachable("Don't know how to assign a value to an address yet");
65+
}
66+
67+
MachineInstrBuilder &MIB;
68+
};
69+
} // End anonymous namespace.
70+
71+
/// Lower the return value for the already existing \p Ret. This assumes that
72+
/// \p MIRBuilder's insertion point is correct.
73+
bool ARMCallLowering::lowerReturnVal(MachineIRBuilder &MIRBuilder,
74+
const Value *Val, unsigned VReg,
75+
MachineInstrBuilder &Ret) const {
76+
if (!Val)
77+
// Nothing to do here.
78+
return true;
79+
80+
auto &MF = MIRBuilder.getMF();
81+
const auto &F = *MF.getFunction();
82+
83+
auto DL = MF.getDataLayout();
84+
auto &TLI = *getTLI<ARMTargetLowering>();
85+
if (!isSupportedType(DL, TLI, Val->getType()))
86+
return false;
87+
88+
CCAssignFn *AssignFn =
89+
TLI.CCAssignFnForReturn(F.getCallingConv(), F.isVarArg());
90+
91+
ArgInfo RetInfo(VReg, Val->getType());
92+
setArgFlags(RetInfo, AttributeSet::ReturnIndex, DL, F);
93+
94+
FuncReturnHandler RetHandler(MIRBuilder, MF.getRegInfo(), Ret);
95+
return handleAssignments(MIRBuilder, AssignFn, RetInfo, RetHandler);
96+
}
97+
3298
bool ARMCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
3399
const Value *Val, unsigned VReg) const {
34-
// We're currently only handling void returns
35-
if (Val != nullptr)
36-
return false;
100+
assert(!Val == !VReg && "Return value without a vreg");
37101

38-
AddDefaultPred(MIRBuilder.buildInstr(ARM::BX_RET));
102+
auto Ret = AddDefaultPred(MIRBuilder.buildInstrNoInsert(ARM::BX_RET));
39103

104+
if (!lowerReturnVal(MIRBuilder, Val, VReg, Ret))
105+
return false;
106+
107+
MIRBuilder.insertInstr(Ret);
40108
return true;
41109
}
42110

111+
namespace {
112+
struct FormalArgHandler : public CallLowering::ValueHandler {
113+
FormalArgHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI)
114+
: ValueHandler(MIRBuilder, MRI) {}
115+
116+
unsigned getStackAddress(uint64_t Size, int64_t Offset,
117+
MachinePointerInfo &MPO) override {
118+
llvm_unreachable("Don't know how to get a stack address yet");
119+
}
120+
121+
void assignValueToReg(unsigned ValVReg, unsigned PhysReg,
122+
CCValAssign &VA) override {
123+
assert(VA.isRegLoc() && "Value shouldn't be assigned to reg");
124+
assert(VA.getLocReg() == PhysReg && "Assigning to the wrong reg?");
125+
126+
assert(VA.getValVT().getSizeInBits() == 32 && "Unsupported value size");
127+
assert(VA.getLocVT().getSizeInBits() == 32 && "Unsupported location size");
128+
129+
MIRBuilder.getMBB().addLiveIn(PhysReg);
130+
MIRBuilder.buildCopy(ValVReg, PhysReg);
131+
}
132+
133+
void assignValueToAddress(unsigned ValVReg, unsigned Addr, uint64_t Size,
134+
MachinePointerInfo &MPO, CCValAssign &VA) override {
135+
llvm_unreachable("Don't know how to assign a value to an address yet");
136+
}
137+
};
138+
} // End anonymous namespace
139+
43140
bool ARMCallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder,
44141
const Function &F,
45142
ArrayRef<unsigned> VRegs) const {
46-
return F.arg_empty();
143+
// Quick exit if there aren't any args
144+
if (F.arg_empty())
145+
return true;
146+
147+
// Stick to only 4 arguments for now
148+
if (F.arg_size() > 4)
149+
return false;
150+
151+
if (F.isVarArg())
152+
return false;
153+
154+
auto DL = MIRBuilder.getMF().getDataLayout();
155+
auto &TLI = *getTLI<ARMTargetLowering>();
156+
157+
auto &Args = F.getArgumentList();
158+
for (auto &Arg : Args)
159+
if (!isSupportedType(DL, TLI, Arg.getType()))
160+
return false;
161+
162+
CCAssignFn *AssignFn =
163+
TLI.CCAssignFnForCall(F.getCallingConv(), F.isVarArg());
164+
165+
SmallVector<ArgInfo, 8> ArgInfos;
166+
unsigned Idx = 0;
167+
for (auto &Arg : Args) {
168+
ArgInfo AInfo(VRegs[Idx], Arg.getType());
169+
setArgFlags(AInfo, Idx + 1, DL, F);
170+
ArgInfos.push_back(AInfo);
171+
Idx++;
172+
}
173+
174+
FormalArgHandler ArgHandler(MIRBuilder, MIRBuilder.getMF().getRegInfo());
175+
return handleAssignments(MIRBuilder, AssignFn, ArgInfos, ArgHandler);
47176
}

‎llvm/lib/Target/ARM/ARMCallLowering.h

+5
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
namespace llvm {
2323

2424
class ARMTargetLowering;
25+
class MachineInstrBuilder;
2526

2627
class ARMCallLowering : public CallLowering {
2728
public:
@@ -32,6 +33,10 @@ class ARMCallLowering : public CallLowering {
3233

3334
bool lowerFormalArguments(MachineIRBuilder &MIRBuilder, const Function &F,
3435
ArrayRef<unsigned> VRegs) const override;
36+
37+
private:
38+
bool lowerReturnVal(MachineIRBuilder &MIRBuilder, const Value *Val,
39+
unsigned VReg, MachineInstrBuilder &Ret) const;
3540
};
3641
} // End of namespace llvm
3742
#endif

‎llvm/lib/Target/ARM/ARMInstructionSelector.cpp

+54-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "ARMRegisterBankInfo.h"
1616
#include "ARMSubtarget.h"
1717
#include "ARMTargetMachine.h"
18+
#include "llvm/CodeGen/MachineRegisterInfo.h"
1819
#include "llvm/Support/Debug.h"
1920

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

33-
bool ARMInstructionSelector::select(llvm::MachineInstr &I) const {
34-
return !isPreISelGenericOpcode(I.getOpcode());
34+
static bool selectCopy(MachineInstr &I, const TargetInstrInfo &TII,
35+
MachineRegisterInfo &MRI, const TargetRegisterInfo &TRI,
36+
const RegisterBankInfo &RBI) {
37+
unsigned DstReg = I.getOperand(0).getReg();
38+
if (TargetRegisterInfo::isPhysicalRegister(DstReg))
39+
return true;
40+
41+
const RegisterBank *RegBank = RBI.getRegBank(DstReg, MRI, TRI);
42+
assert(RegBank && "Can't get reg bank for virtual register");
43+
44+
const unsigned DstSize = MRI.getType(DstReg).getSizeInBits();
45+
unsigned SrcReg = I.getOperand(1).getReg();
46+
const unsigned SrcSize = RBI.getSizeInBits(SrcReg, MRI, TRI);
47+
(void)SrcSize;
48+
assert(DstSize == SrcSize && "Copy with different width?!");
49+
50+
assert(RegBank->getID() == ARM::GPRRegBankID && "Unsupported reg bank");
51+
const TargetRegisterClass *RC = &ARM::GPRRegClass;
52+
53+
// No need to constrain SrcReg. It will get constrained when
54+
// we hit another of its uses or its defs.
55+
// Copies do not have constraints.
56+
if (!RBI.constrainGenericRegister(DstReg, *RC, MRI)) {
57+
DEBUG(dbgs() << "Failed to constrain " << TII.getName(I.getOpcode())
58+
<< " operand\n");
59+
return false;
60+
}
61+
return true;
62+
}
63+
64+
bool ARMInstructionSelector::select(MachineInstr &I) const {
65+
assert(I.getParent() && "Instruction should be in a basic block!");
66+
assert(I.getParent()->getParent() && "Instruction should be in a function!");
67+
68+
auto &MBB = *I.getParent();
69+
auto &MF = *MBB.getParent();
70+
auto &MRI = MF.getRegInfo();
71+
72+
if (!isPreISelGenericOpcode(I.getOpcode())) {
73+
if (I.isCopy())
74+
return selectCopy(I, TII, MRI, TRI, RBI);
75+
76+
return true;
77+
}
78+
79+
if (I.getOpcode() == TargetOpcode::G_ADD) {
80+
I.setDesc(TII.get(ARM::ADDrr));
81+
AddDefaultCC(AddDefaultPred(MachineInstrBuilder(MF, I)));
82+
return constrainSelectedInstRegOperands(I, TII, TRI, RBI);
83+
}
84+
85+
return false;
3586
}

‎llvm/lib/Target/ARM/ARMInstructionSelector.h

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class ARMInstructionSelector : public InstructionSelector {
3232
private:
3333
const ARMBaseInstrInfo &TII;
3434
const ARMBaseRegisterInfo &TRI;
35+
const ARMRegisterBankInfo &RBI;
3536
};
3637

3738
} // End llvm namespace.

‎llvm/lib/Target/ARM/ARMLegalizerInfo.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,10 @@ using namespace llvm;
2424
#endif
2525

2626
ARMLegalizerInfo::ARMLegalizerInfo() {
27+
using namespace TargetOpcode;
28+
const LLT s32 = LLT::scalar(32);
29+
30+
setAction({G_ADD, s32}, Legal);
31+
2732
computeTables();
2833
}

‎llvm/lib/Target/ARM/ARMRegisterBankInfo.cpp

+88-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//===----------------------------------------------------------------------===//
1313

1414
#include "ARMRegisterBankInfo.h"
15+
#include "ARMInstrInfo.h" // For the register classes
1516
#include "llvm/CodeGen/GlobalISel/RegisterBank.h"
1617
#include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h"
1718
#include "llvm/CodeGen/MachineRegisterInfo.h"
@@ -23,5 +24,91 @@ using namespace llvm;
2324
#error "You shouldn't build this"
2425
#endif
2526

27+
// FIXME: TableGen this.
28+
// If it grows too much and TableGen still isn't ready to do the job, extract it
29+
// into an ARMGenRegisterBankInfo.def (similar to AArch64).
30+
namespace llvm {
31+
namespace ARM {
32+
RegisterBank GPRRegBank;
33+
RegisterBank *RegBanks[] = {&GPRRegBank};
34+
35+
RegisterBankInfo::PartialMapping GPRPartialMapping{0, 32, GPRRegBank};
36+
37+
RegisterBankInfo::ValueMapping ValueMappings[] = {
38+
{&GPRPartialMapping, 1}, {&GPRPartialMapping, 1}, {&GPRPartialMapping, 1}};
39+
} // end namespace arm
40+
} // end namespace llvm
41+
2642
ARMRegisterBankInfo::ARMRegisterBankInfo(const TargetRegisterInfo &TRI)
27-
: RegisterBankInfo(nullptr, 0) {}
43+
: RegisterBankInfo(ARM::RegBanks, ARM::NumRegisterBanks) {
44+
static bool AlreadyInit = false;
45+
// We have only one set of register banks, whatever the subtarget
46+
// is. Therefore, the initialization of the RegBanks table should be
47+
// done only once. Indeed the table of all register banks
48+
// (ARM::RegBanks) is unique in the compiler. At some point, it
49+
// will get tablegen'ed and the whole constructor becomes empty.
50+
if (AlreadyInit)
51+
return;
52+
AlreadyInit = true;
53+
54+
// Initialize the GPR bank.
55+
createRegisterBank(ARM::GPRRegBankID, "GPRB");
56+
57+
addRegBankCoverage(ARM::GPRRegBankID, ARM::GPRRegClassID, TRI);
58+
addRegBankCoverage(ARM::GPRRegBankID, ARM::GPRwithAPSRRegClassID, TRI);
59+
const RegisterBank &RBGPR = getRegBank(ARM::GPRRegBankID);
60+
(void)RBGPR;
61+
assert(&ARM::GPRRegBank == &RBGPR && "The order in RegBanks is messed up");
62+
assert(RBGPR.covers(*TRI.getRegClass(ARM::GPRRegClassID)) &&
63+
"Subclass not added?");
64+
assert(RBGPR.covers(*TRI.getRegClass(ARM::GPRwithAPSRRegClassID)) &&
65+
"Subclass not added?");
66+
assert(RBGPR.covers(*TRI.getRegClass(ARM::GPRnopcRegClassID)) &&
67+
"Subclass not added?");
68+
assert(RBGPR.covers(*TRI.getRegClass(ARM::rGPRRegClassID)) &&
69+
"Subclass not added?");
70+
assert(RBGPR.covers(*TRI.getRegClass(ARM::tGPRRegClassID)) &&
71+
"Subclass not added?");
72+
assert(RBGPR.covers(*TRI.getRegClass(ARM::tcGPRRegClassID)) &&
73+
"Subclass not added?");
74+
assert(RBGPR.covers(*TRI.getRegClass(ARM::tGPR_and_tcGPRRegClassID)) &&
75+
"Subclass not added?");
76+
assert(RBGPR.getSize() == 32 && "GPRs should hold up to 32-bit");
77+
}
78+
79+
const RegisterBank &ARMRegisterBankInfo::getRegBankFromRegClass(
80+
const TargetRegisterClass &RC) const {
81+
using namespace ARM;
82+
83+
switch (RC.getID()) {
84+
case GPRRegClassID:
85+
case tGPR_and_tcGPRRegClassID:
86+
return getRegBank(ARM::GPRRegBankID);
87+
default:
88+
llvm_unreachable("Unsupported register kind");
89+
}
90+
91+
llvm_unreachable("Switch should handle all register classes");
92+
}
93+
94+
RegisterBankInfo::InstructionMapping
95+
ARMRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
96+
auto Opc = MI.getOpcode();
97+
98+
// Try the default logic for non-generic instructions that are either copies
99+
// or already have some operands assigned to banks.
100+
if (!isPreISelGenericOpcode(Opc)) {
101+
InstructionMapping Mapping = getInstrMappingImpl(MI);
102+
if (Mapping.isValid())
103+
return Mapping;
104+
}
105+
106+
if (Opc == TargetOpcode::G_ADD) {
107+
unsigned NumOperands = MI.getNumOperands();
108+
ValueMapping *OperandsMapping = &ARM::ValueMappings[0];
109+
return InstructionMapping{DefaultMappingID, /*Cost=*/1, OperandsMapping,
110+
NumOperands};
111+
}
112+
113+
return InstructionMapping{};
114+
}

‎llvm/lib/Target/ARM/ARMRegisterBankInfo.h

+12
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,22 @@ namespace llvm {
2020

2121
class TargetRegisterInfo;
2222

23+
namespace ARM {
24+
enum {
25+
GPRRegBankID = 0, // General purpose registers
26+
NumRegisterBanks,
27+
};
28+
} // end namespace ARM
29+
2330
/// This class provides the information for the target register banks.
2431
class ARMRegisterBankInfo final : public RegisterBankInfo {
2532
public:
2633
ARMRegisterBankInfo(const TargetRegisterInfo &TRI);
34+
35+
const RegisterBank &
36+
getRegBankFromRegClass(const TargetRegisterClass &RC) const override;
37+
38+
InstructionMapping getInstrMapping(const MachineInstr &MI) const override;
2739
};
2840
} // End llvm namespace.
2941
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# RUN: llc -O0 -mtriple arm-- -global-isel -run-pass=instruction-select -verify-machineinstrs %s -o - | FileCheck %s
2+
--- |
3+
define void @test_adds32() { ret void }
4+
...
5+
---
6+
name: test_adds32
7+
# CHECK-LABEL: name: test_adds32
8+
legalized: true
9+
regBankSelected: true
10+
selected: false
11+
# CHECK: selected: true
12+
registers:
13+
- { id: 0, class: gprb }
14+
- { id: 1, class: gprb }
15+
- { id: 2, class: gprb }
16+
#CHECK: id: 0, class: gpr
17+
#CHECK: id: 1, class: gpr
18+
#CHECK: id: 2, class: gpr
19+
body: |
20+
bb.0:
21+
liveins: %r0, %r1
22+
23+
%0(s32) = COPY %r0
24+
; CHECK: [[VREGX:%[0-9]+]] = COPY %r0
25+
26+
%1(s32) = COPY %r1
27+
; CHECK: [[VREGY:%[0-9]+]] = COPY %r1
28+
29+
%2(s32) = G_ADD %0, %1
30+
; CHECK: [[VREGSUM:%[0-9]+]] = ADDrr [[VREGX]], [[VREGY]], 14, _, _
31+
32+
%r0 = COPY %2(s32)
33+
; CHECK: %r0 = COPY [[VREGSUM]]
34+
35+
BX_RET 14, _, implicit %r0
36+
; CHECK: BX_RET 14, _, implicit %r0
37+
...

‎llvm/test/CodeGen/ARM/GlobalISel/arm-irtranslator.ll

+12
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,15 @@ entry:
77
ret void
88
}
99

10+
define i32 @test_add(i32 %x, i32 %y) {
11+
; CHECK-LABEL: name: test_add
12+
; CHECK: liveins: %r0, %r1
13+
; CHECK: [[VREGX:%[0-9]+]]{{.*}} = COPY %r0
14+
; CHECK: [[VREGY:%[0-9]+]]{{.*}} = COPY %r1
15+
; CHECK: [[SUM:%[0-9]+]]{{.*}} = G_ADD [[VREGX]], [[VREGY]]
16+
; CHECK: %r0 = COPY [[SUM]]
17+
; CHECK: BX_RET 14, _, implicit %r0
18+
entry:
19+
%sum = add i32 %x, %y
20+
ret i32 %sum
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
; RUN: llc -mtriple arm-unknown -global-isel %s -o - | FileCheck %s
2+
3+
define void @test_void_return() {
4+
; CHECK-LABEL: test_void_return:
5+
; CHECK: bx lr
6+
entry:
7+
ret void
8+
}
9+
10+
define i32 @test_add(i32 %x, i32 %y) {
11+
; CHECK-LABEL: test_add:
12+
; CHECK: add r0, r0, r1
13+
; CHECK: bx lr
14+
entry:
15+
%sum = add i32 %x, %y
16+
ret i32 %sum
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# RUN: llc -mtriple arm-- -global-isel -run-pass=legalizer %s -o - | FileCheck %s
2+
--- |
3+
define void @test_add_s32() { ret void }
4+
...
5+
---
6+
name: test_add_s32
7+
# CHECK-LABEL: name: test_add_s32
8+
legalized: false
9+
# CHECK: legalized: true
10+
regBankSelected: false
11+
selected: false
12+
tracksRegLiveness: true
13+
registers:
14+
- { id: 0, class: _ }
15+
- { id: 1, class: _ }
16+
- { id: 2, class: _ }
17+
body: |
18+
bb.0:
19+
liveins: %r0, %r1
20+
21+
%0(s32) = COPY %r0
22+
%1(s32) = COPY %r1
23+
%2(s32) = G_ADD %0, %1
24+
; G_ADD with s32 is legal, so we should find it unchanged in the output
25+
; CHECK: {{%[0-9]+}}(s32) = G_ADD {{%[0-9]+, %[0-9]+}}
26+
%r0 = COPY %2(s32)
27+
BX_RET 14, _, implicit %r0
28+
29+
...
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# RUN: llc -mtriple arm-- -global-isel -run-pass=regbankselect %s -o - | FileCheck %s
2+
--- |
3+
define void @test_add_s32() { ret void }
4+
...
5+
---
6+
name: test_add_s32
7+
# CHECK-LABEL: name: test_add_s32
8+
legalized: true
9+
regBankSelected: false
10+
selected: false
11+
# CHECK: registers:
12+
# CHECK: - { id: 0, class: gprb }
13+
# CHECK: - { id: 1, class: gprb }
14+
# CHECK: - { id: 2, class: gprb }
15+
16+
registers:
17+
- { id: 0, class: _ }
18+
- { id: 1, class: _ }
19+
- { id: 2, class: _ }
20+
body: |
21+
bb.0:
22+
liveins: %r0, %r1
23+
24+
%0(s32) = COPY %r0
25+
%1(s32) = COPY %r1
26+
%2(s32) = G_ADD %0, %1
27+
%r0 = COPY %2(s32)
28+
BX_RET 14, _, implicit %r0
29+
30+
...

0 commit comments

Comments
 (0)
Please sign in to comment.