diff --git a/llvm/lib/Analysis/InlineCost.cpp b/llvm/lib/Analysis/InlineCost.cpp --- a/llvm/lib/Analysis/InlineCost.cpp +++ b/llvm/lib/Analysis/InlineCost.cpp @@ -2864,6 +2864,9 @@ // Calls to functions with always-inline attributes should be inlined // whenever possible. if (Call.hasFnAttr(Attribute::AlwaysInline)) { + if (Call.getAttributes().hasFnAttr(Attribute::NoInline)) + return InlineResult::failure("noinline call site attribute"); + auto IsViable = isInlineViable(*Callee); if (IsViable.isSuccess()) return InlineResult::success(); diff --git a/llvm/test/Transforms/Inline/call-site-attrs.ll b/llvm/test/Transforms/Inline/call-site-attrs.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/Inline/call-site-attrs.ll @@ -0,0 +1,59 @@ +; RUN: opt < %s -inline -S | FileCheck %s +; RUN: opt < %s -passes='cgscc(inline)' -S | FileCheck %s + +; Always prefer call site attribute over function attribute + +define internal i32 @inner1() { +; CHECK: @inner1( + ret i32 1 +} + +define i32 @outer1() { +; CHECK-LABEL: @outer1( +; CHECK: call + + %r = call i32 @inner1() noinline + ret i32 %r +} + +define internal i32 @inner2() alwaysinline { +; CHECK: @inner2( + ret i32 1 +} + +define i32 @outer2() { +; CHECK-LABEL: @outer2( +; CHECK: call + + %r = call i32 @inner2() noinline + ret i32 %r +} + +define i32 @inner3() alwaysinline { +; CHECK: @inner3( + ret i32 1 +} + +define i32 @outer3() { +; CHECK-LABEL: @outer3( +; CHECK: call + + %r = call i32 @inner3() noinline + ret i32 %r +} + +define i32 @inner4() noinline { +; CHECK: @inner4( + ret i32 1 +} + +define i32 @outer4() { +; CHECK-LABEL: @outer4( +; CHECK-NOT: call +; CHECK: ret + + %r = call i32 @inner4() alwaysinline + + ret i32 %r +} +