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 @@ -2762,6 +2762,10 @@ InGroup>; // Attributes +def warn_nomerge_attribute_ignored_in_stmt: Warning< + "%0 attribute is ignored because there exists no call expression inside the " + "statement">, + InGroup; 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 @@ -4822,6 +4822,12 @@ Attrs.addAttribute(getLLVMContext(), llvm::AttributeList::FunctionIndex, llvm::Attribute::StrictFP); + // Add call-site nomerge attribute if exists. + if (InNoMergeAttributedStmt) + 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 @@ -25,6 +25,7 @@ #include "llvm/IR/InlineAsm.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/MDBuilder.h" +#include "llvm/Support/SaveAndRestore.h" using namespace clang; using namespace CodeGen; @@ -608,6 +609,13 @@ } void CodeGenFunction::EmitAttributedStmt(const AttributedStmt &S) { + bool nomerge = false; + for (const auto *A : S.getAttrs()) + if (A->getKind() == attr::NoMerge) { + nomerge = true; + break; + } + SaveAndRestore save_nomerge(InNoMergeAttributedStmt, nomerge); EmitStmt(S.getSubStmt(), S.getAttrs()); } Index: clang/lib/CodeGen/CodeGenFunction.h =================================================================== --- clang/lib/CodeGen/CodeGenFunction.h +++ clang/lib/CodeGen/CodeGenFunction.h @@ -595,6 +595,9 @@ /// region. bool IsInPreservedAIRegion = false; + /// True if the current statement has nomerge attribute. + bool InNoMergeAttributedStmt = false; + const CodeGen::CGBlockInfo *BlockInfo = nullptr; llvm::Value *BlockPointer = nullptr; Index: clang/lib/Sema/SemaStmtAttr.cpp =================================================================== --- clang/lib/Sema/SemaStmtAttr.cpp +++ clang/lib/Sema/SemaStmtAttr.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/EvaluatedExprVisitor.h" #include "clang/Sema/SemaInternal.h" #include "clang/AST/ASTContext.h" #include "clang/Basic/SourceManager.h" @@ -170,6 +171,44 @@ return LoopHintAttr::CreateImplicit(S.Context, Option, State, ValueExpr, A); } +namespace { +class CallExprFinder : public ConstEvaluatedExprVisitor { + bool FoundCallExpr = false; + +public: + typedef ConstEvaluatedExprVisitor Inherited; + + CallExprFinder(Sema &S, const Stmt *St) : Inherited(S.Context) { Visit(St); } + + bool foundCallExpr() { return FoundCallExpr; } + + void VisitCallExpr(const CallExpr *E) { FoundCallExpr = true; } + + void Visit(const Stmt *St) { + if (!St) + return; + ConstEvaluatedExprVisitor::Visit(St); + } +}; +} // namespace + +static Attr *handleNoMergeAttr(Sema &S, Stmt *St, const ParsedAttr &A, + SourceRange Range) { + NoMergeAttr NMA(S.Context, A); + if (S.CheckAttrNoArgs(A)) + return nullptr; + + CallExprFinder CEF(S, St); + + if (!CEF.foundCallExpr()) { + S.Diag(St->getBeginLoc(), diag::warn_nomerge_attribute_ignored_in_stmt) + << NMA.getSpelling(); + return nullptr; + } + + return ::new (S.Context) NoMergeAttr(S.Context, A); +} + static void CheckForIncompatibleAttributes(Sema &S, const SmallVectorImpl &Attrs) { @@ -335,6 +374,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,26 @@ +// 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::nomerge]] bar(); + [[clang::nomerge]] (i = 4, bar()); + [[clang::nomerge]] (void)(bar()); + [[clang::nomerge]] f(bar(), bar()); + [[clang::nomerge]] [] { bar(); bar(); }(); // nomerge only applies to the anonymous function call + [[clang::nomerge]] for (bar(); 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: call void @"_ZZ3fooiENK3$_0clEv"(%class.anon* %ref.tmp) #[[NOMERGEATTR]] +// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR]] +// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR]] +// CHECK: call zeroext i1 @_Z3barv() #[[NOMERGEATTR]] +// CHECK: 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,17 @@ +// 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}} + + [[clang::nomerge]] label: bar(); // expected-error {{'nomerge' attribute cannot be applied to a declaration}} + +} + +int f(); + +[[clang::nomerge]] static int i = f(); // expected-error {{'nomerge' attribute cannot be applied to a declaration}}