Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -1758,10 +1758,12 @@ let SimpleHandler = 1; } -def NoInline : InheritableAttr { - let Spellings = [GCC<"noinline">, Declspec<"noinline">]; - let Subjects = SubjectList<[Function]>; +def NoInline : DeclOrStmtAttr { + let Spellings = [Clang<"noinline">, Declspec<"noinline">]; let Documentation = [Undocumented]; + let InheritEvenIfAlreadyPresent = 1; + let Subjects = SubjectList<[Function, Stmt], ErrorDiag, + "functions and statements">; let SimpleHandler = 1; } Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2893,7 +2893,7 @@ InGroup>; // Attributes -def warn_nomerge_attribute_ignored_in_stmt: Warning< +def warn_attribute_ignored_in_stmt: Warning< "%0 attribute is ignored because there exists no call expression inside the " "statement">, InGroup; Index: clang/lib/CodeGen/CGCall.cpp =================================================================== --- clang/lib/CodeGen/CGCall.cpp +++ clang/lib/CodeGen/CGCall.cpp @@ -5181,12 +5181,17 @@ 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() && + !InNoInlineAttributedStmt && !(TargetDecl && TargetDecl->hasAttr())) { Attrs = Attrs.addFnAttribute(getLLVMContext(), llvm::Attribute::AlwaysInline); Index: clang/lib/CodeGen/CGStmt.cpp =================================================================== --- clang/lib/CodeGen/CGStmt.cpp +++ clang/lib/CodeGen/CGStmt.cpp @@ -666,12 +666,16 @@ 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(Sub); @@ -679,6 +683,7 @@ } } SaveAndRestore save_nomerge(InNoMergeAttributedStmt, nomerge); + SaveAndRestore save_noinline(InNoInlineAttributedStmt, noinline); SaveAndRestore save_musttail(MustTailCall, musttail); EmitStmt(S.getSubStmt(), S.getAttrs()); } Index: clang/lib/CodeGen/CodeGenFunction.h =================================================================== --- clang/lib/CodeGen/CodeGenFunction.h +++ clang/lib/CodeGen/CodeGenFunction.h @@ -551,6 +551,9 @@ /// 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; Index: clang/lib/Sema/SemaStmtAttr.cpp =================================================================== --- clang/lib/Sema/SemaStmtAttr.cpp +++ clang/lib/Sema/SemaStmtAttr.cpp @@ -201,7 +201,7 @@ CallExprFinder CEF(S, St); if (!CEF.foundCallExpr()) { - S.Diag(St->getBeginLoc(), diag::warn_nomerge_attribute_ignored_in_stmt) + S.Diag(St->getBeginLoc(), diag::warn_attribute_ignored_in_stmt) << NMA.getSpelling(); return nullptr; } @@ -209,6 +209,20 @@ 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 (!CEF.foundCallExpr()) { + S.Diag(St->getBeginLoc(), diag::warn_attribute_ignored_in_stmt) + << NIA.getSpelling(); + return nullptr; + } + + 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(). @@ -418,6 +432,8 @@ 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: Index: clang/test/CodeGen/attr-noinline.cpp =================================================================== --- /dev/null +++ clang/test/CodeGen/attr-noinline.cpp @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -S -emit-llvm %s -triple x86_64-unknown-linux-gnu -o - | FileCheck %s + +bool bar(); +void f(bool, bool); + +void foo(int i) { + [[clang::noinline]] bar(); +// CHECK: call noundef zeroext i1 @_Z3barv() #[[NOINLINEATTR:[0-9]+]] + [[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 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() +} + +// CHECK: attributes #[[NOINLINEATTR]] = { noinline } Index: clang/test/Sema/attr-noinline.cpp =================================================================== --- /dev/null +++ clang/test/Sema/attr-noinline.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -verify -fsyntax-only %s + +int bar(); + +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]] label: x = 1; // expected-error {{'noinline' attribute only applies to functions and statements}} + +} + +[[clang::noinline]] static int i = bar(); // expected-error {{'noinline' attribute only applies to functions and statements}}