7 ms·
Nested functions may require a context pointer of some kind, which the caller can't supply. One way of doing this: create a thunk on the stack that provides the
by tom_ 18d ago
Nested functions may require a context pointer of some kind, which the caller can't supply. One way of doing this: create a thunk on the stack that provides the context pointer, and use that address as the pointer to the nested function.
But now the stack needs to be executable.
- ok123456 18d agoDoesn't x86 actually support nested call pointers using enter to natively support this in Pascal?
- klodolph 18d agoIt’s not a question of ISA support. If you call via a function pointer, how do you supply the pointer to the data? (It has to either be in a separate place from the function code, or the same place. One requires an ABI change, the other an executable, writable section of memory.)
- WalterBright 18d ago> If you call via a function pointer, how do you supply the pointer to the data? D has the notion of a "delegate", which is a (function pointer) and (context pointer) pair. This is incredibly useful, because delegates can: 1. call nested functions that need a pointer to the stack frame of the nestee function 2. call member functions that need `this` pointer 3. call lambdas 4. call COM member functions The neato thing about this is the ABI for delegates is all the same, so a function that gets a delegate parameter will work with any of 1..4. It's one of the most used features of D.
- klodolph 18d agoYes, that’s the “ABI” alternative that I was referring to.
- uecker 18d agoThis is why I want to have such a feature in C. It would be extremely useful for language interoperability. But because ISA was mentioned, x86 does indeed even have native support for this: https://devblogs.microsoft.com/oldnewthing/20231211-00/?p=109126 https://devblogs.microsoft.com/oldnewthing/20231211-00/?p=10... These instructions are not too useful though and I do not think anybody uses them.
- klodolph 18d agoThis feature would IMO violate the contract that C allows you to specify memory layout of objects, to some level of detail (I am being a little vague about the “level of detail”). Supporting closures, more or less, requires design decisions that are equivalent to choosing a specific layout for objects in an object-oriented language. C, as it is, makes none of these assumptions and you can translate a lot of different language ABIs into some C code (that may be clumsy). Keeping the abstraction that function pointers = pointers to entry points for functions, well, that’s frustrating for C programmers writing C programs, but extremely useful for interoperability.
- uecker 18d agoAt the moment we can not call nested functions or other things from other language from C, which is a pretty big hole in our interoperability story. I do not see why we need to make any design decision to specify object layout, we simply need a code pointer and static chain pair, which would then be sufficient to call arbitrary entities from other languages.
- klodolph 18d ago> I do not see why we need to make any design decision to specify object layout > we simply need a code pointer and static chain pair The code pointer and chain pair needs an object layout. You would have to pick a specific layout, and it would not be compatible with other languages that have a different layout. Right now I can do this: struct a { void (*fun)(void *ctx); int data1; int data2; }; struct b { void (*fun)(void *ctx); void *ctx; }; And I could call them: struct a *aptr; a->fun(aptr); struct b *bptr; b->fun(bptr->ctx); There is only one neutral, maximally compatible option here—which is to have the function pointer separate from the arguments you want to pass in, and pass them in explicitly. If you add closures to C you are making compatibility worse, not better.
- QuadmasterXLII 18d agobut that can just be executable heap?
- klodolph 18d agoYes, but the advice was W^X. Either a page of memory is executable or writable but not both at the same time.