Index: clang/include/clang/Basic/Attr.td =================================================================== --- clang/include/clang/Basic/Attr.td +++ clang/include/clang/Basic/Attr.td @@ -1275,6 +1275,11 @@ let Documentation = [FallthroughDocs]; } +def NoMerge : StmtAttr { + let Spellings = [Clang<"nomerge">]; + let Documentation = [NoMergeDocs]; +} + def FastCall : DeclOrTypeAttr { let Spellings = [GCC<"fastcall">, Keyword<"__fastcall">, Keyword<"_fastcall">]; Index: clang/include/clang/Basic/AttrDocs.td =================================================================== --- clang/include/clang/Basic/AttrDocs.td +++ clang/include/clang/Basic/AttrDocs.td @@ -350,6 +350,20 @@ }]; } +def NoMergeDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +If a statement is marked ``nomerge`` and contains call experessions, those call +expressions inside the statement will not be merged during optimization. This +attribute can be used to prevent the optimizer from obscuring the source +location of certain calls. For example, it will prevent tail merging otherwise +identical code sequences that raise an exception or terminate the program. Tail +merging normally reduces the precision of source location information, making +stack traces less useful for debugging. This attribute gives the user control +over the tradeoff between code size and debug information precision. + }]; +} + def AssertCapabilityDocs : Documentation { let Category = DocCatFunction; let Heading = "assert_capability, assert_shared_capability"; Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2751,6 +2751,9 @@ InGroup>; // Attributes +def warn_nomerge_attribute_ignored_in_stmt: Warning< + "%0 attribute is ignored because there exists no call expression inside the " + "statement">; def err_nsobject_attribute : Error< "'NSObject' attribute is for pointer types only">; def err_attributes_are_not_compatible : Error< Index: clang/lib/CodeGen/CGCall.cpp =================================================================== --- clang/lib/CodeGen/CGCall.cpp +++ clang/lib/CodeGen/CGCall.cpp @@ -4523,6 +4523,12 @@ Attrs.addAttribute(getLLVMContext(), llvm::AttributeList::FunctionIndex, llvm::Attribute::StrictFP); + // Add call-site nomerge attribute if exists. + if (NoMerge) + Attrs = + Attrs.addAttribute(getLLVMContext(), llvm::AttributeList::FunctionIndex, + llvm::Attribute::NoMerge); + // Apply some call-site-specific attributes. // TODO: work this into building the attribute set. Index: clang/lib/CodeGen/CGStmt.cpp =================================================================== --- clang/lib/CodeGen/CGStmt.cpp +++ clang/lib/CodeGen/CGStmt.cpp @@ -608,7 +608,13 @@ } void CodeGenFunction::EmitAttributedStmt(const AttributedStmt &S) { + for (const auto *A: S.getAttrs()) + if (A->getKind() == attr::NoMerge) { + NoMerge = true; + break; + } EmitStmt(S.getSubStmt(), S.getAttrs()); + NoMerge = false; } void CodeGenFunction::EmitGotoStmt(const GotoStmt &S) { Index: clang/lib/CodeGen/CodeGenFunction.h =================================================================== --- clang/lib/CodeGen/CodeGenFunction.h +++ clang/lib/CodeGen/CodeGenFunction.h @@ -574,6 +574,9 @@ ~SanitizerScope(); }; + /// True if the current statement has nomerge attribute. + bool NoMerge = false; + /// In C++, whether we are code generating a thunk. This controls whether we /// should emit cleanups. bool CurFuncIsThunk = false; Index: clang/lib/Sema/SemaStmtAttr.cpp =================================================================== --- clang/lib/Sema/SemaStmtAttr.cpp +++ clang/lib/Sema/SemaStmtAttr.cpp @@ -169,6 +169,28 @@ return LoopHintAttr::CreateImplicit(S.Context, Option, State, ValueExpr, A); } +static bool hasCallExpr(Stmt *S) { + if (S->getStmtClass() == Stmt::CallExprClass) + return true; + for (Stmt *SubStmt : S->children()) { + if (hasCallExpr(SubStmt)) + return true; + } + return false; +} + +static Attr *handleNoMergeAttr(Sema &S, Stmt *St, const ParsedAttr &A, + SourceRange Range) { + NoMergeAttr attribute(S.Context, A); + if (S.CheckAttrNoArgs(A)) + return nullptr; + if (!hasCallExpr(St)) { + S.Diag(St->getBeginLoc(), diag::warn_nomerge_attribute_ignored_in_stmt) + << attribute.getSpelling(); + return nullptr; + } + return ::new (S.Context) NoMergeAttr(S.Context, A); +} static void CheckForIncompatibleAttributes(Sema &S, @@ -335,6 +357,8 @@ return handleOpenCLUnrollHint(S, St, A, Range); case ParsedAttr::AT_Suppress: return handleSuppressAttr(S, St, A, Range); + case ParsedAttr::AT_NoMerge: + return handleNoMergeAttr(S, St, A, Range); default: // if we're here, then we parsed a known attribute, but didn't recognize // it as a statement attribute => it is declaration attribute Index: clang/test/CodeGen/attr-nomerge.cpp =================================================================== --- /dev/null +++ clang/test/CodeGen/attr-nomerge.cpp @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -S -emit-llvm %s -o - | FileCheck %s + +bool bar(); +void f(bool, bool); + +void foo(int i) { + [[clang::nomerge]] bar(); + [[clang::nomerge]] (i = 4, bar()); + [[clang::nomerge]] (void)(bar()); + [[clang::nomerge]] f(bar(), bar()); + bar(); +} +// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR:[0-9]+]] +// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR]] +// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR]] +// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR]] +// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR]] +// CHECK: call void @_Z1fbb({{.*}}) #[[NOMERGEATTR]] +// CHECK-NEXT: call zeroext i1 @_Z3barv() +// CHECK: attributes #[[NOMERGEATTR]] = { nomerge } Index: clang/test/Sema/attr-nomerge.cpp =================================================================== --- /dev/null +++ clang/test/Sema/attr-nomerge.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -verify -fsyntax-only %s + +void bar(); + +void foo() { + [[clang::nomerge]] bar(); + [[clang::nomerge(1, 2)]] bar(); // expected-error {{'nomerge' attribute takes no arguments}} + int x; + [[clang::nomerge]] x = 10; // expected-warning {{nomerge attribute is ignored because there exists no call expression inside the statement}} +}