Skip to content

Commit 9697f93

Browse files
[InlineCost] model calls to llvm.is.constant* more carefully
llvm.is.constant* intrinsics are evaluated to 0 or 1 integral values. A common use case for llvm.is.constant comes from the higher level __builtin_constant_p. A common usage pattern of __builtin_constant_p in the Linux kernel is: void foo (int bar) { if (__builtin_constant_p(bar)) { // lots of code that will fold away to a constant. } else { // a little bit of code, usually a libcall. } } A minor issue in InlineCost calculations is when `bar` is _not_ Constant and still will not be after inlining, we don't discount the true branch and the inline cost of `foo` ends up being the cost of both branches together, rather than just the false branch. This leads to code like the above where inlining will not help prove bar Constant, but it still would be beneficial to inline foo, because the "true" branch is irrelevant from a cost perspective. For example, IPSCCP can sink a passed constant argument to foo: const int x = 42; void bar (void) { foo(x); } This improves our inlining decisions, and fixes a few head scratching cases were the disassembly shows a relatively small `foo` not inlined into a lone caller. We could further improve this modeling by tracking whether the argument to llvm.is.constant* is a parameter of the function, and if inlining would allow that parameter to become Constant. This idea is noted in a FIXME comment. Link: ClangBuiltLinux/linux#1302 Reviewed By: kazu Differential Revision: https://reviews.llvm.org/D111272
1 parent 96f9377 commit 9697f93

File tree

2 files changed

+63
-0
lines changed

2 files changed

+63
-0
lines changed

llvm/lib/Analysis/InlineCost.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,7 @@ class CallAnalyzer : public InstVisitor<CallAnalyzer, bool> {
387387
bool simplifyCallSite(Function *F, CallBase &Call);
388388
template <typename Callable>
389389
bool simplifyInstruction(Instruction &I, Callable Evaluate);
390+
bool simplifyIntrinsicCallIsConstant(CallBase &CB);
390391
ConstantInt *stripAndComputeInBoundsConstantOffsets(Value *&V);
391392

392393
/// Return true if the given argument to the function being considered for
@@ -1531,6 +1532,27 @@ bool CallAnalyzer::simplifyInstruction(Instruction &I, Callable Evaluate) {
15311532
return true;
15321533
}
15331534

1535+
/// Try to simplify a call to llvm.is.constant.
1536+
///
1537+
/// Duplicate the argument checking from CallAnalyzer::simplifyCallSite since
1538+
/// we expect calls of this specific intrinsic to be infrequent.
1539+
///
1540+
/// FIXME: Given that we know CB's parent (F) caller
1541+
/// (CandidateCall->getParent()->getParent()), we might be able to determine
1542+
/// whether inlining F into F's caller would change how the call to
1543+
/// llvm.is.constant would evaluate.
1544+
bool CallAnalyzer::simplifyIntrinsicCallIsConstant(CallBase &CB) {
1545+
Value *Arg = CB.getArgOperand(0);
1546+
auto *C = dyn_cast<Constant>(Arg);
1547+
1548+
if (!C)
1549+
C = dyn_cast_or_null<Constant>(SimplifiedValues.lookup(Arg));
1550+
1551+
Type *RT = CB.getFunctionType()->getReturnType();
1552+
SimplifiedValues[&CB] = ConstantInt::get(RT, C ? 1 : 0);
1553+
return true;
1554+
}
1555+
15341556
bool CallAnalyzer::visitBitCast(BitCastInst &I) {
15351557
// Propagate constants through bitcasts.
15361558
if (simplifyInstruction(I, [&](SmallVectorImpl<Constant *> &COps) {
@@ -2154,6 +2176,8 @@ bool CallAnalyzer::visitCallBase(CallBase &Call) {
21542176
if (auto *SROAArg = getSROAArgForValueOrNull(II->getOperand(0)))
21552177
SROAArgValues[II] = SROAArg;
21562178
return true;
2179+
case Intrinsic::is_constant:
2180+
return simplifyIntrinsicCallIsConstant(Call);
21572181
}
21582182
}
21592183

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
; RUN: opt %s -passes=inline -inline-threshold=20 -S | FileCheck %s
2+
3+
declare i1 @llvm.is.constant.i64(i64)
4+
declare void @foo()
5+
6+
define void @callee(i64 %val) {
7+
%cond = call i1 @llvm.is.constant.i64(i64 %val)
8+
br i1 %cond, label %cond.true, label %cond.false
9+
10+
cond.true:
11+
; Rack up costs with a couple of function calls so that this function
12+
; gets inlined only when @llvm.is.constant.i64 is folded. In reality,
13+
; the "then" clause of __builtin_constant_p tends to have statements
14+
; that fold very well, so the cost of the "then" clause is not a huge
15+
; concern.
16+
call void @foo()
17+
call void @foo()
18+
ret void
19+
20+
cond.false:
21+
ret void
22+
}
23+
24+
define void @caller(i64 %val) {
25+
; CHECK-LABEL: @caller(
26+
; CHECK-NEXT: [[COND_I:%.*]] = call i1 @llvm.is.constant.i64(i64 [[VAL:%.*]])
27+
; CHECK-NEXT: br i1 [[COND_I]], label %[[COND_TRUE_I:.*]], label %[[COND_FALSE_I:.*]]
28+
; CHECK: [[COND_TRUE_I]]:
29+
; CHECK-NEXT: call void @foo()
30+
; CHECK-NEXT: call void @foo()
31+
; CHECK-NEXT: br label %[[CALLEE_EXIT:.*]]
32+
; CHECK: [[COND_FALSE_I]]:
33+
; CHECK-NEXT: br label %[[CALLEE_EXIT]]
34+
; CHECK: [[CALLEE_EXIT]]:
35+
; CHECK-NEXT: ret void
36+
;
37+
call void @callee(i64 %val)
38+
ret void
39+
}

0 commit comments

Comments
 (0)