let SimpleHandler = 1;
}
-def NoInline : InheritableAttr {
- let Spellings = [GCC<"noinline">, Declspec<"noinline">];
- let Subjects = SubjectList<[Function]>;
- let Documentation = [Undocumented];
+def NoInline : DeclOrStmtAttr {
+ let Spellings = [GCC<"noinline">, CXX11<"clang", "noinline">,
+ C2x<"clang", "noinline">, Declspec<"noinline">];
+ let Accessors = [Accessor<"isClangNoInline", [CXX11<"clang", "noinline">,
+ C2x<"clang", "noinline">]>];
+ let Documentation = [NoInlineDocs];
+ let Subjects = SubjectList<[Function, Stmt], ErrorDiag,
+ "functions and statements">;
let SimpleHandler = 1;
}
}];
}
+def NoInlineDocs : Documentation {
+ let Category = DocCatFunction;
+ let Content = [{
+This function attribute suppresses the inlining of a function at the call sites
+of the function.
+
+``[[clang::noinline]]`` spelling can be used as a statement attribute; other
+spellings of the attribute are not supported on statements. If a statement is
+marked ``[[clang::noinline]]`` and contains calls, those calls inside the
+statement will not be inlined by the compiler.
+
+.. code-block:: c
+
+ int example(void) {
+ int r;
+ [[clang::noinline]] foo();
+ [[clang::noinline]] r = bar();
+ return r;
+ }
+
+ }];
+}
+
def MustTailDocs : Documentation {
let Category = DocCatStmt;
let Content = [{
InGroup<DiagGroup<"auto-var-id">>;
// Attributes
-def warn_nomerge_attribute_ignored_in_stmt: Warning<
+def warn_attribute_ignored_no_calls_in_stmt: Warning<
"%0 attribute is ignored because there exists no call expression inside the "
"statement">,
InGroup<IgnoredAttributes>;
+def warn_function_attribute_ignored_in_stmt : Warning<
+ "attribute is ignored on this statement as it only applies to functions; "
+ "use '%0' on statements">,
+ InGroup<IgnoredAttributes>;
+
def err_musttail_needs_trivial_args : Error<
"tail call requires that the return value, all parameters, and any "
"temporaries created by the expression are trivially destructible">;
def warn_attribute_nonnull_parm_no_args : Warning<
"'nonnull' attribute when used on parameters takes no arguments">,
InGroup<IgnoredAttributes>;
+def warn_function_stmt_attribute_precedence : Warning<
+ "statement attribute %0 has higher precedence than function attribute "
+ "'%select{always_inline|flatten}1'">,
+ InGroup<IgnoredAttributes>;
def note_declared_nonnull : Note<
"declared %select{'returns_nonnull'|'nonnull'}0 here">;
def warn_attribute_sentinel_named_arguments : Warning<
if (InNoMergeAttributedStmt)
Attrs = Attrs.addFnAttribute(getLLVMContext(), llvm::Attribute::NoMerge);
+ // Add call-site noinline attribute if exists.
+ if (InNoInlineAttributedStmt)
+ Attrs = Attrs.addFnAttribute(getLLVMContext(), llvm::Attribute::NoInline);
+
// Apply some call-site-specific attributes.
// TODO: work this into building the attribute set.
// Apply always_inline to all calls within flatten functions.
// FIXME: should this really take priority over __try, below?
if (CurCodeDecl && CurCodeDecl->hasAttr<FlattenAttr>() &&
+ !InNoInlineAttributedStmt &&
!(TargetDecl && TargetDecl->hasAttr<NoInlineAttr>())) {
Attrs =
Attrs.addFnAttribute(getLLVMContext(), llvm::Attribute::AlwaysInline);
void CodeGenFunction::EmitAttributedStmt(const AttributedStmt &S) {
bool nomerge = false;
+ bool noinline = false;
const CallExpr *musttail = nullptr;
for (const auto *A : S.getAttrs()) {
if (A->getKind() == attr::NoMerge) {
nomerge = true;
}
+ if (A->getKind() == attr::NoInline) {
+ noinline = true;
+ }
if (A->getKind() == attr::MustTail) {
const Stmt *Sub = S.getSubStmt();
const ReturnStmt *R = cast<ReturnStmt>(Sub);
}
}
SaveAndRestore<bool> save_nomerge(InNoMergeAttributedStmt, nomerge);
+ SaveAndRestore<bool> save_noinline(InNoInlineAttributedStmt, noinline);
SaveAndRestore<const CallExpr *> save_musttail(MustTailCall, musttail);
EmitStmt(S.getSubStmt(), S.getAttrs());
}
/// True if the current statement has nomerge attribute.
bool InNoMergeAttributedStmt = false;
+ /// True if the current statement has noinline attribute.
+ bool InNoInlineAttributedStmt = false;
+
// The CallExpr within the current statement that the musttail attribute
// applies to. nullptr if there is no 'musttail' on the current statement.
const CallExpr *MustTailCall = nullptr;
namespace {
class CallExprFinder : public ConstEvaluatedExprVisitor<CallExprFinder> {
- bool FoundCallExpr = false;
+ bool FoundAsmStmt = false;
+ std::vector<const CallExpr *> CallExprs;
public:
typedef ConstEvaluatedExprVisitor<CallExprFinder> Inherited;
CallExprFinder(Sema &S, const Stmt *St) : Inherited(S.Context) { Visit(St); }
- bool foundCallExpr() { return FoundCallExpr; }
+ bool foundCallExpr() { return !CallExprs.empty(); }
+ const std::vector<const CallExpr *> &getCallExprs() { return CallExprs; }
- void VisitCallExpr(const CallExpr *E) { FoundCallExpr = true; }
- void VisitAsmStmt(const AsmStmt *S) { FoundCallExpr = true; }
+ bool foundAsmStmt() { return FoundAsmStmt; }
+
+ void VisitCallExpr(const CallExpr *E) { CallExprs.push_back(E); }
+
+ void VisitAsmStmt(const AsmStmt *S) { FoundAsmStmt = true; }
void Visit(const Stmt *St) {
if (!St)
NoMergeAttr NMA(S.Context, A);
CallExprFinder CEF(S, St);
- if (!CEF.foundCallExpr()) {
- S.Diag(St->getBeginLoc(), diag::warn_nomerge_attribute_ignored_in_stmt)
+ if (!CEF.foundCallExpr() && !CEF.foundAsmStmt()) {
+ S.Diag(St->getBeginLoc(), diag::warn_attribute_ignored_no_calls_in_stmt)
<< A;
return nullptr;
}
return ::new (S.Context) NoMergeAttr(S.Context, A);
}
+static Attr *handleNoInlineAttr(Sema &S, Stmt *St, const ParsedAttr &A,
+ SourceRange Range) {
+ NoInlineAttr NIA(S.Context, A);
+ CallExprFinder CEF(S, St);
+
+ if (!NIA.isClangNoInline()) {
+ S.Diag(St->getBeginLoc(), diag::warn_function_attribute_ignored_in_stmt)
+ << "[[clang::noinline]]";
+ return nullptr;
+ }
+
+ if (!CEF.foundCallExpr()) {
+ S.Diag(St->getBeginLoc(), diag::warn_attribute_ignored_no_calls_in_stmt)
+ << A;
+ return nullptr;
+ }
+
+ for (const auto *CallExpr : CEF.getCallExprs()) {
+ const Decl *Decl = CallExpr->getCalleeDecl();
+ if (Decl->hasAttr<AlwaysInlineAttr>() || Decl->hasAttr<FlattenAttr>())
+ S.Diag(St->getBeginLoc(), diag::warn_function_stmt_attribute_precedence)
+ << A << (Decl->hasAttr<AlwaysInlineAttr>() ? 0 : 1);
+ }
+
+ return ::new (S.Context) NoInlineAttr(S.Context, A);
+}
+
static Attr *handleMustTailAttr(Sema &S, Stmt *St, const ParsedAttr &A,
SourceRange Range) {
// Validation is in Sema::ActOnAttributedStmt().
return handleSuppressAttr(S, St, A, Range);
case ParsedAttr::AT_NoMerge:
return handleNoMergeAttr(S, St, A, Range);
+ case ParsedAttr::AT_NoInline:
+ return handleNoInlineAttr(S, St, A, Range);
case ParsedAttr::AT_MustTail:
return handleMustTailAttr(S, St, A, Range);
case ParsedAttr::AT_Likely:
--- /dev/null
+// RUN: %clang_cc1 -S -emit-llvm %s -triple x86_64-unknown-linux-gnu -o - | FileCheck %s
+
+bool bar();
+void f(bool, bool);
+void g(bool);
+
+static int baz(int x) {
+ return x * 10;
+}
+
+[[clang::noinline]] bool noi() { }
+
+void foo(int i) {
+ [[clang::noinline]] bar();
+// CHECK: call noundef zeroext i1 @_Z3barv() #[[NOINLINEATTR:[0-9]+]]
+ [[clang::noinline]] i = baz(i);
+// CHECK: call noundef i32 @_ZL3bazi({{.*}}) #[[NOINLINEATTR]]
+ [[clang::noinline]] (i = 4, bar());
+// CHECK: call noundef zeroext i1 @_Z3barv() #[[NOINLINEATTR]]
+ [[clang::noinline]] (void)(bar());
+// CHECK: call noundef zeroext i1 @_Z3barv() #[[NOINLINEATTR]]
+ [[clang::noinline]] f(bar(), bar());
+// CHECK: call noundef zeroext i1 @_Z3barv() #[[NOINLINEATTR]]
+// CHECK: call noundef zeroext i1 @_Z3barv() #[[NOINLINEATTR]]
+// CHECK: call void @_Z1fbb({{.*}}) #[[NOINLINEATTR]]
+ [[clang::noinline]] [] { bar(); bar(); }(); // noinline only applies to the anonymous function call
+// CHECK: call void @"_ZZ3fooiENK3$_0clEv"(%class.anon* {{[^,]*}} %ref.tmp) #[[NOINLINEATTR]]
+ [[clang::noinline]] for (bar(); bar(); bar()) {}
+// CHECK: call noundef zeroext i1 @_Z3barv() #[[NOINLINEATTR]]
+// CHECK: call noundef zeroext i1 @_Z3barv() #[[NOINLINEATTR]]
+// CHECK: call noundef zeroext i1 @_Z3barv() #[[NOINLINEATTR]]
+ bar();
+// CHECK: call noundef zeroext i1 @_Z3barv()
+ [[clang::noinline]] noi();
+// CHECK: call noundef zeroext i1 @_Z3noiv()
+ noi();
+// CHECK: call noundef zeroext i1 @_Z3noiv()
+ [[gnu::noinline]] bar();
+// CHECK: call noundef zeroext i1 @_Z3barv()
+}
+
+struct S {
+ friend bool operator==(const S &LHS, const S &RHS);
+};
+
+void func(const S &s1, const S &s2) {
+ [[clang::noinline]]g(s1 == s2);
+// CHECK: call noundef zeroext i1 @_ZeqRK1SS1_({{.*}}) #[[NOINLINEATTR]]
+// CHECK: call void @_Z1gb({{.*}}) #[[NOINLINEATTR]]
+ bool b;
+ [[clang::noinline]] b = s1 == s2;
+// CHECK: call noundef zeroext i1 @_ZeqRK1SS1_({{.*}}) #[[NOINLINEATTR]]
+}
+
+// CHECK: attributes #[[NOINLINEATTR]] = { noinline }
}
__attribute__((fastcall)) goto there; // expected-error {{'fastcall' attribute cannot be applied to a statement}}
- __attribute__((noinline)) there : // expected-warning {{'noinline' attribute only applies to functions}}
+ __attribute__((noinline)) there : // expected-error {{'noinline' attribute only applies to functions and statements}}
__attribute__((weakref)) return; // expected-error {{'weakref' attribute only applies to variables and functions}}
// RUN: %clang_cc1 %s -verify -fsyntax-only
-int a __attribute__((noinline)); // expected-warning {{'noinline' attribute only applies to functions}}
+int a __attribute__((noinline)); // expected-error {{'noinline' attribute only applies to functions and statements}}
void t1(void) __attribute__((noinline));
--- /dev/null
+// RUN: %clang_cc1 -verify -fsyntax-only %s
+
+int bar();
+
+[[gnu::always_inline]] void always_inline_fn(void) { }
+[[gnu::flatten]] void flatten_fn(void) { }
+
+[[gnu::noinline]] void noinline_fn(void) { }
+
+void foo() {
+ [[clang::noinline]] bar();
+ [[clang::noinline(0)]] bar(); // expected-error {{'noinline' attribute takes no arguments}}
+ int x;
+ [[clang::noinline]] x = 0; // expected-warning {{'noinline' attribute is ignored because there exists no call expression inside the statement}}
+ [[clang::noinline]] { asm("nop"); } // expected-warning {{'noinline' attribute is ignored because there exists no call expression inside the statement}}
+ [[clang::noinline]] label: x = 1; // expected-error {{'noinline' attribute only applies to functions and statements}}
+
+
+ [[clang::noinline]] always_inline_fn(); // expected-warning {{statement attribute 'noinline' has higher precedence than function attribute 'always_inline'}}
+ [[clang::noinline]] flatten_fn(); // expected-warning {{statement attribute 'noinline' has higher precedence than function attribute 'flatten'}}
+ [[clang::noinline]] noinline_fn();
+
+ [[gnu::noinline]] bar(); // expected-warning {{attribute is ignored on this statement as it only applies to functions; use '[[clang::noinline]]' on statements}}
+ __attribute__((noinline)) bar(); // expected-warning {{attribute is ignored on this statement as it only applies to functions; use '[[clang::noinline]]' on statements}}
+}
+
+[[clang::noinline]] static int i = bar(); // expected-error {{'noinline' attribute only applies to functions and statements}}