Teach instcombine to transform

(X >s -1) ? C1 : C2 and (X <s  0) ? C2 : C1
into ((X >>s 31) & (C2 - C1)) + C1, avoiding the conditional.

This optimization could be extended to take non-const C1 and C2 but we better
stay conservative to avoid code size bloat for now.

for
int sel(int n) {
     return n >= 0 ? 60 : 100;
}

we now generate
  sarl  $31, %edi
  andl  $40, %edi
  leal  60(%rdi), %eax

instead of
  testl %edi, %edi
  movl  $60, %ecx
  movl  $100, %eax
  cmovnsl %ecx, %eax

llvm-svn: 107866
This commit is contained in:
Benjamin Kramer 2010-07-08 11:39:10 +00:00
parent 896788595d
commit 27eb255a70
3 changed files with 63 additions and 11 deletions

View File

@ -906,17 +906,6 @@ The expression should optimize to something like
//===---------------------------------------------------------------------===//
From GCC Bug 3756:
int
pn (int n)
{
return (n >= 0 ? 1 : -1);
}
Should combine to (n >> 31) | 1. Currently not optimized with "clang
-emit-llvm-bc | opt -std-compile-opts | llc".
//===---------------------------------------------------------------------===//
void a(int variable)
{
if (variable == 4 || variable == 6)

View File

@ -329,6 +329,37 @@ Instruction *InstCombiner::visitSelectInstWithICmp(SelectInst &SI,
}
}
// Transform (X >s -1) ? C1 : C2 --> ((X >>s 31) & (C2 - C1)) + C1
// and (X <s 0) ? C2 : C1 --> ((X >>s 31) & (C2 - C1)) + C1
// FIXME: Type and constness constraints could be lifted, but we have to
// watch code size carefully. We should consider xor instead of
// sub/add when we decide to do that.
if (const IntegerType *Ty = dyn_cast<IntegerType>(CmpLHS->getType())) {
if (TrueVal->getType() == Ty) {
if (ConstantInt *Cmp = dyn_cast<ConstantInt>(CmpRHS)) {
ConstantInt *C1 = NULL, *C2 = NULL;
if (Pred == ICmpInst::ICMP_SGT && Cmp->isAllOnesValue()) {
C1 = dyn_cast<ConstantInt>(TrueVal);
C2 = dyn_cast<ConstantInt>(FalseVal);
} else if (Pred == ICmpInst::ICMP_SLT && Cmp->isNullValue()) {
C1 = dyn_cast<ConstantInt>(FalseVal);
C2 = dyn_cast<ConstantInt>(TrueVal);
}
if (C1 && C2) {
// This shift results in either -1 or 0.
Value *AShr = Builder->CreateAShr(CmpLHS, Ty->getBitWidth()-1);
// Check if we can express the operation with a single or.
if (C2->isAllOnesValue())
return ReplaceInstUsesWith(SI, Builder->CreateOr(AShr, C1));
Value *And = Builder->CreateAnd(AShr, C2->getValue()-C1->getValue());
return ReplaceInstUsesWith(SI, Builder->CreateAdd(And, C1));
}
}
}
}
if (CmpLHS == TrueVal && CmpRHS == FalseVal) {
// Transform (X == Y) ? X : Y -> Y
if (Pred == ICmpInst::ICMP_EQ)

View File

@ -438,3 +438,35 @@ define i32 @test34(i32 %x, i32 %y) {
; CHECK: @test34
; CHECK: ret i32 %x
}
define i32 @test35(i32 %x) {
%cmp = icmp sge i32 %x, 0
%cond = select i1 %cmp, i32 60, i32 100
ret i32 %cond
; CHECK: @test35
; CHECK: ashr i32 %x, 31
; CHECK: and i32 {{.*}}, 40
; CHECK: add i32 {{.*}}, 60
; CHECK: ret
}
define i32 @test36(i32 %x) {
%cmp = icmp slt i32 %x, 0
%cond = select i1 %cmp, i32 60, i32 100
ret i32 %cond
; CHECK: @test36
; CHECK: ashr i32 %x, 31
; CHECK: and i32 {{.*}}, -40
; CHECK: add i32 {{.*}}, 100
; CHECK: ret
}
define i32 @test37(i32 %x) {
%cmp = icmp sgt i32 %x, -1
%cond = select i1 %cmp, i32 1, i32 -1
ret i32 %cond
; CHECK: @test37
; CHECK: ashr i32 %x, 31
; CHECK: or i32 {{.*}}, 1
; CHECK: ret
}