#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/CGSCCPassManager.h"
#include "llvm/Analysis/CallGraph.h"
+#include "llvm/Analysis/InlineCost.h"
#include "llvm/Analysis/LazyCallGraph.h"
#include "llvm/Analysis/MustExecute.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
/// The underlying CGSCC, or null if not available.
SetVector<Function *> *CGSCC;
+ /// Set of inlineable functions
+ SmallPtrSet<const Function *, 8> InlineableFunctions;
+
/// Give the Attributor access to the members so
/// Attributor::identifyDefaultAbstractAttributes(...) can initialize them.
friend struct Attributor;
/// through the information cache interface *prior* to looking at them.
void initializeInformationCache(Function &F);
+ /// Determine whether the function \p F is IPO amendable
+ ///
+ /// If a function is exactly defined or it has alwaysinline attribute
+ /// and is viable to be inlined, we say it is IPO amendable
+ bool isFunctionIPOAmendable(const Function &F) {
+ return F.hasExactDefinition() || InfoCache.InlineableFunctions.count(&F);
+ }
+
/// Mark the internal function \p F as live.
///
/// This will trigger the identification and initialization of attributes for
// TODO: We could always determine abstract attributes and if sufficient
// information was found we could duplicate the functions that do not
// have an exact definition.
- if (IsFnInterface && (!FnScope || !FnScope->hasExactDefinition()))
+ if (IsFnInterface && (!FnScope || !A.isFunctionIPOAmendable(*FnScope)))
this->getState().indicatePessimisticFixpoint();
}
/// Return an assumed constant for the assocaited value a program point \p
/// CtxI.
Optional<ConstantInt *>
- getAssumedConstantInt(Attributor &A, const Instruction *CtxI = nullptr) const {
+ getAssumedConstantInt(Attributor &A,
+ const Instruction *CtxI = nullptr) const {
ConstantRange RangeV = getAssumedConstantRange(A, CtxI);
if (auto *C = RangeV.getSingleElement())
return cast<ConstantInt>(
}
}
- if (!F->hasExactDefinition())
+ if (!A.isFunctionIPOAmendable(*F))
indicatePessimisticFixpoint();
}
// FIXME: Any cycle is regarded as endless loop for now.
// We have to allow some patterns.
static bool containsPossiblyEndlessLoop(Function *F) {
- return !F || !F->hasExactDefinition() || containsCycle(*F);
+ return containsCycle(*F);
}
struct AAWillReturnImpl : public AAWillReturn {
AAWillReturn::initialize(A);
Function *F = getAssociatedFunction();
- if (containsPossiblyEndlessLoop(F))
+ if (!F || !A.isFunctionIPOAmendable(*F) || containsPossiblyEndlessLoop(F))
indicatePessimisticFixpoint();
}
/// See AbstractAttribute::initialize(...).
void initialize(Attributor &A) override {
- if (!getAssociatedFunction()->hasExactDefinition())
+ if (!A.isFunctionIPOAmendable(*getAssociatedFunction()))
indicatePessimisticFixpoint();
}
const IRPosition &IRP = this->getIRPosition();
bool IsFnInterface = IRP.isFnInterfaceKind();
- const Function *FnScope = IRP.getAnchorScope();
- if (IsFnInterface && (!FnScope || !FnScope->hasExactDefinition()))
+ Function *FnScope = IRP.getAnchorScope();
+ if (IsFnInterface && (!FnScope || !A.isFunctionIPOAmendable(*FnScope)))
indicatePessimisticFixpoint();
}
if (getIRPosition().isFnInterfaceKind() &&
(!getAssociatedFunction() ||
- !getAssociatedFunction()->hasExactDefinition()))
+ !A.isFunctionIPOAmendable(*getAssociatedFunction())))
indicatePessimisticFixpoint();
}
}
Function *AnchorScope = getAnchorScope();
if (isFnInterfaceKind() &&
- (!AnchorScope || !AnchorScope->hasExactDefinition())) {
+ (!AnchorScope || !A.isFunctionIPOAmendable(*AnchorScope))) {
indicatePessimisticFixpoint();
return;
}
// Initialize the use vector with all direct uses of the associated value.
Argument *Arg = getAssociatedArgument();
- if (!Arg || !Arg->getParent()->hasExactDefinition()) {
+ if (!Arg || !A.isFunctionIPOAmendable(*(Arg->getParent()))) {
indicatePessimisticFixpoint();
} else {
// Initialize the use vector with all direct uses of the associated value.
void initialize(Attributor &A) override {
AAMemoryBehaviorImpl::initialize(A);
Function *F = getAssociatedFunction();
- if (!F || !F->hasExactDefinition())
+ if (!F || !A.isFunctionIPOAmendable(*F))
indicatePessimisticFixpoint();
}
void initialize(Attributor &A) override {
AAMemoryLocationImpl::initialize(A);
Function *F = getAssociatedFunction();
- if (!F || !F->hasExactDefinition())
+ if (!F || !A.isFunctionIPOAmendable(*F))
indicatePessimisticFixpoint();
}
if (I.mayReadOrWriteMemory())
ReadOrWriteInsts.push_back(&I);
}
+
+ if (F.hasFnAttribute(Attribute::AlwaysInline) &&
+ isInlineViable(F).isSuccess())
+ InfoCache.InlineableFunctions.insert(&F);
}
void Attributor::recordDependence(const AbstractAttribute &FromAA,
--- /dev/null
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -attributor -attributor-disable=false -attributor-annotate-decl-cs -attributor-max-iterations=5 -S < %s | FileCheck %s --check-prefix=CHECK
+;
+; When a function is marked `alwaysinline` and is able to be inlined,
+; we can IPO its boundaries
+
+; the function is not exactly defined, and marked alwaysinline and can be inlined,
+; so the function can be analyzed
+; CHECK: Function Attrs: alwaysinline nofree nosync nounwind readnone willreturn
+define linkonce void @inner1() alwaysinline {
+; CHECK-LABEL: @inner1(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: ret void
+;
+entry:
+ ret void
+}
+
+; CHECK: Function Attrs: nofree nosync nounwind readnone willreturn
+define void @outer1() {
+; CHECK-LABEL: @outer1(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: ret void
+;
+entry:
+ call void @inner1()
+ ret void
+}
+
+; The function is not alwaysinline and is not exactly defined
+; so it will not be analyzed
+; CHECK-NOT: Function Attrs:
+define linkonce i32 @inner2() {
+; CHECK-LABEL: @inner2(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: ret i32 1
+;
+entry:
+ ret i32 1
+}
+
+; CHECK-NOT: Function Attrs
+define i32 @outer2() {
+; CHECK-LABEL: @outer2(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[R:%.*]] = call i32 @inner2() #2
+; CHECK-NEXT: ret i32 [[R]]
+;
+entry:
+ %r = call i32 @inner2() alwaysinline
+ ret i32 %r
+}
+
+; This function cannot be inlined although it is marked alwaysinline
+; it is `unexactly defined` and alwaysinline but cannot be inlined.
+; so it will not be analyzed
+; CHECK: Function Attrs:
+; CHECK-NOT: nofree nosync nounwind readnone
+define linkonce i32 @inner3(i8* %addr) alwaysinline {
+; CHECK-LABEL: @inner3(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: indirectbr i8* [[ADDR:%.*]], [label [[ONE:%.*]], label %two]
+; CHECK: one:
+; CHECK-NEXT: ret i32 42
+; CHECK: two:
+; CHECK-NEXT: ret i32 44
+;
+entry:
+ indirectbr i8* %addr, [ label %one, label %two ]
+
+one:
+ ret i32 42
+
+two:
+ ret i32 44
+}
+
+; CHECK-NOT: Function Attrs:
+define i32 @outer3(i32 %x) {
+; CHECK-LABEL: @outer3(
+; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[X:%.*]], 42
+; CHECK-NEXT: [[ADDR:%.*]] = select i1 [[CMP]], i8* blockaddress(@inner3, [[ONE:%.*]]), i8* blockaddress(@inner3, [[TWO:%.*]])
+; CHECK-NEXT: [[CALL:%.*]] = call i32 @inner3(i8* [[ADDR]])
+; CHECK-NEXT: ret i32 [[CALL]]
+;
+ %cmp = icmp slt i32 %x, 42
+ %addr = select i1 %cmp, i8* blockaddress(@inner3, %one), i8* blockaddress(@inner3, %two)
+ %call = call i32 @inner3(i8* %addr)
+ ret i32 %call
+}