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/
This statement of the attribute having "no effect on indirect calls" is slightly confusing now that we talk about function pointers immediately afterward. Can you please rework this a bit, and clarify that when applied to function pointers, the attribute only takes effect when the call target is directly the variable which carries the attribute? For example, this has no effect: