[ARM] GlobalISel: Support i8/i16 ABI extensions
At the moment, this means supporting the signext/zeroext attribute on the return type of the function. For function arguments, signext/zeroext should be handled by the caller, so there's nothing for us to do until we start lowering calls. Note that this does not include support for other extensions (i8 to i16), those will be added later. Differential Revision: https://reviews.llvm.org/D27705 llvm-svn: 293034
This commit is contained in:
parent
43a7759f4b
commit
8b6c6bedcb
|
@ -60,11 +60,8 @@ struct FuncReturnHandler : public CallLowering::ValueHandler {
|
||||||
assert(VA.getValVT().getSizeInBits() <= 32 && "Unsupported value size");
|
assert(VA.getValVT().getSizeInBits() <= 32 && "Unsupported value size");
|
||||||
assert(VA.getLocVT().getSizeInBits() == 32 && "Unsupported location size");
|
assert(VA.getLocVT().getSizeInBits() == 32 && "Unsupported location size");
|
||||||
|
|
||||||
assert(VA.getLocInfo() != CCValAssign::SExt &&
|
unsigned ExtReg = extendRegister(ValVReg, VA);
|
||||||
VA.getLocInfo() != CCValAssign::ZExt &&
|
MIRBuilder.buildCopy(PhysReg, ExtReg);
|
||||||
"ABI extensions not supported yet");
|
|
||||||
|
|
||||||
MIRBuilder.buildCopy(PhysReg, ValVReg);
|
|
||||||
MIB.addUse(PhysReg, RegState::Implicit);
|
MIB.addUse(PhysReg, RegState::Implicit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,6 +153,7 @@ struct FormalArgHandler : public CallLowering::ValueHandler {
|
||||||
assert(VA.getValVT().getSizeInBits() <= 32 && "Unsupported value size");
|
assert(VA.getValVT().getSizeInBits() <= 32 && "Unsupported value size");
|
||||||
assert(VA.getLocVT().getSizeInBits() == 32 && "Unsupported location size");
|
assert(VA.getLocVT().getSizeInBits() == 32 && "Unsupported location size");
|
||||||
|
|
||||||
|
// The caller should handle all necesary extensions.
|
||||||
MIRBuilder.getMBB().addLiveIn(PhysReg);
|
MIRBuilder.getMBB().addLiveIn(PhysReg);
|
||||||
MIRBuilder.buildCopy(ValVReg, PhysReg);
|
MIRBuilder.buildCopy(ValVReg, PhysReg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,23 @@ static bool selectCopy(MachineInstr &I, const TargetInstrInfo &TII,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Select the opcode for simple extensions (that translate to a single SXT/UXT
|
||||||
|
/// instruction). Extension operations more complicated than that should not
|
||||||
|
/// invoke this.
|
||||||
|
static unsigned selectSimpleExtOpc(unsigned Opc, unsigned Size) {
|
||||||
|
using namespace TargetOpcode;
|
||||||
|
|
||||||
|
assert((Size == 8 || Size == 16) && "Unsupported size");
|
||||||
|
|
||||||
|
if (Opc == G_SEXT)
|
||||||
|
return Size == 8 ? ARM::SXTB : ARM::SXTH;
|
||||||
|
|
||||||
|
if (Opc == G_ZEXT)
|
||||||
|
return Size == 8 ? ARM::UXTB : ARM::UXTH;
|
||||||
|
|
||||||
|
llvm_unreachable("Unsupported opcode");
|
||||||
|
}
|
||||||
|
|
||||||
bool ARMInstructionSelector::select(MachineInstr &I) const {
|
bool ARMInstructionSelector::select(MachineInstr &I) const {
|
||||||
assert(I.getParent() && "Instruction should be in a basic block!");
|
assert(I.getParent() && "Instruction should be in a basic block!");
|
||||||
assert(I.getParent()->getParent() && "Instruction should be in a function!");
|
assert(I.getParent()->getParent() && "Instruction should be in a function!");
|
||||||
|
@ -87,6 +104,31 @@ bool ARMInstructionSelector::select(MachineInstr &I) const {
|
||||||
|
|
||||||
using namespace TargetOpcode;
|
using namespace TargetOpcode;
|
||||||
switch (I.getOpcode()) {
|
switch (I.getOpcode()) {
|
||||||
|
case G_SEXT:
|
||||||
|
case G_ZEXT: {
|
||||||
|
LLT DstTy = MRI.getType(I.getOperand(0).getReg());
|
||||||
|
// FIXME: Smaller destination sizes coming soon!
|
||||||
|
if (DstTy.getSizeInBits() != 32) {
|
||||||
|
DEBUG(dbgs() << "Unsupported destination size for extension");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LLT SrcTy = MRI.getType(I.getOperand(1).getReg());
|
||||||
|
unsigned SrcSize = SrcTy.getSizeInBits();
|
||||||
|
switch (SrcSize) {
|
||||||
|
case 8:
|
||||||
|
case 16: {
|
||||||
|
unsigned NewOpc = selectSimpleExtOpc(I.getOpcode(), SrcSize);
|
||||||
|
I.setDesc(TII.get(NewOpc));
|
||||||
|
MIB.addImm(0).add(predOps(ARMCC::AL));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
DEBUG(dbgs() << "Unsupported source size for extension");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case G_ADD:
|
case G_ADD:
|
||||||
I.setDesc(TII.get(ARM::ADDrr));
|
I.setDesc(TII.get(ARM::ADDrr));
|
||||||
MIB.add(predOps(ARMCC::AL)).add(condCodeOp());
|
MIB.add(predOps(ARMCC::AL)).add(condCodeOp());
|
||||||
|
|
|
@ -40,5 +40,11 @@ ARMLegalizerInfo::ARMLegalizerInfo() {
|
||||||
for (auto Ty : {s8, s16, s32})
|
for (auto Ty : {s8, s16, s32})
|
||||||
setAction({G_ADD, Ty}, Legal);
|
setAction({G_ADD, Ty}, Legal);
|
||||||
|
|
||||||
|
for (auto Op : {G_SEXT, G_ZEXT}) {
|
||||||
|
setAction({Op, s32}, Legal);
|
||||||
|
for (auto Ty : {s8, s16})
|
||||||
|
setAction({Op, 1, Ty}, Legal);
|
||||||
|
}
|
||||||
|
|
||||||
computeTables();
|
computeTables();
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,6 +107,7 @@ const RegisterBank &ARMRegisterBankInfo::getRegBankFromRegClass(
|
||||||
|
|
||||||
switch (RC.getID()) {
|
switch (RC.getID()) {
|
||||||
case GPRRegClassID:
|
case GPRRegClassID:
|
||||||
|
case GPRnopcRegClassID:
|
||||||
case tGPR_and_tcGPRRegClassID:
|
case tGPR_and_tcGPRRegClassID:
|
||||||
return getRegBank(ARM::GPRRegBankID);
|
return getRegBank(ARM::GPRRegBankID);
|
||||||
default:
|
default:
|
||||||
|
@ -136,6 +137,8 @@ ARMRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
|
||||||
switch (Opc) {
|
switch (Opc) {
|
||||||
case G_ADD:
|
case G_ADD:
|
||||||
case G_LOAD:
|
case G_LOAD:
|
||||||
|
case G_SEXT:
|
||||||
|
case G_ZEXT:
|
||||||
// FIXME: We're abusing the fact that everything lives in a GPR for now; in
|
// FIXME: We're abusing the fact that everything lives in a GPR for now; in
|
||||||
// the real world we would use different mappings.
|
// the real world we would use different mappings.
|
||||||
OperandsMapping = &ARM::ValueMappings[0];
|
OperandsMapping = &ARM::ValueMappings[0];
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
# RUN: llc -O0 -mtriple arm-- -global-isel -run-pass=instruction-select -verify-machineinstrs %s -o - | FileCheck %s
|
# RUN: llc -O0 -mtriple arm-- -global-isel -run-pass=instruction-select -verify-machineinstrs %s -o - | FileCheck %s
|
||||||
--- |
|
--- |
|
||||||
|
define void @test_sext_s8() { ret void }
|
||||||
|
define void @test_zext_s16() { ret void }
|
||||||
|
|
||||||
define void @test_add_s8() { ret void }
|
define void @test_add_s8() { ret void }
|
||||||
define void @test_add_s16() { ret void }
|
define void @test_add_s16() { ret void }
|
||||||
define void @test_add_s32() { ret void }
|
define void @test_add_s32() { ret void }
|
||||||
|
@ -7,6 +10,59 @@
|
||||||
define void @test_load_from_stack() { ret void }
|
define void @test_load_from_stack() { ret void }
|
||||||
...
|
...
|
||||||
---
|
---
|
||||||
|
---
|
||||||
|
name: test_sext_s8
|
||||||
|
# CHECK-LABEL: name: test_sext_s8
|
||||||
|
legalized: true
|
||||||
|
regBankSelected: true
|
||||||
|
selected: false
|
||||||
|
# CHECK: selected: true
|
||||||
|
registers:
|
||||||
|
- { id: 0, class: gprb }
|
||||||
|
- { id: 1, class: gprb }
|
||||||
|
body: |
|
||||||
|
bb.0:
|
||||||
|
liveins: %r0
|
||||||
|
|
||||||
|
%0(s8) = COPY %r0
|
||||||
|
; CHECK: [[VREGX:%[0-9]+]] = COPY %r0
|
||||||
|
|
||||||
|
%1(s32) = G_SEXT %0(s8)
|
||||||
|
; CHECK: [[VREGEXT:%[0-9]+]] = SXTB [[VREGX]], 0, 14, _
|
||||||
|
|
||||||
|
%r0 = COPY %1(s32)
|
||||||
|
; CHECK: %r0 = COPY [[VREGEXT]]
|
||||||
|
|
||||||
|
BX_RET 14, _, implicit %r0
|
||||||
|
; CHECK: BX_RET 14, _, implicit %r0
|
||||||
|
...
|
||||||
|
---
|
||||||
|
name: test_zext_s16
|
||||||
|
# CHECK-LABEL: name: test_zext_s16
|
||||||
|
legalized: true
|
||||||
|
regBankSelected: true
|
||||||
|
selected: false
|
||||||
|
# CHECK: selected: true
|
||||||
|
registers:
|
||||||
|
- { id: 0, class: gprb }
|
||||||
|
- { id: 1, class: gprb }
|
||||||
|
body: |
|
||||||
|
bb.0:
|
||||||
|
liveins: %r0
|
||||||
|
|
||||||
|
%0(s16) = COPY %r0
|
||||||
|
; CHECK: [[VREGX:%[0-9]+]] = COPY %r0
|
||||||
|
|
||||||
|
%1(s32) = G_ZEXT %0(s16)
|
||||||
|
; CHECK: [[VREGEXT:%[0-9]+]] = UXTH [[VREGX]], 0, 14, _
|
||||||
|
|
||||||
|
%r0 = COPY %1(s32)
|
||||||
|
; CHECK: %r0 = COPY [[VREGEXT]]
|
||||||
|
|
||||||
|
BX_RET 14, _, implicit %r0
|
||||||
|
; CHECK: BX_RET 14, _, implicit %r0
|
||||||
|
...
|
||||||
|
---
|
||||||
name: test_add_s8
|
name: test_add_s8
|
||||||
# CHECK-LABEL: name: test_add_s8
|
# CHECK-LABEL: name: test_add_s8
|
||||||
legalized: true
|
legalized: true
|
||||||
|
|
|
@ -20,6 +20,17 @@ entry:
|
||||||
ret i8 %sum
|
ret i8 %sum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define signext i8 @test_return_sext_i8(i8 %x) {
|
||||||
|
; CHECK-LABEL: name: test_return_sext_i8
|
||||||
|
; CHECK: liveins: %r0
|
||||||
|
; CHECK: [[VREG:%[0-9]+]](s8) = COPY %r0
|
||||||
|
; CHECK: [[VREGEXT:%[0-9]+]](s32) = G_SEXT [[VREG]]
|
||||||
|
; CHECK: %r0 = COPY [[VREGEXT]](s32)
|
||||||
|
; CHECK: BX_RET 14, _, implicit %r0
|
||||||
|
entry:
|
||||||
|
ret i8 %x
|
||||||
|
}
|
||||||
|
|
||||||
define i16 @test_add_i16(i16 %x, i16 %y) {
|
define i16 @test_add_i16(i16 %x, i16 %y) {
|
||||||
; CHECK-LABEL: name: test_add_i16
|
; CHECK-LABEL: name: test_add_i16
|
||||||
; CHECK: liveins: %r0, %r1
|
; CHECK: liveins: %r0, %r1
|
||||||
|
@ -33,6 +44,17 @@ entry:
|
||||||
ret i16 %sum
|
ret i16 %sum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define zeroext i16 @test_return_zext_i16(i16 %x) {
|
||||||
|
; CHECK-LABEL: name: test_return_zext_i16
|
||||||
|
; CHECK: liveins: %r0
|
||||||
|
; CHECK: [[VREG:%[0-9]+]](s16) = COPY %r0
|
||||||
|
; CHECK: [[VREGEXT:%[0-9]+]](s32) = G_ZEXT [[VREG]]
|
||||||
|
; CHECK: %r0 = COPY [[VREGEXT]](s32)
|
||||||
|
; CHECK: BX_RET 14, _, implicit %r0
|
||||||
|
entry:
|
||||||
|
ret i16 %x
|
||||||
|
}
|
||||||
|
|
||||||
define i32 @test_add_i32(i32 %x, i32 %y) {
|
define i32 @test_add_i32(i32 %x, i32 %y) {
|
||||||
; CHECK-LABEL: name: test_add_i32
|
; CHECK-LABEL: name: test_add_i32
|
||||||
; CHECK: liveins: %r0, %r1
|
; CHECK: liveins: %r0, %r1
|
||||||
|
|
|
@ -7,6 +7,22 @@ entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define zeroext i8 @test_ext_i8(i8 %x) {
|
||||||
|
; CHECK-LABEL: test_ext_i8:
|
||||||
|
; CHECK: uxtb r0, r0
|
||||||
|
; CHECK: bx lr
|
||||||
|
entry:
|
||||||
|
ret i8 %x
|
||||||
|
}
|
||||||
|
|
||||||
|
define signext i16 @test_ext_i16(i16 %x) {
|
||||||
|
; CHECK-LABEL: test_ext_i16:
|
||||||
|
; CHECK: sxth r0, r0
|
||||||
|
; CHECK: bx lr
|
||||||
|
entry:
|
||||||
|
ret i16 %x
|
||||||
|
}
|
||||||
|
|
||||||
define i8 @test_add_i8(i8 %x, i8 %y) {
|
define i8 @test_add_i8(i8 %x, i8 %y) {
|
||||||
; CHECK-LABEL: test_add_i8:
|
; CHECK-LABEL: test_add_i8:
|
||||||
; CHECK: add r0, r0, r1
|
; CHECK: add r0, r0, r1
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
# RUN: llc -mtriple arm-- -global-isel -run-pass=legalizer %s -o - | FileCheck %s
|
# RUN: llc -mtriple arm-- -global-isel -run-pass=legalizer %s -o - | FileCheck %s
|
||||||
--- |
|
--- |
|
||||||
|
define void @test_sext_s8() { ret void }
|
||||||
|
define void @test_zext_s16() { ret void }
|
||||||
|
|
||||||
define void @test_add_s8() { ret void }
|
define void @test_add_s8() { ret void }
|
||||||
define void @test_add_s16() { ret void }
|
define void @test_add_s16() { ret void }
|
||||||
define void @test_add_s32() { ret void }
|
define void @test_add_s32() { ret void }
|
||||||
|
@ -7,6 +10,50 @@
|
||||||
define void @test_load_from_stack() { ret void }
|
define void @test_load_from_stack() { ret void }
|
||||||
...
|
...
|
||||||
---
|
---
|
||||||
|
name: test_sext_s8
|
||||||
|
# CHECK-LABEL: name: test_sext_s8
|
||||||
|
legalized: false
|
||||||
|
# CHECK: legalized: true
|
||||||
|
regBankSelected: false
|
||||||
|
selected: false
|
||||||
|
tracksRegLiveness: true
|
||||||
|
registers:
|
||||||
|
- { id: 0, class: _ }
|
||||||
|
- { id: 1, class: _ }
|
||||||
|
body: |
|
||||||
|
bb.0:
|
||||||
|
liveins: %r0
|
||||||
|
|
||||||
|
%0(s8) = COPY %r0
|
||||||
|
%1(s32) = G_SEXT %0
|
||||||
|
; G_SEXT with s8 is legal, so we should find it unchanged in the output
|
||||||
|
; CHECK: {{%[0-9]+}}(s32) = G_SEXT {{%[0-9]+}}
|
||||||
|
%r0 = COPY %1(s32)
|
||||||
|
BX_RET 14, _, implicit %r0
|
||||||
|
...
|
||||||
|
---
|
||||||
|
name: test_zext_s16
|
||||||
|
# CHECK-LABEL: name: test_zext_s16
|
||||||
|
legalized: false
|
||||||
|
# CHECK: legalized: true
|
||||||
|
regBankSelected: false
|
||||||
|
selected: false
|
||||||
|
tracksRegLiveness: true
|
||||||
|
registers:
|
||||||
|
- { id: 0, class: _ }
|
||||||
|
- { id: 1, class: _ }
|
||||||
|
body: |
|
||||||
|
bb.0:
|
||||||
|
liveins: %r0
|
||||||
|
|
||||||
|
%0(s16) = COPY %r0
|
||||||
|
%1(s32) = G_ZEXT %0
|
||||||
|
; G_ZEXT with s16 is legal, so we should find it unchanged in the output
|
||||||
|
; CHECK: {{%[0-9]+}}(s32) = G_ZEXT {{%[0-9]+}}
|
||||||
|
%r0 = COPY %1(s32)
|
||||||
|
BX_RET 14, _, implicit %r0
|
||||||
|
...
|
||||||
|
---
|
||||||
name: test_add_s8
|
name: test_add_s8
|
||||||
# CHECK-LABEL: name: test_add_s8
|
# CHECK-LABEL: name: test_add_s8
|
||||||
legalized: false
|
legalized: false
|
||||||
|
|
Loading…
Reference in New Issue