Index: llvm/include/llvm/Transforms/IPO/Attributor.h =================================================================== --- llvm/include/llvm/Transforms/IPO/Attributor.h +++ llvm/include/llvm/Transforms/IPO/Attributor.h @@ -103,6 +103,7 @@ #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" @@ -1704,7 +1705,7 @@ } bool IsFnInterface = IRP.isFnInterfaceKind(); - const Function *FnScope = IRP.getAnchorScope(); + Function *FnScope = IRP.getAnchorScope(); // TODO: Not all attributes require an exact definition. Find a way to // enable deduction for some but not all attributes in case the // definition might be changed at runtime, see also @@ -1712,8 +1713,15 @@ // 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 || !FnScope->hasExactDefinition())) { + if (FnScope->hasFnAttribute(Attribute::AlwaysInline)) { + // check if it is OK to inline, if so, do fix-point analysis for the + // function + if (isInlineViable(*FnScope).isSuccess()) + return; + } this->getState().indicatePessimisticFixpoint(); + } } /// See AbstractAttribute::manifest(...). @@ -2748,7 +2756,8 @@ /// Return an assumed constant for the assocaited value a program point \p /// CtxI. Optional - 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( Index: llvm/test/Transforms/Attributor/alwaysinline.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/Attributor/alwaysinline.ll @@ -0,0 +1,78 @@ +; 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=ATTRIBUTOR +; +; When a function is marked `alwaysinline` and is able to be inlined, +; we can IPO its boundaries + +; The function can be removed safely because it has external linkage +; so this will be inlined, although the function is not exactly +; defined. +; ATTRIBUTOR: Function Attrs: alwaysinline nofree nosync nounwind readnone +define linkonce void @inner1() alwaysinline { +; ATTRIBUTOR-LABEL: @inner1( +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: ret void +; +entry: + ret void +} + +; ATTRIBUTOR: Function Attrs: nofree nosync nounwind +define void @outer1() { +; ATTRIBUTOR-LABEL: @outer1( +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: ret void +; +entry: + call void @inner1() + ret void +} + +; The function is not alwaysinline and is external +define i32 @inner2() { +; ATTRIBUTOR-LABEL: @inner2( +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: ret i32 1 +; +entry: + ret i32 1 +} + +define i32 @outer2() { +; ATTRIBUTOR-LABEL: @outer2( +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: ret i32 1 +; +entry: + %r = call i32 @inner2() alwaysinline + ret i32 %r +} + +; This function cannot be inlined although it is marked alwaysinline +declare i32 @a() returns_twice + +define internal i32 @inner3() alwaysinline { +; ATTRIBUTOR-LABEL: @inner3( +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[CALL:%.*]] = call i32 @a() #3 +; ATTRIBUTOR-NEXT: [[ADD:%.*]] = add nsw i32 1, [[CALL]] +; ATTRIBUTOR-NEXT: ret i32 [[ADD]] +; +entry: + %call = call i32 @a() returns_twice + %add = add nsw i32 1, %call + ret i32 %add +} + +define i32 @outer3() { +; ATTRIBUTOR-LABEL: @outer3( +; ATTRIBUTOR-NEXT: entry: +; ATTRIBUTOR-NEXT: [[CALL:%.*]] = call i32 @inner3() +; ATTRIBUTOR-NEXT: [[ADD:%.*]] = add nsw i32 1, [[CALL]] +; ATTRIBUTOR-NEXT: ret i32 [[ADD]] +; +entry: + %call = call i32 @inner3() + %add = add nsw i32 1, %call + ret i32 %add +}