Index: lib/Target/X86/X86InstructionSelector.cpp =================================================================== --- lib/Target/X86/X86InstructionSelector.cpp +++ lib/Target/X86/X86InstructionSelector.cpp @@ -67,6 +67,8 @@ MachineFunction &MF) const; bool selectConstant(MachineInstr &I, MachineRegisterInfo &MRI, MachineFunction &MF) const; + bool selectTrunc(MachineInstr &I, MachineRegisterInfo &MRI, + MachineFunction &MF) const; const X86Subtarget &STI; const X86InstrInfo &TII; @@ -99,6 +101,10 @@ static const TargetRegisterClass * getRegClassForTypeOnBank(LLT Ty, const RegisterBank &RB) { if (RB.getID() == X86::GPRRegBankID) { + if (Ty.getSizeInBits() <= 8) + return &X86::GR8RegClass; + if (Ty.getSizeInBits() == 16) + return &X86::GR16RegClass; if (Ty.getSizeInBits() == 32) return &X86::GR32RegClass; if (Ty.getSizeInBits() == 64) @@ -207,6 +213,8 @@ return true; if (selectConstant(I, MRI, MF)) return true; + if (selectTrunc(I, MRI, MF)) + return true; return selectImpl(I); } @@ -509,6 +517,59 @@ return constrainSelectedInstRegOperands(I, TII, TRI, RBI); } +bool X86InstructionSelector::selectTrunc(MachineInstr &I, + MachineRegisterInfo &MRI, + MachineFunction &MF) const { + if (I.getOpcode() != TargetOpcode::G_TRUNC) + return false; + + const unsigned DstReg = I.getOperand(0).getReg(); + const unsigned SrcReg = I.getOperand(1).getReg(); + + const LLT DstTy = MRI.getType(DstReg); + const LLT SrcTy = MRI.getType(SrcReg); + + const RegisterBank &DstRB = *RBI.getRegBank(DstReg, MRI, TRI); + const RegisterBank &SrcRB = *RBI.getRegBank(SrcReg, MRI, TRI); + + if (DstRB.getID() != SrcRB.getID()) { + DEBUG(dbgs() << "G_TRUNC input/output on different banks\n"); + return false; + } + + if (DstRB.getID() != X86::GPRRegBankID) + return false; + + const TargetRegisterClass *DstRC = getRegClassForTypeOnBank(DstTy, DstRB); + if (!DstRC) + return false; + + const TargetRegisterClass *SrcRC = getRegClassForTypeOnBank(SrcTy, SrcRB); + if (!SrcRC) + return false; + + if (!RBI.constrainGenericRegister(SrcReg, *SrcRC, MRI) || + !RBI.constrainGenericRegister(DstReg, *DstRC, MRI)) { + DEBUG(dbgs() << "Failed to constrain G_TRUNC\n"); + return false; + } + + if (DstRC == SrcRC) { + // Nothing to be done + } else if (DstRC == &X86::GR32RegClass) { + I.getOperand(1).setSubReg(X86::sub_32bit); + } else if (DstRC == &X86::GR16RegClass) { + I.getOperand(1).setSubReg(X86::sub_16bit); + } else if (DstRC == &X86::GR8RegClass) { + I.getOperand(1).setSubReg(X86::sub_8bit); + } else { + return false; + } + + I.setDesc(TII.get(X86::COPY)); + return true; +} + InstructionSelector * llvm::createX86InstructionSelector(X86Subtarget &Subtarget, X86RegisterBankInfo &RBI) { Index: lib/Target/X86/X86RegisterBankInfo.cpp =================================================================== --- lib/Target/X86/X86RegisterBankInfo.cpp +++ lib/Target/X86/X86RegisterBankInfo.cpp @@ -68,6 +68,7 @@ X86GenRegisterBankInfo::getPartialMappingIdx(const LLT &Ty, bool isFP) { if ((Ty.isScalar() && !isFP) || Ty.isPointer()) { switch (Ty.getSizeInBits()) { + case 1: case 8: return PMI_GPR8; case 16: Index: test/CodeGen/X86/GlobalISel/X86-regbankselect.mir =================================================================== --- test/CodeGen/X86/GlobalISel/X86-regbankselect.mir +++ test/CodeGen/X86/GlobalISel/X86-regbankselect.mir @@ -106,6 +106,10 @@ ret void } + define void @trunc_check() { + ret void + } + ... --- name: test_add_i8 @@ -632,3 +636,27 @@ RET 0 ... +--- +name: trunc_check +alignment: 4 +legalized: true +# CHECK-LABEL: name: trunc_check +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gpr } +# CHECK-NEXT: - { id: 1, class: gpr } +# CHECK-NEXT: - { id: 2, class: gpr } +# CHECK-NEXT: - { id: 3, class: gpr } +registers: + - { id: 0, class: _ } + - { id: 1, class: _ } + - { id: 2, class: _ } + - { id: 3, class: _ } +body: | + bb.0 (%ir-block.0): + %0(s32) = IMPLICIT_DEF + %1(s1) = G_TRUNC %0(s32) + %2(s8) = G_TRUNC %0(s32) + %3(s16) = G_TRUNC %0(s32) + RET 0 + +... Index: test/CodeGen/X86/GlobalISel/legalize-trunc.mir =================================================================== --- /dev/null +++ test/CodeGen/X86/GlobalISel/legalize-trunc.mir @@ -0,0 +1,31 @@ +# RUN: llc -mtriple=i386-linux-gnu -global-isel -run-pass=legalizer %s -o - | FileCheck %s --check-prefix=ALL --check-prefix=X32 +# RUN: llc -mtriple=x86_64-linux-gnu -global-isel -run-pass=legalizer %s -o - | FileCheck %s --check-prefix=ALL --check-prefix=X64 +--- | + define void @trunc_check() { + ret void + } + +... +--- +name: trunc_check +# ALL-LABEL: name: trunc_check +registers: + - { id: 0, class: _ } + - { id: 1, class: _ } + - { id: 2, class: _ } + - { id: 3, class: _ } +body: | + bb.1 (%ir-block.0): + %0(s32) = IMPLICIT_DEF + ; ALL: %1(s1) = G_TRUNC %0(s32) + %1(s1) = G_TRUNC %0(s32) + + ; ALL: %2(s8) = G_TRUNC %0(s32) + %2(s8) = G_TRUNC %0(s32) + + ; ALL: %3(s16) = G_TRUNC %0(s32) + %3(s16) = G_TRUNC %0(s32) + RET 0 + +... + Index: test/CodeGen/X86/GlobalISel/select-trunc.mir =================================================================== --- /dev/null +++ test/CodeGen/X86/GlobalISel/select-trunc.mir @@ -0,0 +1,183 @@ +# RUN: llc -mtriple=x86_64-linux-gnu -global-isel -run-pass=instruction-select %s -o - | FileCheck %s --check-prefix=CHECK +--- | + define i1 @trunc_i32toi1(i32 %a) { + %r = trunc i32 %a to i1 + ret i1 %r + } + + define i8 @trunc_i32toi8(i32 %a) { + %r = trunc i32 %a to i8 + ret i8 %r + } + + define i16 @trunc_i32toi16(i32 %a) { + %r = trunc i32 %a to i16 + ret i16 %r + } + + define i8 @trunc_i64toi8(i64 %a) { + %r = trunc i64 %a to i8 + ret i8 %r + } + + define i16 @trunc_i64toi16(i64 %a) { + %r = trunc i64 %a to i16 + ret i16 %r + } + + define i32 @trunc_i64toi32(i64 %a) { + %r = trunc i64 %a to i32 + ret i32 %r + } + +... +--- +name: trunc_i32toi1 +alignment: 4 +legalized: true +regBankSelected: true +selected: false +# CHECK-LABEL: name: trunc_i32toi1 +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gr32 } +# CHECK-NEXT: - { id: 1, class: gr8 } +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } +# CHECK: body: +# CHECK: %1 = COPY %0.sub_8 +body: | + bb.1 (%ir-block.0): + liveins: %edi + + %0(s32) = COPY %edi + %1(s1) = G_TRUNC %0(s32) + %al = COPY %1(s1) + RET 0, implicit %al + +... +--- +name: trunc_i32toi8 +alignment: 4 +legalized: true +regBankSelected: true +selected: false +# CHECK-LABEL: name: trunc_i32toi8 +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gr32 } +# CHECK-NEXT: - { id: 1, class: gr8 } +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } +# CHECK: body: +# CHECK: %1 = COPY %0.sub_8 +body: | + bb.1 (%ir-block.0): + liveins: %edi + + %0(s32) = COPY %edi + %1(s8) = G_TRUNC %0(s32) + %al = COPY %1(s8) + RET 0, implicit %al + +... +--- +name: trunc_i32toi16 +alignment: 4 +legalized: true +regBankSelected: true +selected: false +# CHECK-LABEL: name: trunc_i32toi16 +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gr32 } +# CHECK-NEXT: - { id: 1, class: gr16 } +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } +# CHECK: body: +# CHECK: %1 = COPY %0.sub_16 +body: | + bb.1 (%ir-block.0): + liveins: %edi + + %0(s32) = COPY %edi + %1(s16) = G_TRUNC %0(s32) + %ax = COPY %1(s16) + RET 0, implicit %ax + +... +--- +name: trunc_i64toi8 +alignment: 4 +legalized: true +regBankSelected: true +selected: false +# CHECK-LABEL: name: trunc_i64toi8 +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gr64 } +# CHECK-NEXT: - { id: 1, class: gr8 } +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } +# CHECK: body: +# CHECK: %1 = COPY %0.sub_8 +body: | + bb.1 (%ir-block.0): + liveins: %rdi + + %0(s64) = COPY %rdi + %1(s8) = G_TRUNC %0(s64) + %al = COPY %1(s8) + RET 0, implicit %al + +... +--- +name: trunc_i64toi16 +alignment: 4 +legalized: true +regBankSelected: true +selected: false +# CHECK-LABEL: name: trunc_i64toi16 +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gr64 } +# CHECK-NEXT: - { id: 1, class: gr16 } +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } +# CHECK: body: +# CHECK: %1 = COPY %0.sub_16 +body: | + bb.1 (%ir-block.0): + liveins: %rdi + + %0(s64) = COPY %rdi + %1(s16) = G_TRUNC %0(s64) + %ax = COPY %1(s16) + RET 0, implicit %ax + +... +--- +name: trunc_i64toi32 +alignment: 4 +legalized: true +regBankSelected: true +selected: false +# CHECK-LABEL: name: trunc_i64toi32 +# CHECK: registers: +# CHECK-NEXT: - { id: 0, class: gr64 } +# CHECK-NEXT: - { id: 1, class: gr32 } +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } +# CHECK: body: +# CHECK: %1 = COPY %0.sub_32 +body: | + bb.1 (%ir-block.0): + liveins: %rdi + + %0(s64) = COPY %rdi + %1(s32) = G_TRUNC %0(s64) + %eax = COPY %1(s32) + RET 0, implicit %eax + +... Index: test/CodeGen/X86/GlobalISel/trunc.ll =================================================================== --- /dev/null +++ test/CodeGen/X86/GlobalISel/trunc.ll @@ -0,0 +1,57 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=x86_64-linux-gnu -global-isel < %s -o - | FileCheck %s --check-prefix=CHECK + +define i1 @trunc_i32toi1(i32 %a) { +; CHECK-LABEL: trunc_i32toi1: +; CHECK: # BB#0: +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: retq + %r = trunc i32 %a to i1 + ret i1 %r +} + +define i8 @trunc_i32toi8(i32 %a) { +; CHECK-LABEL: trunc_i32toi8: +; CHECK: # BB#0: +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: retq + %r = trunc i32 %a to i8 + ret i8 %r +} + +define i16 @trunc_i32toi16(i32 %a) { +; CHECK-LABEL: trunc_i32toi16: +; CHECK: # BB#0: +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: retq + %r = trunc i32 %a to i16 + ret i16 %r +} + +define i8 @trunc_i64toi8(i64 %a) { +; CHECK-LABEL: trunc_i64toi8: +; CHECK: # BB#0: +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: retq + %r = trunc i64 %a to i8 + ret i8 %r +} + +define i16 @trunc_i64toi16(i64 %a) { +; CHECK-LABEL: trunc_i64toi16: +; CHECK: # BB#0: +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: retq + %r = trunc i64 %a to i16 + ret i16 %r +} + +define i32 @trunc_i64toi32(i64 %a) { +; CHECK-LABEL: trunc_i64toi32: +; CHECK: # BB#0: +; CHECK-NEXT: movl %edi, %eax +; CHECK-NEXT: retq + %r = trunc i64 %a to i32 + ret i32 %r +} +