mulle-objc: a meta calling convention
Continued from mulle-objc: a new Objective-C
So the mulle-objc runtime is written in pure C. How is that supposed to work ?
Currently a method is just like a C-function. You declare the parameters and they are passed by using the current ABI scheme. Sometimes on the stack, sometimes in registers, sometimes in a mix of both.
That makes things very complicated on the runtime side, and you need special libraries like FFI or write your own assembler code to access the arguments.
I try to keep the language consistent with respect to parameter-vs-argument. So as we are inside the function, it is parameter. In that respect,
main( argc, argv)
is wrong :(, confusing.
A method in mulle-objc has two faces
On the syntax side, you still write code like this:
- (int) fooWithA:(int) a b:(int) b
{
return( a + b);
}
but internally the compiler creates a method like this:
struct whatever
{
int a;
int b;
};
- (int) fooWithA:b:(struct whatever *) _param
{
return( _param->a + _param->b);
}
mulle-objc uses a modified clang
compiler that creates the new meta-calling convention. I call it meta calling convention, because it does not conflict with the actual calling convention used by the compiler like cdecl or fastcall, but rather exists “on top” of it.
All parameters outside of self and _cmd are passed via a single parameter _param, that references a struct. This struct is created in the default C struct manner, and not in the “hidden” stack/register ABI layout, so sizeof()
and offsetof()
can be used.
And the message send routine itself looks something like this now:
void *mulle_objc_object_call( void *self,
SEL _cmd,
void *_param)
The fixed number of only three parameters makes everything much easier for the runtime implementation.
A more formal definition of the mulle-objc meta call convention
- a method which expects no arguments does not get a _param parameter (or _param may be any bogus value, if that is more convenient).
- a method with one parameter that fits inside (void *) may get that parameter directly. In this case _param will not exist, but just the regular parameter name.
- all other methods get a struct address passed in as _param.
- a method returning a
struct/union
type or a type that doesn’t cast transparently intovoid *
always gets a _param parameter with a size large enough to hold the return value - the struct passed in is an even multiple of (5 * sizeof( void *)) bytes
- the struct passed in is “destroyed” by the callee and the caller can make no assumptions about its contents after the call regardless on how many parameters it actually passed. All bytes must be considered garbage after the callee returns, except when the returned value is placed in the _param block
- as the method is free to change the struct. It can use it in tail calls asparameter storage.
Food for thought
- How much slower is this, when ?
- How fast and simple is it now to forward a message ?
- What happens with variable style arguments ?
- How could tail calls reuse and expand existing parameter blocks ?
Continue to mulle-objc: removing superflous ifs
Post a comment
All comments are held for moderation; basic HTML formatting accepted.