[clang] 06eee73 - [clang] Allow 'nomerge' attribute for function pointers
Eduard Zingerman via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 26 15:23:05 PDT 2023
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-76dd9c8318a4@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 at -1 {{'nomerge' attribute only applies to functions and statements}}
+ // expected-error at -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 at -1 {{'nomerge' attribute only applies to functions and statements}}
+ // expected-error at -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}}
More information about the cfe-commits
mailing list