Index: lib/Target/ARM/ARMCallLowering.cpp =================================================================== --- lib/Target/ARM/ARMCallLowering.cpp +++ lib/Target/ARM/ARMCallLowering.cpp @@ -17,6 +17,7 @@ #include "ARMBaseInstrInfo.h" #include "ARMISelLowering.h" +#include "ARMSubtarget.h" #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" #include "llvm/CodeGen/MachineRegisterInfo.h" @@ -59,11 +60,8 @@ 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); } @@ -154,6 +152,7 @@ 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); } @@ -173,6 +172,9 @@ auto DL = MIRBuilder.getMF().getDataLayout(); auto &TLI = *getTLI(); + if (TLI.getSubtarget()->isThumb()) + return false; + auto &Args = F.getArgumentList(); unsigned ArgIdx = 0; for (auto &Arg : Args) { Index: lib/Target/ARM/ARMInstructionSelector.cpp =================================================================== --- lib/Target/ARM/ARMInstructionSelector.cpp +++ lib/Target/ARM/ARMInstructionSelector.cpp @@ -67,6 +67,23 @@ 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!"); @@ -86,6 +103,31 @@ 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)); + AddDefaultPred(MIB.addImm(0)); + break; + } + default: + DEBUG(dbgs() << "Unsupported source size for extension"); + return false; + } + break; + } case G_ADD: I.setDesc(TII.get(ARM::ADDrr)); AddDefaultCC(AddDefaultPred(MIB)); Index: lib/Target/ARM/ARMLegalizerInfo.cpp =================================================================== --- lib/Target/ARM/ARMLegalizerInfo.cpp +++ lib/Target/ARM/ARMLegalizerInfo.cpp @@ -40,5 +40,11 @@ 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(); } Index: lib/Target/ARM/ARMRegisterBankInfo.cpp =================================================================== --- lib/Target/ARM/ARMRegisterBankInfo.cpp +++ lib/Target/ARM/ARMRegisterBankInfo.cpp @@ -82,6 +82,7 @@ switch (RC.getID()) { case GPRRegClassID: + case GPRnopcRegClassID: case tGPR_and_tcGPRRegClassID: return getRegBank(ARM::GPRRegBankID); default: @@ -111,6 +112,8 @@ 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]; Index: test/CodeGen/ARM/GlobalISel/arm-instruction-select.mir =================================================================== --- test/CodeGen/ARM/GlobalISel/arm-instruction-select.mir +++ test/CodeGen/ARM/GlobalISel/arm-instruction-select.mir @@ -1,5 +1,8 @@ # 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 } @@ -7,6 +10,59 @@ 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 Index: test/CodeGen/ARM/GlobalISel/arm-irtranslator.ll =================================================================== --- test/CodeGen/ARM/GlobalISel/arm-irtranslator.ll +++ test/CodeGen/ARM/GlobalISel/arm-irtranslator.ll @@ -20,6 +20,17 @@ 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 @@ -33,6 +44,17 @@ 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 Index: test/CodeGen/ARM/GlobalISel/arm-isel.ll =================================================================== --- test/CodeGen/ARM/GlobalISel/arm-isel.ll +++ test/CodeGen/ARM/GlobalISel/arm-isel.ll @@ -7,6 +7,22 @@ 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 Index: test/CodeGen/ARM/GlobalISel/arm-legalizer.mir =================================================================== --- test/CodeGen/ARM/GlobalISel/arm-legalizer.mir +++ test/CodeGen/ARM/GlobalISel/arm-legalizer.mir @@ -1,5 +1,8 @@ # 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 } @@ -7,6 +10,50 @@ 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