Index: lib/Target/X86/X86InstructionSelector.cpp =================================================================== --- lib/Target/X86/X86InstructionSelector.cpp +++ lib/Target/X86/X86InstructionSelector.cpp @@ -19,6 +19,7 @@ #include "X86Subtarget.h" #include "X86TargetMachine.h" #include "llvm/CodeGen/GlobalISel/InstructionSelector.h" +#include "llvm/CodeGen/GlobalISel/Utils.h" #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineInstr.h" @@ -76,6 +77,9 @@ bool selectCmp(MachineInstr &I, MachineRegisterInfo &MRI, MachineFunction &MF) const; + bool selectUadde(MachineInstr &I, MachineRegisterInfo &MRI, + MachineFunction &MF) const; + const X86TargetMachine &TM; const X86Subtarget &STI; const X86InstrInfo &TII; @@ -249,6 +253,8 @@ return true; if (selectCmp(I, MRI, MF)) return true; + if (selectUadde(I, MRI, MF)) + return true; return false; } @@ -669,6 +675,53 @@ return true; } +bool X86InstructionSelector::selectUadde(MachineInstr &I, + MachineRegisterInfo &MRI, + MachineFunction &MF) const { + if (I.getOpcode() != TargetOpcode::G_UADDE) + return false; + + const unsigned DstReg = I.getOperand(0).getReg(); + unsigned CarryInReg = I.getOperand(4).getReg(); + + const LLT DstTy = MRI.getType(DstReg); + + if (DstTy != LLT::scalar(32)) + return false; + + // find CarryIn def instruction. + MachineInstr *Def = MRI.getVRegDef(CarryInReg); + while (Def->getOpcode() == TargetOpcode::G_TRUNC) { + CarryInReg = Def->getOperand(1).getReg(); + Def = MRI.getVRegDef(CarryInReg); + } + + MachineInstrBuilder MIB(MF, I); + unsigned Opcode; + if (Def->getOpcode() == TargetOpcode::G_UADDE) { + // carry set by prev ADD + Opcode = X86::ADC32rr; + MIB.addDef(X86::EFLAGS, RegState::Implicit); + MIB.addUse(X86::EFLAGS, RegState::Implicit); + } else if (auto val = getConstantVRegVal(CarryInReg, MRI)) { + // carry is constant, support only 0. + if (*val != 0) + return false; + + Opcode = X86::ADD32rr; + MIB.addDef(X86::EFLAGS, RegState::Implicit); + } else + return false; + + // remove carryIn/Out + I.RemoveOperand(4); + I.RemoveOperand(1); + + I.setDesc(TII.get(Opcode)); + + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); +} + InstructionSelector * llvm::createX86InstructionSelector(const X86TargetMachine &TM, X86Subtarget &Subtarget, Index: lib/Target/X86/X86LegalizerInfo.cpp =================================================================== --- lib/Target/X86/X86LegalizerInfo.cpp +++ lib/Target/X86/X86LegalizerInfo.cpp @@ -59,6 +59,11 @@ for (auto Ty : {s8, s16, s32}) setAction({BinOp, Ty}, Legal); + for (unsigned Op : {G_UADDE}) { + setAction({Op, s32}, Legal); + setAction({Op, 1, s1}, Legal); + } + for (unsigned MemOp : {G_LOAD, G_STORE}) { for (auto Ty : {s8, s16, s32, p0}) setAction({MemOp, Ty}, Legal); Index: test/CodeGen/X86/GlobalISel/add-scalar.ll =================================================================== --- test/CodeGen/X86/GlobalISel/add-scalar.ll +++ test/CodeGen/X86/GlobalISel/add-scalar.ll @@ -1,44 +1,87 @@ ; 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=ALL --check-prefix=X64 +; RUN: llc -mtriple=i386-linux-gnu -global-isel < %s -o - | FileCheck %s --check-prefix=ALL --check-prefix=X32 define i64 @test_add_i64(i64 %arg1, i64 %arg2) { -; ALL-LABEL: test_add_i64: -; ALL: # BB#0: -; ALL-NEXT: leaq (%rsi,%rdi), %rax -; ALL-NEXT: retq +; X64-LABEL: test_add_i64: +; X64: # BB#0: +; X64-NEXT: leaq (%rsi,%rdi), %rax +; X64-NEXT: retq +; +; X32-LABEL: test_add_i64: +; X32: # BB#0: +; X32-NEXT: pushl %esi +; X32-NEXT: .Lcfi0: +; X32-NEXT: .cfi_def_cfa_offset 8 +; X32-NEXT: .Lcfi1: +; X32-NEXT: .cfi_offset %esi, -8 +; X32-NEXT: leal 8(%esp), %ecx +; X32-NEXT: leal 12(%esp), %esi +; X32-NEXT: leal 16(%esp), %eax +; X32-NEXT: movl (%eax), %eax +; X32-NEXT: leal 20(%esp), %edx +; X32-NEXT: movl (%edx), %edx +; X32-NEXT: addl (%ecx), %eax +; X32-NEXT: adcl (%esi), %edx +; X32-NEXT: popl %esi +; X32-NEXT: retl %ret = add i64 %arg1, %arg2 ret i64 %ret } define i32 @test_add_i32(i32 %arg1, i32 %arg2) { -; ALL-LABEL: test_add_i32: -; ALL: # BB#0: -; ALL-NEXT: # kill: %EDI %EDI %RDI -; ALL-NEXT: # kill: %ESI %ESI %RSI -; ALL-NEXT: leal (%rsi,%rdi), %eax -; ALL-NEXT: retq +; X64-LABEL: test_add_i32: +; X64: # BB#0: +; X64-NEXT: # kill: %EDI %EDI %RDI +; X64-NEXT: # kill: %ESI %ESI %RSI +; X64-NEXT: leal (%rsi,%rdi), %eax +; X64-NEXT: retq +; +; X32-LABEL: test_add_i32: +; X32: # BB#0: +; X32-NEXT: leal 4(%esp), %ecx +; X32-NEXT: leal 8(%esp), %eax +; X32-NEXT: movl (%eax), %eax +; X32-NEXT: addl (%ecx), %eax +; X32-NEXT: retl %ret = add i32 %arg1, %arg2 ret i32 %ret } define i16 @test_add_i16(i16 %arg1, i16 %arg2) { -; ALL-LABEL: test_add_i16: -; ALL: # BB#0: -; ALL-NEXT: # kill: %DI %DI %RDI -; ALL-NEXT: # kill: %SI %SI %RSI -; ALL-NEXT: leal (%rsi,%rdi), %eax -; ALL-NEXT: # kill: %AX %AX %EAX -; ALL-NEXT: retq +; X64-LABEL: test_add_i16: +; X64: # BB#0: +; X64-NEXT: # kill: %DI %DI %RDI +; X64-NEXT: # kill: %SI %SI %RSI +; X64-NEXT: leal (%rsi,%rdi), %eax +; X64-NEXT: # kill: %AX %AX %EAX +; X64-NEXT: retq +; +; X32-LABEL: test_add_i16: +; X32: # BB#0: +; X32-NEXT: leal 4(%esp), %ecx +; X32-NEXT: leal 8(%esp), %eax +; X32-NEXT: movzwl (%eax), %eax +; X32-NEXT: addw (%ecx), %ax +; X32-NEXT: retl %ret = add i16 %arg1, %arg2 ret i16 %ret } define i8 @test_add_i8(i8 %arg1, i8 %arg2) { -; ALL-LABEL: test_add_i8: -; ALL: # BB#0: -; ALL-NEXT: addb %dil, %sil -; ALL-NEXT: movl %esi, %eax -; ALL-NEXT: retq +; X64-LABEL: test_add_i8: +; X64: # BB#0: +; X64-NEXT: addb %dil, %sil +; X64-NEXT: movl %esi, %eax +; X64-NEXT: retq +; +; X32-LABEL: test_add_i8: +; X32: # BB#0: +; X32-NEXT: leal 4(%esp), %ecx +; X32-NEXT: leal 8(%esp), %eax +; X32-NEXT: movb (%eax), %al +; X32-NEXT: addb (%ecx), %al +; X32-NEXT: retl %ret = add i8 %arg1, %arg2 ret i8 %ret } Index: test/CodeGen/X86/GlobalISel/legalize-add.mir =================================================================== --- test/CodeGen/X86/GlobalISel/legalize-add.mir +++ test/CodeGen/X86/GlobalISel/legalize-add.mir @@ -1,40 +1,67 @@ -# RUN: llc -mtriple=x86_64-linux-gnu -global-isel -run-pass=legalizer %s -o - | FileCheck %s - +# RUN: llc -mtriple=x86_64-linux-gnu -global-isel -run-pass=legalizer %s -o - | FileCheck %s --check-prefix=ALL --check-prefix=X64 +# RUN: llc -mtriple=i386-linux-gnu -global-isel -run-pass=legalizer %s -o - | FileCheck %s --check-prefix=ALL --check-prefix=X32 --- | - ; ModuleID = '' - source_filename = "" - target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" - target triple = "x86_64--linux-gnu" + define void @test_add_i32() { + ret void + } - define i32 @test_add_i32(i32 %arg1, i32 %arg2) { - %ret = add i32 %arg1, %arg2 - ret i32 %ret + define void @test_add_i64() { + ret void } ... --- name: test_add_i32 +# ALL-LABEL: name: test_add_i32 alignment: 4 legalized: false regBankSelected: false -selected: false -tracksRegLiveness: true registers: - { id: 0, class: _ } - { id: 1, class: _ } - { id: 2, class: _ } +# ALL: %0(s32) = IMPLICIT_DEF +# ALL-NEXT: %1(s32) = IMPLICIT_DEF +# ALL-NEXT: %2(s32) = G_ADD %0, %1 +# ALL-NEXT: RET 0 body: | bb.1 (%ir-block.0): - liveins: %edi, %esi - ; CHECK-LABEL: name: test_add_i32 - ; CHECK: [[VAL1:%.*]](s32) = COPY %edi - ; CHECK: [[VAL2:%.*]](s32) = COPY %esi - ; CHECK: [[RES:%.*]](s32) = G_ADD [[VAL1:%.*]], [[VAL2:%.*]] - - %0(s32) = COPY %edi - %1(s32) = COPY %esi + %0(s32) = IMPLICIT_DEF + %1(s32) = IMPLICIT_DEF %2(s32) = G_ADD %0, %1 - %eax = COPY %2(s32) - RET 0, implicit %eax + RET 0 + +... +--- +name: test_add_i64 +# ALL-LABEL: name: test_add_i64 +alignment: 4 +legalized: false +regBankSelected: false +registers: + - { id: 0, class: _ } + - { id: 1, class: _ } + - { id: 2, class: _ } +# X64: %0(s64) = IMPLICIT_DEF +# X64-NEXT: %1(s64) = IMPLICIT_DEF +# X64-NEXT: %2(s64) = G_ADD %0, %1 +# X64-NEXT: RET 0 +# +# X32: %0(s64) = IMPLICIT_DEF +# X32-NEXT: %1(s64) = IMPLICIT_DEF +# X32-NEXT: %3(s32), %4(s32) = G_UNMERGE_VALUES %0(s64) +# X32-NEXT: %5(s32), %6(s32) = G_UNMERGE_VALUES %1(s64) +# X32-NEXT: %12(s8) = G_CONSTANT i8 0 +# X32-NEXT: %7(s1) = G_TRUNC %12(s8) +# X32-NEXT: %8(s32), %9(s1) = G_UADDE %3, %5, %7 +# X32-NEXT: %10(s32), %11(s1) = G_UADDE %4, %6, %9 +# X32-NEXT: %2(s64) = G_MERGE_VALUES %8(s32), %10(s32) +# X32-NEXT: RET 0 +body: | + bb.1 (%ir-block.0): + %0(s64) = IMPLICIT_DEF + %1(s64) = IMPLICIT_DEF + %2(s64) = G_ADD %0, %1 + RET 0 ... Index: test/CodeGen/X86/GlobalISel/regbankselect-X32.mir =================================================================== --- /dev/null +++ test/CodeGen/X86/GlobalISel/regbankselect-X32.mir @@ -0,0 +1,36 @@ +# RUN: llc -mtriple=i386-linux-gnu -global-isel -run-pass=regbankselect %s -o - | FileCheck %s --check-prefix=CHECK --check-prefix=FAST +# RUN: llc -mtriple=i386-linux-gnu -global-isel -regbankselect-greedy -run-pass=regbankselect %s -o - | FileCheck %s --check-prefix=CHECK --check-prefix=GREEDY + +--- | + define void @test_uadde_i32() { + ret void + } + +... +--- +name: test_uadde_i32 +# CHECK-LABEL: name: test_uadde_i32 +alignment: 4 +legalized: true +regBankSelected: false +# 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 } +# CHECK-NEXT: - { id: 4, class: gpr } +registers: + - { id: 0, class: _ } + - { id: 1, class: _ } + - { id: 2, class: _ } + - { id: 3, class: _ } + - { id: 4, class: _ } +body: | + bb.0 (%ir-block.0): + %0(s32) = IMPLICIT_DEF + %1(s32) = IMPLICIT_DEF + %2(s1) = IMPLICIT_DEF + %3(s32), %4(s1) = G_UADDE %0, %1, %2 + RET 0 + +... Index: test/CodeGen/X86/GlobalISel/select-add-x32.mir =================================================================== --- /dev/null +++ test/CodeGen/X86/GlobalISel/select-add-x32.mir @@ -0,0 +1,60 @@ +# RUN: llc -mtriple=i386-linux-gnu -global-isel -run-pass=instruction-select -verify-machineinstrs %s -o - | FileCheck %s --check-prefix=X32 +--- | + define i64 @test_add_i64(i64 %a, i64 %b) { + %r = add i64 %a, %b + ret i64 %r + } + +... +--- +name: test_add_i64 +# X32-LABEL: name: test_add_i64 +alignment: 4 +legalized: true +regBankSelected: true +# X32: registers: +# X32-NEXT: - { id: 0, class: gr32 } +# X32-NEXT: - { id: 1, class: gr32 } +# X32-NEXT: - { id: 2, class: gr32 } +# X32-NEXT: - { id: 3, class: gr32 } +# X32-NEXT: - { id: 4, class: gpr } +# X32-NEXT: - { id: 5, class: gr32 } +# X32-NEXT: - { id: 6, class: gpr } +# X32-NEXT: - { id: 7, class: gr32 } +# X32-NEXT: - { id: 8, class: gpr } +# X32-NEXT: - { id: 9, class: gpr } +registers: + - { id: 0, class: gpr } + - { id: 1, class: gpr } + - { id: 2, class: gpr } + - { id: 3, class: gpr } + - { id: 4, class: gpr } + - { id: 5, class: gpr } + - { id: 6, class: gpr } + - { id: 7, class: gpr } + - { id: 8, class: gpr } + - { id: 9, class: gpr } +# X32: %0 = IMPLICIT_DEF +# X32-NEXT: %1 = IMPLICIT_DEF +# X32-NEXT: %2 = IMPLICIT_DEF +# X32-NEXT: %3 = IMPLICIT_DEF +# X32-NEXT: %5 = ADD32rr %0, %2, implicit-def %eflags +# X32-NEXT: %7 = ADC32rr %1, %3, implicit-def %eflags, implicit %eflags +# X32-NEXT: %eax = COPY %5 +# X32-NEXT: %edx = COPY %7 +# X32-NEXT: RET 0, implicit %eax, implicit %edx +body: | + bb.0 (%ir-block.0): + %0(s32) = IMPLICIT_DEF + %1(s32) = IMPLICIT_DEF + %2(s32) = IMPLICIT_DEF + %3(s32) = IMPLICIT_DEF + %9(s8) = G_CONSTANT i8 0 + %4(s1) = G_TRUNC %9(s8) + %5(s32), %6(s1) = G_UADDE %0, %2, %4 + %7(s32), %8(s1) = G_UADDE %1, %3, %6 + %eax = COPY %5(s32) + %edx = COPY %7(s32) + RET 0, implicit %eax, implicit %edx + +...