Skip to content

Commit

Permalink
[GlobalISel][X86] Support G_LSHR/G_ASHR/G_SHL
Browse files Browse the repository at this point in the history
Support G_LSHR/G_ASHR/G_SHL. We have 3 variance for
shift instructions : shift gpr, shift imm, shift 1.
Currently GlobalIsel TableGen generate patterns for
shift imm and shift 1, but with shiftCount i8.
In G_LSHR/G_ASHR/G_SHL like LLVM-IR both arguments
has the same type, so for now only shift i8 can use
auto generated TableGen patterns.

The support of G_SHL/G_ASHR enables tryCombineSExt
from LegalizationArtifactCombiner.h to hit, which
results in different legalization for the following tests:
    LLVM :: CodeGen/X86/GlobalISel/ext-x86-64.ll
    LLVM :: CodeGen/X86/GlobalISel/gep.ll
    LLVM :: CodeGen/X86/GlobalISel/legalize-ext-x86-64.mir

-; X64-NEXT:    movsbl %dil, %eax
+; X64-NEXT:    movl $24, %ecx
+; X64-NEXT:    # kill: def $cl killed $ecx
+; X64-NEXT:    shll %cl, %edi
+; X64-NEXT:    movl $24, %ecx
+; X64-NEXT:    # kill: def $cl killed $ecx
+; X64-NEXT:    sarl %cl, %edi
+; X64-NEXT:    movl %edi, %eax

..which is not optimal and should be addressed later.

Rework of the patch by igorb

Reviewed By: igorb

Differential Revision: https://reviews.llvm.org/D44395

llvm-svn: 327499
  • Loading branch information
Alexander Ivchenko committed Mar 14, 2018
1 parent 0dd81ba commit 0bd4d8c
Show file tree
Hide file tree
Showing 16 changed files with 2,430 additions and 13 deletions.
85 changes: 85 additions & 0 deletions llvm/lib/Target/X86/X86InstructionSelector.cpp
Expand Up @@ -112,6 +112,8 @@ class X86InstructionSelector : public InstructionSelector {
bool materializeFP(MachineInstr &I, MachineRegisterInfo &MRI,
MachineFunction &MF) const;
bool selectImplicitDefOrPHI(MachineInstr &I, MachineRegisterInfo &MRI) const;
bool selectShift(MachineInstr &I, MachineRegisterInfo &MRI,
MachineFunction &MF) const;

// emit insert subreg instruction and insert it before MachineInstr &I
bool emitInsertSubreg(unsigned DstReg, unsigned SrcReg, MachineInstr &I,
Expand Down Expand Up @@ -373,6 +375,10 @@ bool X86InstructionSelector::select(MachineInstr &I,
case TargetOpcode::G_IMPLICIT_DEF:
case TargetOpcode::G_PHI:
return selectImplicitDefOrPHI(I, MRI);
case TargetOpcode::G_SHL:
case TargetOpcode::G_ASHR:
case TargetOpcode::G_LSHR:
return selectShift(I, MRI, MF);
}

return false;
Expand Down Expand Up @@ -1396,6 +1402,85 @@ bool X86InstructionSelector::selectImplicitDefOrPHI(
return true;
}

// Currently GlobalIsel TableGen generates patterns for shift imm and shift 1,
// but with shiftCount i8. In G_LSHR/G_ASHR/G_SHL like LLVM-IR both arguments
// has the same type, so for now only shift i8 can use auto generated
// TableGen patterns.
bool X86InstructionSelector::selectShift(MachineInstr &I,
MachineRegisterInfo &MRI,
MachineFunction &MF) const {

assert((I.getOpcode() == TargetOpcode::G_SHL ||
I.getOpcode() == TargetOpcode::G_ASHR ||
I.getOpcode() == TargetOpcode::G_LSHR) &&
"unexpected instruction");

unsigned DstReg = I.getOperand(0).getReg();
const LLT DstTy = MRI.getType(DstReg);
const RegisterBank &DstRB = *RBI.getRegBank(DstReg, MRI, TRI);

const static struct ShiftEntry {
unsigned SizeInBits;
unsigned CReg;
unsigned OpLSHR;
unsigned OpASHR;
unsigned OpSHL;
} OpTable[] = {
{8, X86::CL, X86::SHR8rCL, X86::SAR8rCL, X86::SHL8rCL}, // i8
{16, X86::CX, X86::SHR16rCL, X86::SAR16rCL, X86::SHL16rCL}, // i16
{32, X86::ECX, X86::SHR32rCL, X86::SAR32rCL, X86::SHL32rCL}, // i32
{64, X86::RCX, X86::SHR64rCL, X86::SAR64rCL, X86::SHL64rCL} // i64
};

if (DstRB.getID() != X86::GPRRegBankID)
return false;

auto ShiftEntryIt = std::find_if(
std::begin(OpTable), std::end(OpTable), [DstTy](const ShiftEntry &El) {
return El.SizeInBits == DstTy.getSizeInBits();
});
if (ShiftEntryIt == std::end(OpTable))
return false;

unsigned CReg = ShiftEntryIt->CReg;
unsigned Opcode = 0;
switch (I.getOpcode()) {
case TargetOpcode::G_SHL:
Opcode = ShiftEntryIt->OpSHL;
break;
case TargetOpcode::G_ASHR:
Opcode = ShiftEntryIt->OpASHR;
break;
case TargetOpcode::G_LSHR:
Opcode = ShiftEntryIt->OpLSHR;
break;
default:
return false;
}

unsigned Op0Reg = I.getOperand(1).getReg();
unsigned Op1Reg = I.getOperand(2).getReg();

BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(TargetOpcode::COPY),
ShiftEntryIt->CReg)
.addReg(Op1Reg);

// The shift instruction uses X86::CL. If we defined a super-register
// of X86::CL, emit a subreg KILL to precisely describe what we're doing here.
if (CReg != X86::CL)
BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(TargetOpcode::KILL),
X86::CL)
.addReg(CReg, RegState::Kill);

MachineInstr &ShiftInst =
*BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Opcode), DstReg)
.addReg(Op0Reg);

