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,19 @@ }]; } +def NoMergeDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +Calls to functions marked `nomerge` 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/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 *Attr: S.getAttrs()) + if (Attr->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 @@ -170,6 +170,11 @@ return LoopHintAttr::CreateImplicit(S.Context, Option, State, ValueExpr, A); } +static Attr *handleNoMergeAttr(Sema &S, Stmt *St, const ParsedAttr &A, + SourceRange Range) { + return ::new (S.Context) NoMergeAttr(S.Context, A); +} + static void CheckForIncompatibleAttributes(Sema &S, const SmallVectorImpl &Attrs) { @@ -335,6 +340,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,15 @@ +// RUN: %clang_cc1 -S -emit-llvm %s -o - | FileCheck %s + +void bar(); + +void foo(int i) { + [[clang::nomerge]] bar(); + [[clang::nomerge]] (i = 4, bar()); + [[clang::nomerge]] (void)(bar()); + bar(); +} +// CHECK: call void @_Z3barv() #[[NOMERGEATTR:[0-9]+]] +// CHECK: call void @_Z3barv() #[[NOMERGEATTR]] +// CHECK: call void @_Z3barv() #[[NOMERGEATTR]] +// CHECK-NEXT: call void @_Z3barv() +// CHECK: attributes #[[NOMERGEATTR]] = { nomerge }