Skip to content

Commit

Permalink
[InlineCost] Find more free binary operations
Browse files Browse the repository at this point in the history
Currently, inline cost model considers a binary operator as free only if both
its operands are constants. Some simple cases are missing such as a + 0, a - a,
etc. This patch modifies visitBinaryOperator() to call SimplifyBinOp() without
going through simplifyInstruction() to get rid of the constant restriction.
Thus, visitAnd() and visitOr() are not needed.

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

llvm-svn: 321366
  • Loading branch information
Haicheng Wu committed Dec 22, 2017
1 parent 5ca33a1 commit 6d14dfe
Show file tree
Hide file tree
Showing 2 changed files with 309 additions and 40 deletions.
58 changes: 18 additions & 40 deletions llvm/lib/Analysis/InlineCost.cpp
Expand Up @@ -249,8 +249,6 @@ class CallAnalyzer : public InstVisitor<CallAnalyzer, bool> {
bool visitCastInst(CastInst &I);
bool visitUnaryInstruction(UnaryInstruction &I);
bool visitCmpInst(CmpInst &I);
bool visitAnd(BinaryOperator &I);
bool visitOr(BinaryOperator &I);
bool visitSub(BinaryOperator &I);
bool visitBinaryOperator(BinaryOperator &I);
bool visitLoad(LoadInst &I);
Expand Down Expand Up @@ -1021,34 +1019,6 @@ bool CallAnalyzer::visitCmpInst(CmpInst &I) {
return false;
}

bool CallAnalyzer::visitOr(BinaryOperator &I) {
// This is necessary because the generic simplify instruction only works if
// both operands are constants.
for (unsigned i = 0; i < 2; ++i) {
if (ConstantInt *C = dyn_cast_or_null<ConstantInt>(
SimplifiedValues.lookup(I.getOperand(i))))
if (C->isAllOnesValue()) {
SimplifiedValues[&I] = C;
return true;
}
}
return Base::visitOr(I);
}

bool CallAnalyzer::visitAnd(BinaryOperator &I) {
// This is necessary because the generic simplify instruction only works if
// both operands are constants.
for (unsigned i = 0; i < 2; ++i) {
if (ConstantInt *C = dyn_cast_or_null<ConstantInt>(
SimplifiedValues.lookup(I.getOperand(i))))
if (C->isZero()) {
SimplifiedValues[&I] = C;
return true;
}
}
return Base::visitAnd(I);
}

bool CallAnalyzer::visitSub(BinaryOperator &I) {
// Try to handle a special case: we can fold computing the difference of two
// constant-related pointers.
Expand Down Expand Up @@ -1078,17 +1048,25 @@ bool CallAnalyzer::visitSub(BinaryOperator &I) {

bool CallAnalyzer::visitBinaryOperator(BinaryOperator &I) {
Value *LHS = I.getOperand(0), *RHS = I.getOperand(1);
auto Evaluate = [&](SmallVectorImpl<Constant *> &COps) {
Value *SimpleV = nullptr;
if (auto FI = dyn_cast<FPMathOperator>(&I))
SimpleV = SimplifyFPBinOp(I.getOpcode(), COps[0], COps[1],
FI->getFastMathFlags(), DL);
else
SimpleV = SimplifyBinOp(I.getOpcode(), COps[0], COps[1], DL);
return dyn_cast_or_null<Constant>(SimpleV);
};
Constant *CLHS = dyn_cast<Constant>(LHS);
if (!CLHS)
CLHS = SimplifiedValues.lookup(LHS);
Constant *CRHS = dyn_cast<Constant>(RHS);
if (!CRHS)
CRHS = SimplifiedValues.lookup(RHS);

Value *SimpleV = nullptr;
if (auto FI = dyn_cast<FPMathOperator>(&I))
SimpleV = SimplifyFPBinOp(I.getOpcode(), CLHS ? CLHS : LHS,
CRHS ? CRHS : RHS, FI->getFastMathFlags(), DL);
else
SimpleV =
SimplifyBinOp(I.getOpcode(), CLHS ? CLHS : LHS, CRHS ? CRHS : RHS, DL);

if (Constant *C = dyn_cast_or_null<Constant>(SimpleV))
SimplifiedValues[&I] = C;

if (simplifyInstruction(I, Evaluate))
if (SimpleV)
return true;

// Disable any SROA on arguments to arbitrary, unsimplified binary operators.
Expand Down
291 changes: 291 additions & 0 deletions llvm/test/Transforms/Inline/AArch64/binop.ll
@@ -0,0 +1,291 @@
; RUN: opt -inline -mtriple=aarch64--linux-gnu -S -o - < %s -inline-threshold=0 | FileCheck %s

target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64--linux-gnu"

declare void @pad()
@glbl = external global i32

define i32 @outer_add1(i32 %a) {
; CHECK-LABEL: @outer_add1(
; CHECK-NOT: call i32 @add
%C = call i32 @add(i32 %a, i32 0)
ret i32 %C
}

define i32 @outer_add2(i32 %a) {
; CHECK-LABEL: @outer_add2(
; CHECK-NOT: call i32 @add
%C = call i32 @add(i32 0, i32 %a)
ret i32 %C
}

define i32 @add(i32 %a, i32 %b) {
%add = add i32 %a, %b
call void @pad()
store i32 0, i32* @glbl
ret i32 %add
}



define i32 @outer_sub1(i32 %a) {
; CHECK-LABEL: @outer_sub1(
; CHECK-NOT: call i32 @sub1
%C = call i32 @sub1(i32 %a, i32 0)
ret i32 %C
}

define i32 @sub1(i32 %a, i32 %b) {
%sub = sub i32 %a, %b
call void @pad()
store i32 0, i32* @glbl
ret i32 %sub
}


define i32 @outer_sub2(i32 %a) {
; CHECK-LABEL: @outer_sub2(
; CHECK-NOT: call i32 @sub2
%C = call i32 @sub2(i32 %a)
ret i32 %C
}

define i32 @sub2(i32 %a) {
%sub = sub i32 %a, %a
call void @pad()
ret i32 %sub
}



define i32 @outer_mul1(i32 %a) {
; CHECK-LABEL: @outer_mul1(
; CHECK-NOT: call i32 @mul
%C = call i32 @mul(i32 %a, i32 0)
ret i32 %C
}

define i32 @outer_mul2(i32 %a) {
; CHECK-LABEL: @outer_mul2(
; CHECK-NOT: call i32 @mul
%C = call i32 @mul(i32 %a, i32 1)
ret i32 %C
}

define i32 @mul(i32 %a, i32 %b) {
%mul = mul i32 %a, %b
call void @pad()
store i32 0, i32* @glbl
ret i32 %mul
}



define i32 @outer_div1(i32 %a) {
; CHECK-LABEL: @outer_div1(
; CHECK-NOT: call i32 @div1
%C = call i32 @div1(i32 0, i32 %a)
ret i32 %C
}

define i32 @outer_div2(i32 %a) {
; CHECK-LABEL: @outer_div2(
; CHECK-NOT: call i32 @div1
%C = call i32 @div1(i32 %a, i32 1)
ret i32 %C
}

define i32 @div1(i32 %a, i32 %b) {
%div = sdiv i32 %a, %b
call void @pad()
store i32 0, i32* @glbl
ret i32 %div
}


define i32 @outer_div3(i32 %a) {
; CHECK-LABEL: @outer_div3(
; CHECK-NOT: call i32 @div
%C = call i32 @div2(i32 %a)
ret i32 %C
}

define i32 @div2(i32 %a) {
%div = sdiv i32 %a, %a
call void @pad()
ret i32 %div
}



define i32 @outer_rem1(i32 %a) {
; CHECK-LABEL: @outer_rem1(
; CHECK-NOT: call i32 @rem
%C = call i32 @rem1(i32 0, i32 %a)
ret i32 %C
}

define i32 @outer_rem2(i32 %a) {
; CHECK-LABEL: @outer_rem2(
; CHECK-NOT: call i32 @rem
%C = call i32 @rem1(i32 %a, i32 1)
ret i32 %C
}

define i32 @rem1(i32 %a, i32 %b) {
%rem = urem i32 %a, %b
call void @pad()
store i32 0, i32* @glbl
ret i32 %rem
}


define i32 @outer_rem3(i32 %a) {
; CHECK-LABEL: @outer_rem3(
; CHECK-NOT: call i32 @rem
%C = call i32 @rem2(i32 %a)
ret i32 %C
}

define i32 @rem2(i32 %a) {
%rem = urem i32 %a, %a
call void @pad()
ret i32 %rem
}



define i32 @outer_shl1(i32 %a) {
; CHECK-LABEL: @outer_shl1(
; CHECK-NOT: call i32 @shl
%C = call i32 @shl(i32 %a, i32 0)
ret i32 %C
}

define i32 @shl(i32 %a, i32 %b) {
%shl = shl i32 %a, %b
call void @pad()
store i32 0, i32* @glbl
ret i32 %shl
}



define i32 @outer_shr1(i32 %a) {
; CHECK-LABEL: @outer_shr1(
; CHECK-NOT: call i32 @shr
%C = call i32 @shr(i32 %a, i32 0)
ret i32 %C
}

define i32 @shr(i32 %a, i32 %b) {
%shr = ashr i32 %a, %b
call void @pad()
store i32 0, i32* @glbl
ret i32 %shr
}



define i1 @outer_and1(i1 %a) {
; check-label: @outer_and1(
; check-not: call i1 @and1
%c = call i1 @and1(i1 %a, i1 false)
ret i1 %c
}

define i1 @outer_and2(i1 %a) {
; check-label: @outer_and2(
; check-not: call i1 @and1
%c = call i1 @and1(i1 %a, i1 true)
ret i1 %c
}

define i1 @and1(i1 %a, i1 %b) {
%and = and i1 %a, %b
call void @pad()
store i32 0, i32* @glbl
ret i1 %and
}


define i1 @outer_and3(i1 %a) {
; check-label: @outer_and3(
; check-not: call i1 @and2
%c = call i1 @and2(i1 %a)
ret i1 %c
}

define i1 @and2(i1 %a) {
%and = and i1 %a, %a
call void @pad()
ret i1 %and
}



define i1 @outer_or1(i1 %a) {
; check-label: @outer_or1(
; check-not: call i1 @or1
%c = call i1 @or1(i1 %a, i1 false)
ret i1 %c
}

define i1 @outer_or2(i1 %a) {
; check-label: @outer_or2(
; check-not: call i1 @or1
%c = call i1 @or1(i1 %a, i1 true)
ret i1 %c
}

define i1 @or1(i1 %a, i1 %b) {
%or = or i1 %a, %b
call void @pad()
store i32 0, i32* @glbl
ret i1 %or
}


define i1 @outer_or3(i1 %a) {
; check-label: @outer_or3(
; check-not: call i1 @or2
%c = call i1 @or2(i1 %a)
ret i1 %c
}

define i1 @or2(i1 %a) {
%or = or i1 %a, %a
call void @pad()
ret i1 %or
}



define i1 @outer_xor1(i1 %a) {
; check-label: @outer_xor1(
; check-not: call i1 @xor
%c = call i1 @xor1(i1 %a, i1 false)
ret i1 %c
}

define i1 @xor1(i1 %a, i1 %b) {
%xor = xor i1 %a, %b
call void @pad()
store i32 0, i32* @glbl
ret i1 %xor
}


define i1 @outer_xor3(i1 %a) {
; check-label: @outer_xor3(
; check-not: call i1 @xor
%c = call i1 @xor2(i1 %a)
ret i1 %c
}

define i1 @xor2(i1 %a) {
%xor = xor i1 %a, %a
call void @pad()
ret i1 %xor
}

0 comments on commit 6d14dfe

Please sign in to comment.