Using GCC's Nested Functions with Wide Pointers and No Trampolines II
Ranked #3 on Hacker News with 12 points and 0 comments.
Using GCC's Nested Functions with Wide Pointers and no Trampolines II
When the address of a nested function is taken, GCC creates at trampoline at run-time. This trampoline is placed on the stack, which means that the stack has to be executable. This is problematic because a non-executable stack is an important security feature. For some time now, GCC has the option to place the trampoline on the heap, which is better but still not ideal as the allocation has a higher cost and the trampoline could leak when longjmp is used. Previously , I pointed out how this could be avoided with a new wide pointer type, and also talked about a preliminary patch to GCC that implements some core functionality that would be necessary for this.
First, GCC 16 was released. In GCC 16 it is guaranteed that a nested function that does not access variables of a parent function will not require a trampoline. In practice, this was already ensured when optimizing, but now it is also ensured when not optimizing and documented. This also means that you can safely return such a function from its parent and that you will get a warning when trying to return a function that does access the local context (at least in simple cases where this is obvious to the compiler) as in the following example (Godbolt Example)
typedef int cb_f(int); cb_f *foo(int x) { int worker(int y) { return x + y; // capture } return worker; } cb_f *bar(int _x) { static int x; x = _x; int worker(int y) { return x + y; // no capture } return worker; } Nested function that do not access variables of the parent function can still access static variables, named constants, or types (if not variably modified). This is useful for writing callback functions ( Godbolt Example ).
typedef int cb_f(void *, int y); int process(cb_f *cb, void *data) { return cb(data, 5); } int foo(int x) { struct { int x; } data = { .x = x }; int worker(void *_data, int y) { typeof(data) *data = _data; return data->x + y; } return process(worker, &data); } Still, the true power of nested functions is being able to directly access the variables of the parent function. Also, the code above seems like we just simulate such functions by manually adding some boilerplate code - the compiler should certainly be able to help us with this. This is what my second update is about.
Recently, a version of my patch was merged into the development branch of GCC that implements the two new built-in functions that can be used together with the existing built-in __builtin_call_with_static_chain to write this example (Godbolt Example) without requiring trampolines.