constrainSelectedInstRegOperands(ShiftInst, TII, TRI, RBI);
I.eraseFromParent();
return true;
}

InstructionSelector *
llvm::createX86InstructionSelector(const X86TargetMachine &TM,
X86Subtarget &Subtarget,
Expand Down
10 changes: 10 additions & 0 deletions llvm/lib/Target/X86/X86LegalizerInfo.cpp
Expand Up @@ -130,6 +130,11 @@ void X86LegalizerInfo::setLegalizerInfo32bit() {
.maxScalar(0, s32)
.widenScalarToNextPow2(0, /*Min*/ 8);
getActionDefinitionsBuilder(G_INTTOPTR).legalFor({s32, p0});

// Shifts
getActionDefinitionsBuilder({G_SHL, G_LSHR, G_ASHR})
.legalFor({s8, s16, s32})
.clampScalar(0, s8, s32);
}

// Control-flow
Expand Down Expand Up @@ -209,6 +214,11 @@ void X86LegalizerInfo::setLegalizerInfo64bit() {
// Comparison
setAction({G_ICMP, 1, s64}, Legal);

// Shifts
getActionDefinitionsBuilder({G_SHL, G_LSHR, G_ASHR})
.legalFor({s8, s16, s32, s64})
.clampScalar(0, s8, s64);

// Merge/Unmerge
setAction({G_MERGE_VALUES, s128}, Legal);
setAction({G_UNMERGE_VALUES, 1, s128}, Legal);
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Target/X86/X86RegisterBankInfo.cpp
Expand Up @@ -173,6 +173,10 @@ X86RegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
switch (Opc) {
case TargetOpcode::G_ADD:
case TargetOpcode::G_SUB:
case TargetOpcode::G_MUL:
case TargetOpcode::G_SHL:
case TargetOpcode::G_LSHR:
case TargetOpcode::G_ASHR:
return getSameOperandsMapping(MI, false);
break;
case TargetOpcode::G_FADD:
Expand Down
182 changes: 182 additions & 0 deletions llvm/test/CodeGen/X86/GlobalISel/ashr-scalar.ll
@@ -0,0 +1,182 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=x86_64-linux-gnu -global-isel -verify-machineinstrs < %s -o - | FileCheck %s --check-prefix=X64

define i64 @test_ashr_i64(i64 %arg1, i64 %arg2) {
; X64-LABEL: test_ashr_i64:
; X64: # %bb.0:
; X64-NEXT: movq %rsi, %rcx
; X64-NEXT: # kill: def $cl killed $rcx
; X64-NEXT: sarq %cl, %rdi
; X64-NEXT: movq %rdi, %rax
; X64-NEXT: retq
%res = ashr i64 %arg1, %arg2
ret i64 %res
}

define i64 @test_ashr_i64_imm(i64 %arg1) {
; X64-LABEL: test_ashr_i64_imm:
; X64: # %bb.0:
; X64-NEXT: movq $5, %rcx
; X64-NEXT: # kill: def $cl killed $rcx
; X64-NEXT: sarq %cl, %rdi
; X64-NEXT: movq %rdi, %rax
; X64-NEXT: retq
%res = ashr i64 %arg1, 5
ret i64 %res
}

define i64 @test_ashr_i64_imm1(i64 %arg1) {
; X64-LABEL: test_ashr_i64_imm1:
; X64: # %bb.0:
; X64-NEXT: movq $1, %rcx
; X64-NEXT: # kill: def $cl killed $rcx
; X64-NEXT: sarq %cl, %rdi
; X64-NEXT: movq %rdi, %rax
; X64-NEXT: retq
%res = ashr i64 %arg1, 1
ret i64 %res
}

define i32 @test_ashr_i32(i32 %arg1, i32 %arg2) {
; X64-LABEL: test_ashr_i32:
; X64: # %bb.0:
; X64-NEXT: movl %esi, %ecx
; X64-NEXT: # kill: def $cl killed $ecx
; X64-NEXT: sarl %cl, %edi
; X64-NEXT: movl %edi, %eax
; X64-NEXT: retq
%res = ashr i32 %arg1, %arg2
ret i32 %res
}

define i32 @test_ashr_i32_imm(i32 %arg1) {
; X64-LABEL: test_ashr_i32_imm:
; X64: # %bb.0:
; X64-NEXT: movl $5, %ecx
; X64-NEXT: # kill: def $cl killed $ecx
; X64-NEXT: sarl %cl, %edi
; X64-NEXT: movl %edi, %eax
; X64-NEXT: retq
%res = ashr i32 %arg1, 5
ret i32 %res
}

define i32 @test_ashr_i32_imm1(i32 %arg1) {
; X64-LABEL: test_ashr_i32_imm1:
; X64: # %bb.0:
; X64-NEXT: movl $1, %ecx
; X64-NEXT: # kill: def $cl killed $ecx
; X64-NEXT: sarl %cl, %edi
; X64-NEXT: movl %edi, %eax
; X64-NEXT: retq
%res = ashr i32 %arg1, 1
ret i32 %res
}

define i16 @test_ashr_i16(i32 %arg1, i32 %arg2) {
; X64-LABEL: test_ashr_i16:
; X64: # %bb.0:
; X64-NEXT: movl %esi, %ecx
; X64-NEXT: # kill: def $cl killed $cx
; X64-NEXT: sarw %cl, %di
; X64-NEXT: movl %edi, %eax
; X64-NEXT: retq
%a = trunc i32 %arg1 to i16
%a2 = trunc i32 %arg2 to i16
%res = ashr i16 %a, %a2
ret i16 %res
}

define i16 @test_ashr_i16_imm(i32 %arg1) {
; X64-LABEL: test_ashr_i16_imm:
; X64: # %bb.0:
; X64-NEXT: movw $5, %cx
; X64-NEXT: # kill: def $cl killed $cx
; X64-NEXT: sarw %cl, %di
; X64-NEXT: movl %edi, %eax
; X64-NEXT: retq
%a = trunc i32 %arg1 to i16
%res = ashr i16 %a, 5
ret i16 %res
}

define i16 @test_ashr_i16_imm1(i32 %arg1) {
; X64-LABEL: test_ashr_i16_imm1:
; X64: # %bb.0:
; X64-NEXT: movw $1, %cx
; X64-NEXT: # kill: def $cl killed $cx
; X64-NEXT: sarw %cl, %di
; X64-NEXT: movl %edi, %eax
; X64-NEXT: retq
%a = trunc i32 %arg1 to i16
%res = ashr i16 %a, 1
ret i16 %res
}

define i8 @test_ashr_i8(i32 %arg1, i32 %arg2) {
; X64-LABEL: test_ashr_i8:
; X64: # %bb.0:
; X64-NEXT: movl %esi, %ecx
; X64-NEXT: sarb %cl, %dil
; X64-NEXT: movl %edi, %eax
; X64-NEXT: retq
%a = trunc i32 %arg1 to i8
%a2 = trunc i32 %arg2 to i8
%res = ashr i8 %a, %a2
ret i8 %res
}

define i8 @test_ashr_i8_imm(i32 %arg1) {
; X64-LABEL: test_ashr_i8_imm:
; X64: # %bb.0:
; X64-NEXT: sarb $5, %dil
; X64-NEXT: movl %edi, %eax
; X64-NEXT: retq
%a = trunc i32 %arg1 to i8
%res = ashr i8 %a, 5
ret i8 %res
}

define i8 @test_ashr_i8_imm1(i32 %arg1) {
; X64-LABEL: test_ashr_i8_imm1:
; X64: # %bb.0:
; X64-NEXT: sarb %dil
; X64-NEXT: movl %edi, %eax
; X64-NEXT: retq
%a = trunc i32 %arg1 to i8
%res = ashr i8 %a, 1
ret i8 %res
}

define i1 @test_ashr_i1(i32 %arg1, i32 %arg2) {
; X64-LABEL: test_ashr_i1:
; X64: # %bb.0:
; X64-NEXT: shlb $7, %dil
; X64-NEXT: sarb $7, %dil
; X64-NEXT: shlb $7, %sil
; X64-NEXT: sarb $7, %sil
; X64-NEXT: movl %esi, %ecx
; X64-NEXT: sarb %cl, %dil
; X64-NEXT: movl %edi, %eax
; X64-NEXT: retq
%a = trunc i32 %arg1 to i1
%a2 = trunc i32 %arg2 to i1
%res = ashr i1 %a, %a2
ret i1 %res
}

define i1 @test_ashr_i1_imm1(i32 %arg1) {
; X64-LABEL: test_ashr_i1_imm1:
; X64: # %bb.0:
; X64-NEXT: movb $-1, %cl
; X64-NEXT: shlb $7, %dil
; X64-NEXT: sarb $7, %dil
; X64-NEXT: shlb $7, %cl
; X64-NEXT: sarb $7, %cl
; X64-NEXT: sarb %cl, %dil
; X64-NEXT: movl %edi, %eax
; X64-NEXT: retq
%a = trunc i32 %arg1 to i1
%res = ashr i1 %a, 1
ret i1 %res
}
18 changes: 16 additions & 2 deletions llvm/test/CodeGen/X86/GlobalISel/ext-x86-64.ll
Expand Up @@ -18,7 +18,14 @@ define i64 @test_zext_i1(i8 %a) {
define i64 @test_sext_i8(i8 %val) {
; X64-LABEL: test_sext_i8:
; X64: # %bb.0:
; X64-NEXT: movsbq %dil, %rax
; X64-NEXT: # kill: def $edi killed $edi def $rdi
; X64-NEXT: movq $56, %rcx
; X64-NEXT: # kill: def $cl killed $rcx
; X64-NEXT: shlq %cl, %rdi
; X64-NEXT: movq $56, %rcx
; X64-NEXT: # kill: def $cl killed $rcx
; X64-NEXT: sarq %cl, %rdi
; X64-NEXT: movq %rdi, %rax
; X64-NEXT: retq
%r = sext i8 %val to i64
ret i64 %r
Expand All @@ -27,7 +34,14 @@ define i64 @test_sext_i8(i8 %val) {
define i64 @test_sext_i16(i16 %val) {
; X64-LABEL: test_sext_i16:
; X64: # %bb.0:
; X64-NEXT: movswq %di, %rax
; X64-NEXT: # kill: def $edi killed $edi def $rdi
; X64-NEXT: movq $48, %rcx
; X64-NEXT: # kill: def $cl killed $rcx
; X64-NEXT: shlq %cl, %rdi
; X64-NEXT: movq $48, %rcx
; X64-NEXT: # kill: def $cl killed $rcx
; X64-NEXT: sarq %cl, %rdi
; X64-NEXT: movq %rdi, %rax
; X64-NEXT: retq
%r = sext i16 %val to i64
ret i64 %r
Expand Down
16 changes: 14 additions & 2 deletions llvm/test/CodeGen/X86/GlobalISel/ext.ll
Expand Up @@ -86,7 +86,13 @@ define i32 @test_zext_i16(i16 %val) {
define i32 @test_sext_i8(i8 %val) {
; X64-LABEL: test_sext_i8:
; X64: # %bb.0:
; X64-NEXT: movsbl %dil, %eax
; X64-NEXT: movl $24, %ecx
; X64-NEXT: # kill: def $cl killed $ecx
; X64-NEXT: shll %cl, %edi
; X64-NEXT: movl $24, %ecx
; X64-NEXT: # kill: def $cl killed $ecx
; X64-NEXT: sarl %cl, %edi
; X64-NEXT: movl %edi, %eax
; X64-NEXT: retq
;
; X32-LABEL: test_sext_i8:
Expand All @@ -100,7 +106,13 @@ define i32 @test_sext_i8(i8 %val) {
define i32 @test_sext_i16(i16 %val) {
; X64-LABEL: test_sext_i16:
; X64: # %bb.0:
; X64-NEXT: movswl %di, %eax
; X64-NEXT: movl $16, %ecx
; X64-NEXT: # kill: def $cl killed $ecx
; X64-NEXT: shll %cl, %edi
; X64-NEXT: movl $16, %ecx
; X64-NEXT: # kill: def $cl killed $ecx
; X64-NEXT: sarl %cl, %edi
; X64-NEXT: movl %edi, %eax
; X64-NEXT: retq
;
; X32-LABEL: test_sext_i16:
Expand Down

0 comments on commit 0bd4d8c

Please sign in to comment.