diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1482,9 +1482,8 @@ def NoMerge : DeclOrStmtAttr { let Spellings = [Clang<"nomerge">]; let Documentation = [NoMergeDocs]; - let Subjects = SubjectList<[Function, Stmt], ErrorDiag, - "functions and statements">; - let SimpleHandler = 1; + let Subjects = SubjectList<[Function, Stmt, Var], ErrorDiag, + "functions, statements and variables">; } def MustTail : StmtAttr { diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -549,7 +549,31 @@ ``nomerge`` attribute can also be used as function attribute to prevent all calls to the specified function from merging. It has no effect on indirect -calls. +calls to such functions. For example: + +.. code-block:: c++ + + [[clang::nomerge]] void foo(int) {} + + void bar(int x) { + auto *ptr = foo; + if (x) foo(1); else foo(2); // will not be merged + if (x) ptr(1); else ptr(2); // indirect call, can be merged + } + +``nomerge`` attribute can also be used for pointers to functions to +prevent calls through such pointer from merging. In such case the +effect applies only to a specific function pointer. For example: + +.. code-block:: c++ + + [[clang::nomerge]] void (*foo)(int); + + void bar(int x) { + auto *ptr = foo; + if (x) foo(1); else foo(2); // will not be merged + if (x) ptr(1); else ptr(2); // 'ptr' has no 'nomerge' attribute, can be merged + } }]; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2981,6 +2981,10 @@ "statement">, InGroup; +def warn_attribute_ignored_non_function_pointer: Warning< + "%0 attribute is ignored because %1 is not a function pointer">, + InGroup; + def warn_function_attribute_ignored_in_stmt : Warning< "attribute is ignored on this statement as it only applies to functions; " "use '%0' on statements">, diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2341,6 +2341,9 @@ FuncAttrs.addAttribute(llvm::Attribute::NoReturn); NBA = Fn->getAttr(); } + } + + if (isa(TargetDecl) || isa(TargetDecl)) { // Only place nomerge attribute on call sites, never functions. This // allows it to work on indirect virtual function calls. if (AttrOnCallSite && TargetDecl->hasAttr()) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -8375,6 +8375,16 @@ handleSimpleAttribute(S, D, AL); } +static void handleNoMergeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + auto *VDecl = dyn_cast(D); + if (VDecl && !VDecl->isFunctionPointerType()) { + S.Diag(AL.getLoc(), diag::warn_attribute_ignored_non_function_pointer) + << AL << VDecl; + return; + } + D->addAttr(NoMergeAttr::Create(S.Context, AL)); +} + static void handleSYCLKernelAttr(Sema &S, Decl *D, const ParsedAttr &AL) { // The 'sycl_kernel' attribute applies only to function templates. const auto *FD = cast(D); @@ -9255,6 +9265,9 @@ case ParsedAttr::AT_FunctionReturnThunks: handleFunctionReturnThunksAttr(S, D, AL); break; + case ParsedAttr::AT_NoMerge: + handleNoMergeAttr(S, D, AL); + break; case ParsedAttr::AT_AvailableOnlyInDefaultEvalMethod: handleAvailableOnlyInDefaultEvalMethod(S, D, AL); diff --git a/clang/test/CodeGen/attr-nomerge.cpp b/clang/test/CodeGen/attr-nomerge.cpp --- a/clang/test/CodeGen/attr-nomerge.cpp +++ b/clang/test/CodeGen/attr-nomerge.cpp @@ -16,12 +16,14 @@ bool bar(); [[clang::nomerge]] void f(bool, bool); +[[clang::nomerge]] void (*fptr)(void); void foo(int i, A *ap, B *bp) { [[clang::nomerge]] bar(); [[clang::nomerge]] (i = 4, bar()); [[clang::nomerge]] (void)(bar()); f(bar(), bar()); + fptr(); [[clang::nomerge]] [] { bar(); bar(); }(); // nomerge only applies to the anonymous function call [[clang::nomerge]] for (bar(); bar(); bar()) {} [[clang::nomerge]] { asm("nop"); } @@ -66,6 +68,8 @@ // CHECK: call noundef zeroext i1 @_Z3barv(){{$}} // CHECK: call noundef zeroext i1 @_Z3barv(){{$}} // CHECK: call void @_Z1fbb({{.*}}) #[[ATTR0]] +// CHECK: %[[FPTR:.*]] = load ptr, ptr @fptr +// CHECK-NEXT: call void %[[FPTR]]() #[[ATTR0]] // CHECK: call void @"_ZZ3fooiP1AP1BENK3$_0clEv"{{.*}} #[[ATTR0]] // CHECK: call noundef zeroext i1 @_Z3barv() #[[ATTR0]] // CHECK-LABEL: for.cond: diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -105,7 +105,7 @@ // CHECK-NEXT: NoEscape (SubjectMatchRule_variable_is_parameter) // CHECK-NEXT: NoInline (SubjectMatchRule_function) // CHECK-NEXT: NoInstrumentFunction (SubjectMatchRule_function, SubjectMatchRule_objc_method) -// CHECK-NEXT: NoMerge (SubjectMatchRule_function) +// CHECK-NEXT: NoMerge (SubjectMatchRule_function, SubjectMatchRule_variable) // CHECK-NEXT: NoMicroMips (SubjectMatchRule_function) // CHECK-NEXT: NoMips16 (SubjectMatchRule_function) // CHECK-NEXT: NoProfileFunction (SubjectMatchRule_function) diff --git a/clang/test/Parser/stmt-attributes.c b/clang/test/Parser/stmt-attributes.c --- a/clang/test/Parser/stmt-attributes.c +++ b/clang/test/Parser/stmt-attributes.c @@ -82,9 +82,11 @@ int x; __attribute__((nomerge)) x = 10; // expected-warning {{'nomerge' attribute is ignored because there exists no call expression inside the statement}} - __attribute__((nomerge)) label : bar(); // expected-error {{'nomerge' attribute only applies to functions and statements}} + __attribute__((nomerge)) label : bar(); // expected-error {{'nomerge' attribute only applies to functions, statements and variables}} } int f(void); -__attribute__((nomerge)) static int i; // expected-error {{'nomerge' attribute only applies to functions and statements}} +__attribute__((nomerge)) static int (*fptr)(void); +__attribute__((nomerge)) static int i; // expected-warning {{'nomerge' attribute is ignored because 'i' is not a function pointer}} +struct buz {} __attribute__((nomerge)); // expected-error {{'nomerge' attribute only applies to functions, statements and variables}} diff --git a/clang/test/Parser/stmt-attributes.cpp b/clang/test/Parser/stmt-attributes.cpp --- a/clang/test/Parser/stmt-attributes.cpp +++ b/clang/test/Parser/stmt-attributes.cpp @@ -6,7 +6,7 @@ template class __attribute__((nomerge)) A { - // expected-error@-1 {{'nomerge' attribute only applies to functions and statements}} + // expected-error@-1 {{'nomerge' attribute only applies to functions, statements and variables}} }; class B : public A<> { diff --git a/clang/test/Parser/stmt-attributes.m b/clang/test/Parser/stmt-attributes.m --- a/clang/test/Parser/stmt-attributes.m +++ b/clang/test/Parser/stmt-attributes.m @@ -19,7 +19,7 @@ @implementation Test - (void)foo __attribute__((nomerge)) { - // expected-error@-1 {{'nomerge' attribute only applies to functions and statements}} + // expected-error@-1 {{'nomerge' attribute only applies to functions, statements and variables}} } - (void)bar { diff --git a/clang/test/Sema/attr-nomerge-ast.cpp b/clang/test/Sema/attr-nomerge-ast.cpp --- a/clang/test/Sema/attr-nomerge-ast.cpp +++ b/clang/test/Sema/attr-nomerge-ast.cpp @@ -4,6 +4,7 @@ [[clang::nomerge]] void func(); void func(); [[clang::nomerge]] void func() {} +[[clang::nomerge]] void (*var)(void); // CHECK: FunctionDecl {{.*}} func 'void ()' // CHECK-NEXT: NoMergeAttr @@ -14,3 +15,6 @@ // CHECK-NEXT: FunctionDecl {{.*}} func 'void ()' // CHECK-NEXT: CompoundStmt // CHECK-NEXT: NoMergeAttr + +// CHECK-NEXT: VarDecl {{.*}} var 'void (*)()' +// CHECK-NEXT: NoMergeAttr diff --git a/clang/test/Sema/attr-nomerge.cpp b/clang/test/Sema/attr-nomerge.cpp --- a/clang/test/Sema/attr-nomerge.cpp +++ b/clang/test/Sema/attr-nomerge.cpp @@ -8,10 +8,14 @@ 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 only applies to functions and statements}} + [[clang::nomerge]] label: bar(); // expected-error {{'nomerge' attribute only applies to functions, statements and variables}} } [[clang::nomerge]] int f(); -[[clang::nomerge]] static int i = f(); // expected-error {{'nomerge' attribute only applies to functions and statements}} +[[clang::nomerge]] static int i = f(); // expected-warning {{'nomerge' attribute is ignored because 'i' is not a function pointer}} + +[[clang::nomerge]] void (*j)(void); + +struct [[clang::nomerge]] buz {}; // expected-error {{'nomerge' attribute only applies to functions, statements and variables}}