1. merge fast-isel-shift-imm.ll into fast-isel-x86-64.ll

2. implement rdar://9289501 - fast isel should fold trivial multiplies to shifts
3. teach tblgen to handle shift immediates that are different sizes than the 
   shifted operands, eliminating some code from the X86 fast isel backend.
4. Have FastISel::SelectBinaryOp use (the poorly named) FastEmit_ri_ function
   instead of FastEmit_ri to simplify code.

llvm-svn: 129666
This commit is contained in:
Chris Lattner 2011-04-17 20:23:29 +00:00
parent a9e4117991
commit b53ccb8e36
6 changed files with 95 additions and 61 deletions

View File

@ -339,35 +339,35 @@ bool FastISel::SelectBinaryOp(const User *I, unsigned ISDOpcode) {
bool Op1IsKill = hasTrivialKill(I->getOperand(1));
unsigned ResultReg = FastEmit_ri(VT.getSimpleVT(), VT.getSimpleVT(),
ISDOpcode, Op1, Op1IsKill,
CI->getZExtValue());
if (ResultReg != 0) {
unsigned ResultReg = FastEmit_ri_(VT.getSimpleVT(), ISDOpcode, Op1,
Op1IsKill, CI->getZExtValue(),
VT.getSimpleVT());
if (ResultReg == 0) return false;
// We successfully emitted code for the given LLVM Instruction.
UpdateValueMap(I, ResultReg);
return true;
}
}
unsigned Op0 = getRegForValue(I->getOperand(0));
if (Op0 == 0)
// Unhandled operand. Halt "fast" selection and bail.
if (Op0 == 0) // Unhandled operand. Halt "fast" selection and bail.
return false;
bool Op0IsKill = hasTrivialKill(I->getOperand(0));
// Check if the second operand is a constant and handle it appropriately.
if (ConstantInt *CI = dyn_cast<ConstantInt>(I->getOperand(1))) {
unsigned ResultReg = FastEmit_ri(VT.getSimpleVT(), VT.getSimpleVT(),
ISDOpcode, Op0, Op0IsKill,
CI->getZExtValue());
if (ResultReg != 0) {
uint64_t Imm = CI->getZExtValue();
unsigned ResultReg = FastEmit_ri_(VT.getSimpleVT(), ISDOpcode, Op0,
Op0IsKill, Imm, VT.getSimpleVT());
if (ResultReg == 0) return false;
// We successfully emitted code for the given LLVM Instruction.
UpdateValueMap(I, ResultReg);
return true;
}
}
// Check if the second operand is a constant float.
if (ConstantFP *CF = dyn_cast<ConstantFP>(I->getOperand(1))) {
@ -986,6 +986,18 @@ unsigned FastISel::FastEmit_rri(MVT, MVT,
unsigned FastISel::FastEmit_ri_(MVT VT, unsigned Opcode,
unsigned Op0, bool Op0IsKill,
uint64_t Imm, MVT ImmType) {
// If this is a multiply by a power of two, emit this as a shift left.
if (Opcode == ISD::MUL && isPowerOf2_64(Imm)) {
Opcode = ISD::SHL;
Imm = Log2_64(Imm);
}
// Horrible hack (to be removed), check to make sure shift amounts are
// in-range.
if ((Opcode == ISD::SHL || Opcode == ISD::SRA || Opcode == ISD::SRL) &&
Imm >= VT.getSizeInBits())
return 0;
// First check if immediate type is legal. If not, we can't use the ri form.
unsigned ResultReg = FastEmit_ri(VT, VT, Opcode, Op0, Op0IsKill, Imm);
if (ResultReg != 0)

View File

@ -1084,42 +1084,42 @@ bool X86FastISel::X86SelectBranch(const Instruction *I) {
}
bool X86FastISel::X86SelectShift(const Instruction *I) {
unsigned CReg = 0, OpReg = 0, OpImm = 0;
unsigned CReg = 0, OpReg = 0;
const TargetRegisterClass *RC = NULL;
if (I->getType()->isIntegerTy(8)) {
CReg = X86::CL;
RC = &X86::GR8RegClass;
switch (I->getOpcode()) {
case Instruction::LShr: OpReg = X86::SHR8rCL; OpImm = X86::SHR8ri; break;
case Instruction::AShr: OpReg = X86::SAR8rCL; OpImm = X86::SAR8ri; break;
case Instruction::Shl: OpReg = X86::SHL8rCL; OpImm = X86::SHL8ri; break;
case Instruction::LShr: OpReg = X86::SHR8rCL; break;
case Instruction::AShr: OpReg = X86::SAR8rCL; break;
case Instruction::Shl: OpReg = X86::SHL8rCL; break;
default: return false;
}
} else if (I->getType()->isIntegerTy(16)) {
CReg = X86::CX;
RC = &X86::GR16RegClass;
switch (I->getOpcode()) {
case Instruction::LShr: OpReg = X86::SHR16rCL; OpImm = X86::SHR16ri; break;
case Instruction::AShr: OpReg = X86::SAR16rCL; OpImm = X86::SAR16ri; break;
case Instruction::Shl: OpReg = X86::SHL16rCL; OpImm = X86::SHL16ri; break;
case Instruction::LShr: OpReg = X86::SHR16rCL; break;
case Instruction::AShr: OpReg = X86::SAR16rCL; break;
case Instruction::Shl: OpReg = X86::SHL16rCL; break;
default: return false;
}
} else if (I->getType()->isIntegerTy(32)) {
CReg = X86::ECX;
RC = &X86::GR32RegClass;
switch (I->getOpcode()) {
case Instruction::LShr: OpReg = X86::SHR32rCL; OpImm = X86::SHR32ri; break;
case Instruction::AShr: OpReg = X86::SAR32rCL; OpImm = X86::SAR32ri; break;
case Instruction::Shl: OpReg = X86::SHL32rCL; OpImm = X86::SHL32ri; break;
case Instruction::LShr: OpReg = X86::SHR32rCL; break;
case Instruction::AShr: OpReg = X86::SAR32rCL; break;
case Instruction::Shl: OpReg = X86::SHL32rCL; break;
default: return false;
}
} else if (I->getType()->isIntegerTy(64)) {
CReg = X86::RCX;
RC = &X86::GR64RegClass;
switch (I->getOpcode()) {
case Instruction::LShr: OpReg = X86::SHR64rCL; OpImm = X86::SHR64ri; break;
case Instruction::AShr: OpReg = X86::SAR64rCL; OpImm = X86::SAR64ri; break;
case Instruction::Shl: OpReg = X86::SHL64rCL; OpImm = X86::SHL64ri; break;
case Instruction::LShr: OpReg = X86::SHR64rCL; break;
case Instruction::AShr: OpReg = X86::SAR64rCL; break;
case Instruction::Shl: OpReg = X86::SHL64rCL; break;
default: return false;
}
} else {
@ -1133,15 +1133,6 @@ bool X86FastISel::X86SelectShift(const Instruction *I) {
unsigned Op0Reg = getRegForValue(I->getOperand(0));
if (Op0Reg == 0) return false;
// Fold immediate in shl(x,3).
if (const ConstantInt *CI = dyn_cast<ConstantInt>(I->getOperand(1))) {
unsigned ResultReg = createResultReg(RC);
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DL, TII.get(OpImm),
ResultReg).addReg(Op0Reg).addImm(CI->getZExtValue() & 0xff);
UpdateValueMap(I, ResultReg);
return true;
}
unsigned Op1Reg = getRegForValue(I->getOperand(1));
if (Op1Reg == 0) return false;
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DL, TII.get(TargetOpcode::COPY),

View File

@ -1,8 +0,0 @@
; RUN: llc < %s -march=x86 -O0 | grep {sarl \$80, %e}
; PR3242
define void @foo(i32 %x, i32* %p) nounwind {
%y = ashr i32 %x, 50000
store i32 %y, i32* %p
ret void
}

View File

@ -1,4 +1,4 @@
; RUN: llc < %s -fast-isel -O0 -regalloc=fast | FileCheck %s
; RUN: llc < %s -fast-isel -O0 -regalloc=fast -asm-verbose=0 | FileCheck %s
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
target triple = "x86_64-apple-darwin10.0.0"
@ -61,3 +61,35 @@ define i32 @test4(i64 %idxprom9) nounwind {
; CHECK-NEXT: movzbl (%rax,%rdi), %eax
; CHECK-NEXT: ret
}
; PR3242 - Out of range shifts should not be folded by fastisel.
define void @test5(i32 %x, i32* %p) nounwind {
%y = ashr i32 %x, 50000
store i32 %y, i32* %p
ret void
; CHECK: test5:
; CHECK: movl $50000, %ecx
; CHECK: sarl %cl, %edi
; CHECK: ret
}
; rdar://9289501 - fast isel should fold trivial multiplies to shifts.
define i64 @test6(i64 %x) nounwind ssp {
entry:
%mul = mul nsw i64 %x, 8
ret i64 %mul
; CHECK: test6:
; CHECK: leaq (,%rdi,8), %rax
}
define i32 @test7(i32 %x) nounwind ssp {
entry:
%mul = mul nsw i32 %x, 8
ret i32 %mul
; CHECK: test7:
; CHECK: leal (,%rdi,8), %eax
}

View File

@ -4,10 +4,10 @@ target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f3
target triple = "x86_64-apple-darwin10.0.0"
; CHECK: f0:
; CHECK: addq %rax, (%rdi)
; CHECK: # encoding: [0xf0,0x48,0x01,0x07]
; CHECK: addq %rcx, (%rdi)
; CHECK: # encoding: [0xf0,0x48,0x01,0x0f]
; CHECK: ret
define void @f0(i64* %a0) {
define void @f0(i64* %a0) nounwind {
%t0 = and i64 1, 1
call void @llvm.memory.barrier(i1 true, i1 true, i1 true, i1 true, i1 true) nounwind
%1 = call i64 @llvm.atomic.load.add.i64.p0i64(i64* %a0, i64 %t0) nounwind

View File

@ -52,8 +52,7 @@ struct OperandsSignature {
/// of the Operands array accordingly. Return true if all the operands
/// are supported, false otherwise.
///
bool initialize(TreePatternNode *InstPatNode,
const CodeGenTarget &Target,
bool initialize(TreePatternNode *InstPatNode, const CodeGenTarget &Target,
MVT::SimpleValueType VT) {
if (!InstPatNode->isLeaf()) {
@ -74,13 +73,7 @@ struct OperandsSignature {
// For now, filter out any operand with a predicate.
// For now, filter out any operand with multiple values.
if (!Op->getPredicateFns().empty() ||
Op->getNumTypes() != 1)
return false;
assert(Op->hasTypeSet(0) && "Type infererence not done?");
// For now, all the operands must have the same type.
if (Op->getType(0) != VT)
if (!Op->getPredicateFns().empty() || Op->getNumTypes() != 1)
return false;
if (!Op->isLeaf()) {
@ -95,6 +88,15 @@ struct OperandsSignature {
// For now, ignore other non-leaf nodes.
return false;
}
assert(Op->hasTypeSet(0) && "Type infererence not done?");
// For now, all the operands must have the same type (if they aren't
// immediates). Note that this causes us to reject variable sized shifts
// on X86.
if (Op->getType(0) != VT)
return false;
DefInit *OpDI = dynamic_cast<DefInit*>(Op->getLeafValue());
if (!OpDI)
return false;
@ -322,6 +324,11 @@ void FastISelMap::CollectPatterns(CodeGenDAGPatterns &CGP) {
VT = InstPatNode->getChild(0)->getType(0);
}
if (InstPatOp->getName() =="shl") {
InstPatNode->dump();
}
// For now, filter out instructions which just set a register to
// an Operand or an immediate, like MOV32ri.
if (InstPatOp->isSubClassOf("Operand"))