assert(VA.getValVT().getSizeInBits() <= 32 && "Unsupported value size");
assert(VA.getLocVT().getSizeInBits() == 32 && "Unsupported location size");
- assert(VA.getLocInfo() != CCValAssign::SExt &&
- VA.getLocInfo() != CCValAssign::ZExt &&
- "ABI extensions not supported yet");
-
- MIRBuilder.buildCopy(PhysReg, ValVReg);
+ unsigned ExtReg = extendRegister(ValVReg, VA);
+ MIRBuilder.buildCopy(PhysReg, ExtReg);
MIB.addUse(PhysReg, RegState::Implicit);
}
assert(VA.getValVT().getSizeInBits() <= 32 && "Unsupported value size");
assert(VA.getLocVT().getSizeInBits() == 32 && "Unsupported location size");
+ // The caller should handle all necesary extensions.
MIRBuilder.getMBB().addLiveIn(PhysReg);
MIRBuilder.buildCopy(ValVReg, PhysReg);
}
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 {
assert(I.getParent() && "Instruction should be in a basic block!");
assert(I.getParent()->getParent() && "Instruction should be in a function!");
using namespace TargetOpcode;
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:
I.setDesc(TII.get(ARM::ADDrr));
MIB.add(predOps(ARMCC::AL)).add(condCodeOp());
for (auto Ty : {s8, s16, s32})
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();
}
switch (RC.getID()) {
case GPRRegClassID:
+ case GPRnopcRegClassID:
case tGPR_and_tcGPRRegClassID:
return getRegBank(ARM::GPRRegBankID);
default:
switch (Opc) {
case G_ADD:
case G_LOAD:
+ case G_SEXT:
+ case G_ZEXT:
// FIXME: We're abusing the fact that everything lives in a GPR for now; in
// the real world we would use different mappings.
OperandsMapping = &ARM::ValueMappings[0];
# 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_s16() { ret void }
define void @test_add_s32() { 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
# CHECK-LABEL: name: test_add_s8
legalized: true
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) {
; CHECK-LABEL: name: test_add_i16
; CHECK: liveins: %r0, %r1
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) {
; CHECK-LABEL: name: test_add_i32
; CHECK: liveins: %r0, %r1
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) {
; CHECK-LABEL: test_add_i8:
; CHECK: add r0, r0, r1
# 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_s16() { ret void }
define void @test_add_s32() { 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
# CHECK-LABEL: name: test_add_s8
legalized: false