Author: Eduard Zingerman Date: 2023-06-27T01:15:45+03:00 New Revision: 06eee734c1ea9de754c1e7e8c79c0897b9e01350
URL: https://github.com/llvm/llvm-project/commit/06eee734c1ea9de754c1e7e8c79c0897b9e01350 DIFF: https://github.com/llvm/llvm-project/commit/06eee734c1ea9de754c1e7e8c79c0897b9e01350.diff LOG: [clang] Allow 'nomerge' attribute for function pointers Allow specifying 'nomerge' attribute for function pointers, e.g. like in the following C code: extern void (*foo)(void) __attribute__((nomerge)); void bar(long i) { if (i) foo(); else foo(); } With the goal to attach 'nomerge' to both calls done through 'foo': @foo = external local_unnamed_addr global ptr, align 8 define dso_local void @bar(i64 noundef %i) local_unnamed_addr #0 { ; ... %0 = load ptr, ptr @foo, align 8, !tbaa !5 ; ... if.then: tail call void %0() #1 br label %if.end if.else: tail call void %0() #1 br label %if.end if.end: ret void } ; ... attributes #1 = { nomerge ... } Report a warning in case if 'nomerge' is specified for a variable that is not a function pointer, e.g.: t.c:2:22: warning: 'nomerge' attribute is ignored because 'j' is not a function pointer [-Wignored-attributes] 2 | int j __attribute__((nomerge)); | ^ The intended use-case is for BPF backend. BPF provides a sort of "standard library" functions that are called helpers. BPF also verifies usage of these helpers before program execution. Because of limitations of verification / runtime model it is important to keep calls to some of such helpers from merging. An example could be found by the link [1], there input C code: if (data_end - data > 1024) { bpf_for_each_map_elem(&map1, cb, &cb_data, 0); } else { bpf_for_each_map_elem(&map2, cb, &cb_data, 0); } Is converted to bytecode equivalent to: if (data_end - data > 1024) tmp = &map1; else tmp = &map2; bpf_for_each_map_elem(tmp, cb, &cb_data, 0); However, BPF verification/runtime requires to use the same map address for each particular `bpf_for_each_map_elem()` call. The 'nomerge' attribute is a perfect match for this situation, but unfortunately BPF helpers are declared as pointers to functions: static long (*bpf_for_each_map_elem)(void *map, ...) = (void *) 164; Hence, this commit, allowing to use 'nomerge' for function pointers. [1] https://lore.kernel.org/bpf/03bdf90f-f374-1e67-69d6-76dd9c831...@meta.com/ Differential Revision: https://reviews.llvm.org/D152986 Added: Modified: clang/include/clang/Basic/Attr.td clang/include/clang/Basic/AttrDocs.td clang/include/clang/Basic/DiagnosticSemaKinds.td clang/lib/CodeGen/CGCall.cpp clang/lib/Sema/SemaDeclAttr.cpp clang/test/CodeGen/attr-nomerge.cpp clang/test/Misc/pragma-attribute-supported-attributes-list.test clang/test/Parser/stmt-attributes.c clang/test/Parser/stmt-attributes.cpp clang/test/Parser/stmt-attributes.m clang/test/Sema/attr-nomerge-ast.cpp clang/test/Sema/attr-nomerge.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index b9e701641fdf1..d5204b2869667 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1482,9 +1482,8 @@ def : MutualExclusions<[Likely, Unlikely]>; 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 index 5178ca43b9710..2c950231255d7 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -549,7 +549,31 @@ over the tradeoff between code size and debug information precision. ``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 index 3f0b7a54d8890..507e9f11f4664 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2981,6 +2981,10 @@ def warn_attribute_ignored_no_calls_in_stmt: Warning< "statement">, InGroup<IgnoredAttributes>; +def warn_attribute_ignored_non_function_pointer: Warning< + "%0 attribute is ignored because %1 is not a function pointer">, + InGroup<IgnoredAttributes>; + 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 index 61029c9226d7b..6a9be661998fc 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2341,6 +2341,9 @@ void CodeGenModule::ConstructAttributeList(StringRef Name, FuncAttrs.addAttribute(llvm::Attribute::NoReturn); NBA = Fn->getAttr<NoBuiltinAttr>(); } + } + + if (isa<FunctionDecl>(TargetDecl) || isa<VarDecl>(TargetDecl)) { // Only place nomerge attribute on call sites, never functions. This // allows it to work on indirect virtual function calls. if (AttrOnCallSite && TargetDecl->hasAttr<NoMergeAttr>()) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index f4fb096bb2ffc..ed69e802c95dd 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -8375,6 +8375,16 @@ static void handleAvailableOnlyInDefaultEvalMethod(Sema &S, Decl *D, handleSimpleAttribute<AvailableOnlyInDefaultEvalMethodAttr>(S, D, AL); } +static void handleNoMergeAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + auto *VDecl = dyn_cast<VarDecl>(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<FunctionDecl>(D); @@ -9255,6 +9265,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, 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 index 1be84d76aa45a..2d015f6164550 100644 --- a/clang/test/CodeGen/attr-nomerge.cpp +++ b/clang/test/CodeGen/attr-nomerge.cpp @@ -16,12 +16,14 @@ class B : public A { 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 @@ void something_else_again() { // 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 index 3691e827dfb1d..eaf6d34421bbe 100644 --- 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 index ccd206e70a225..782e055f6263e 100644 --- a/clang/test/Parser/stmt-attributes.c +++ b/clang/test/Parser/stmt-attributes.c @@ -82,9 +82,11 @@ void foobar(void) { 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 index 74192ea68141d..839c44e908af0 100644 --- a/clang/test/Parser/stmt-attributes.cpp +++ b/clang/test/Parser/stmt-attributes.cpp @@ -6,7 +6,7 @@ template <typename T = void> 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 index 227ea9dc2facc..7b15bc8e0b815 100644 --- a/clang/test/Parser/stmt-attributes.m +++ b/clang/test/Parser/stmt-attributes.m @@ -19,7 +19,7 @@ - (void)bar; @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 index c440cf760b16b..410fda5a4e096 100644 --- 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 @@ void func(); // 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 index 294abfafcd2aa..69a535bde76e2 100644 --- a/clang/test/Sema/attr-nomerge.cpp +++ b/clang/test/Sema/attr-nomerge.cpp @@ -8,10 +8,14 @@ void foo() { 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}} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits