Implementation of GCC's Nested Functions (vs. C++ Lambdas)(uecker.codeberg.page) |
Implementation of GCC's Nested Functions (vs. C++ Lambdas)(uecker.codeberg.page) |
Without all this nested functions are as useful as the "rewritten" examples in the article, one can easily do that by hand without any compiler or language support.
This problem doesn't arise with C++ lambdas because you pass them around as special objects, not as bare function pointers.
For my part, I'll point out that there is one rather important difference between nested functions and C++ lambdas that the author completely ignores, as exhibited by this godbolt example: https://godbolt.org/z/35beWrrTe (note the differences in the generated assembly, especially that which cannot be explained merely by -O0 code generation).
Also misses that the way C++ lambdas work is that one design requirement was that they should be syntax sugar for the functor[0] pattern from C++98.
[0] - Not to mix with ML functors, rather classes with call operator overloaded.
If they don't capture anything, I believe you _can_ pass them as bare function pointers.
Thus, to access stack variables two enclosing functions up, the static link is walked twice.
A reference to a nested function in D is represented by a pair - a pointer to the function, and the static link. (Called a "delegate" in D parlance.) Interestingly, this is the same layout as taking a reference to a member function, where the "this" pointer takes the place of the static link.
This means that references to nested functions are ABI compatible with references to member functions.
Lambdas in D are just a more compact syntax for nested functions.
Generally this is a bit nicer than having explicit lambdas, but I thought the 'best-case' scenario would be if GCC saw into the stack layout of the calling function and could manipulate the calling functions stored stack variables (and saved registers). After all, a debugger can track what variable goes where at every line of code, so this can be done.
Not sure if this would be useful or practical, but would be a nice bit of nerd cred.
A modern compiler IR is probably going to be an SSA-based infinite virtual register set model. In such a model, any variable without its address taken ends up being a register (which may happen to be spilled to the stack). Referencing the variable via a nested function means its a local variable whose address escapes, which kills a lot of optimization potential. It's probably possible to adjust SSA to handle this, but it's a lot of work for little benefit, especially since closure models (closures being regular objects with an unnameable type and an overloaded call operator) have taken over nested functions in language design and thus it isn't really applicable for modern languages.
> After all, a debugger can track what variable goes where at every line of code, so this can be done.
Variable value tracking breaks down pretty much the moment any optimization happens.
> Variable value tracking breaks down pretty much the moment any optimization happens.
I think for nested functions it's not [only] a question of performance/optimizations but of correctness. Even if you properly unwind the stack like a debugger trying to find the parent frame, you may actually find multiple due to recursion. It's impossible to know which one is "yours" unless at least some information about the parent frame was passed to the nested function at invocation time. It can't be a truly static function.