[InstCombine] add (sext i1 X), 1 --> zext (not X)

http://rise4fun.com/Alive/i8Q

A narrow bitwise logic op is obviously better than math for value tracking, 
and zext is better than sext. Typically, the 'not' will be folded into an 
icmp predicate.

The IR difference would even survive through codegen for x86, so we would see 
worse code:

https://godbolt.org/g/C14HMF

one_or_zero(int, int):                      # @one_or_zero(int, int)
        xorl    %eax, %eax
        cmpl    %esi, %edi
        setle   %al
        retq

one_or_zero_alt(int, int):                  # @one_or_zero_alt(int, int)
        xorl    %ecx, %ecx
        cmpl    %esi, %edi
        setg    %cl
        movl    $1, %eax
        subl    %ecx, %eax
        retq

llvm-svn: 306243
This commit is contained in:
Sanjay Patel 2017-06-25 14:15:28 +00:00
parent 72f991cded
commit 2f3ead7adc
2 changed files with 22 additions and 17 deletions

View File

@ -988,15 +988,24 @@ static Instruction *foldAddWithConstant(BinaryOperator &Add,
return new ZExtInst(Builder.CreateNUWAdd(X, NewC), Ty); return new ZExtInst(Builder.CreateNUWAdd(X, NewC), Ty);
} }
// Shifts and add used to flip and mask off the low bit: if (C->isOneValue() && Op0->hasOneUse()) {
// add (ashr (shl i32 X, 31), 31), 1 --> and (not X), 1 // add (sext i1 X), 1 --> zext (not X)
const APInt *C3; // TODO: The smallest IR representation is (select X, 0, 1), and that would
if (C->isOneValue() && // not require the one-use check. But we need to remove a transform in
match(Op0, // visitSelect and make sure that IR value tracking for select is equal or
m_OneUse(m_AShr(m_Shl(m_Value(X), m_APInt(C2)), m_APInt(C3)))) && // better than for these ops.
C2 == C3 && *C2 == Ty->getScalarSizeInBits() - 1) { if (match(Op0, m_SExt(m_Value(X))) &&
Value *NotX = Builder.CreateNot(X); X->getType()->getScalarSizeInBits() == 1)
return BinaryOperator::CreateAnd(NotX, ConstantInt::get(Ty, 1)); return new ZExtInst(Builder.CreateNot(X), Ty);
// Shifts and add used to flip and mask off the low bit:
// add (ashr (shl i32 X, 31), 31), 1 --> and (not X), 1
const APInt *C3;
if (match(Op0, m_AShr(m_Shl(m_Value(X), m_APInt(C2)), m_APInt(C3))) &&
C2 == C3 && *C2 == Ty->getScalarSizeInBits() - 1) {
Value *NotX = Builder.CreateNot(X);
return BinaryOperator::CreateAnd(NotX, ConstantInt::get(Ty, 1));
}
} }
return nullptr; return nullptr;

View File

@ -1,12 +1,10 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -instcombine -S | FileCheck %s ; RUN: opt < %s -instcombine -S | FileCheck %s
; TODO: This should be canonicalized to either a select or xor+zext.
define i32 @select_0_or_1_from_bool(i1 %x) { define i32 @select_0_or_1_from_bool(i1 %x) {
; CHECK-LABEL: @select_0_or_1_from_bool( ; CHECK-LABEL: @select_0_or_1_from_bool(
; CHECK-NEXT: [[EXT:%.*]] = sext i1 %x to i32 ; CHECK-NEXT: [[TMP1:%.*]] = xor i1 %x, true
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[EXT]], 1 ; CHECK-NEXT: [[ADD:%.*]] = zext i1 [[TMP1]] to i32
; CHECK-NEXT: ret i32 [[ADD]] ; CHECK-NEXT: ret i32 [[ADD]]
; ;
%ext = sext i1 %x to i32 %ext = sext i1 %x to i32
@ -14,12 +12,10 @@ define i32 @select_0_or_1_from_bool(i1 %x) {
ret i32 %add ret i32 %add
} }
; TODO: This should be canonicalized to either a select or xor+zext.
define <2 x i32> @select_0_or_1_from_bool_vec(<2 x i1> %x) { define <2 x i32> @select_0_or_1_from_bool_vec(<2 x i1> %x) {
; CHECK-LABEL: @select_0_or_1_from_bool_vec( ; CHECK-LABEL: @select_0_or_1_from_bool_vec(
; CHECK-NEXT: [[EXT:%.*]] = sext <2 x i1> %x to <2 x i32> ; CHECK-NEXT: [[TMP1:%.*]] = xor <2 x i1> %x, <i1 true, i1 true>
; CHECK-NEXT: [[ADD:%.*]] = add nsw <2 x i32> [[EXT]], <i32 1, i32 1> ; CHECK-NEXT: [[ADD:%.*]] = zext <2 x i1> [[TMP1]] to <2 x i32>
; CHECK-NEXT: ret <2 x i32> [[ADD]] ; CHECK-NEXT: ret <2 x i32> [[ADD]]
; ;
%ext = sext <2 x i1> %x to <2 x i32> %ext = sext <2 x i1> %x to <2 x i32>