6 ms·
> There's no way to express that args... argument when calling the function pointer Yes there is: va_list. > no way to express forwarding an arbitrary return
by tom_mellior 9y ago
> There's no way to express that args... argument when calling the function pointer
Yes there is: va_list.
> no way to express forwarding an arbitrary return value
Of course there is, and lots and lots of language runtimes implemented in C use those ways. Usually it boils down to having a base type called Object or Value and passing around pointers to that. In fact, from your example it looks like the "id" type is meant to play this role.
This is not syntax checked, but the code above would be something like:
Object *objc_msgSend(id self, SEL cmd, ...) {
fptr = ...lookup code...
va_list args;
va_start(cmd, args);
Object *result = fptr(self, cmd, args);
va_end(args);
return result;
}
Yes, this can be faster in assembly, but it's not true that there is no way to express this. (Unless I'm misunderstanding something.)
- connorcpu 9y agoThe missing bit is that objc_msgSend doesn't know how many parameters are being forwarded, and fptr is just a normal function on the other end, it isn't expecting a va_list, it expects arguments to be passed in the C ABI exactly how they're passed to objc_msgSend
- mikeash 9y agoThese are ways to simulate it. Of course you can simulate it; the language is Turing-complete, after all. But it does not actually do it. You can write something similar to objc_msgSend in C, but you cannot write objc_msgSend in C. Using varargs and passing va_list into the method would mean that your method is no longer a plain C function with the declared parameters plus two hidden parameters. It's now a different sort of beast, and has to use va_ calls to extract the values. This would require a lot more work in the method, and hurt performance. Returning everything as an object would mean boxing and unboxing primitive values at every call, which would be horrendously inefficient. And if you don't care about extracting every last bit of performance, it's much easier to do the lookup approach I discussed than it is to faff around with varargs and wrapping return values.
- CodeWriter23 9y ago@mikeash it looks like you might have a topic for an upcoming Friday.
- tom_mellior 9y ago> These are ways to simulate it. Come on. Taking an argument list in an argument list object and passing that argument list as an argument list to a function is exactly what this was about. It's not a "simulation". It's a C feature for capturing and passing around argument lists. It actually does it. > has to use va_ calls to extract the values. This would require a lot more work in the method Loads from the stack at fixed offsets for every argument instead of having some of the arguments in registers and loading others from the stack at fixed offsets. Yes, that is more work. > Returning everything as an object would mean boxing and unboxing primitive values at every call, which would be horrendously inefficient. True, the runtimes I was thinking of box many things. But you can type-pun pointers to other things, so you don't necessarily have to box everything. I don't know enough about Objective-C's constraints, but I do note that the linked article did talk about using tagged pointers already.
- mikeash 9y agoCome on yourself. I'm talking about the what the actual objc_msgSend actually does. And to make sure I'm clear, what it actually does is get called with arbitrary parameters, pass those arbitrary parameters on to an unknown function implemented to take them as standard C parameters, and then that unknown function returns an arbitrary return value back to the caller. You cannot implement this with plain C. That's a simple fact. If your idea doesn't work for a method that, say, takes a double and returns an fd_set as raw C types then your idea doesn't do what objc_msgSend does. Yes, you can shift the problem around and come up with a system that you can implement in plain C. I outlined one approach for that, and you've outlined another. Nothing wrong with that, but it's not solving the same problem. So feel free to elaborate on other ways that it could be done, but don't tell me I'm wrong because you've come up with a way to solve a similar but different problem.
- revelation 9y ago
- deleted 9y ago[deleted]
- chrisseaton 9y ago> fptr(self, cmd, args); But fptr doesn't take a va_list - it takes the actual arguments.
- tom_mellior 9y agoPresumably, as an implementor of an Objective-C compiler, I would choose how to compile methods, and I might just choose to compile methods to functions taking (something memory-compatible with) va_list.
- icodestuff 9y agoBut then you can't call those methods from C yourself (expecting va_list, received arguments). And what about adding methods to classes from plain C functions? Do you duplicate those functions with a va_list version? Seems like that'll add quite a bit of bloat.
- tom_mellior 9y ago<shrug> I corrected a post saying "X is impossible in C". That's a different issue from whether X is as efficient in C as in assembly language.
- chrisseaton 9y agoI think the point was that you cannot implement this particular function in C - a function that forwards arguments to other functions, given a particular standard ABI. You've changed the requirements by saying that now the other functions use a different ABI, so you've missed the point of why it's impossible to implement this function, in these circumstances, in C.
- tom_mellior 9y agoThe original question in this thread was whether it was really impossible, under any circumstances, to write this in C, as part of an Objective-C implementation that you fully control. It is possible in C, if you can control the ABI. It's the people who insist on a particular pre-existing ABI who are changing the question. Anyway, I think I've said all I'm going to say here.
- icodestuff 9y agofptr doesn't take a va_list, it takes the actual arguments. Also this would leave useless objc_msgSend stack frames at every other level of the stack. There's no way to force the compiler to generate a tail call from inside C. Additionally, you'd have to have callers unbox primitives when fptr returns a C type - the language specifies being a superset of C, so all the C types that the compiler otherwise supports have to work. "id" is not a supertype of int, float, etc. And yes, it is significantly faster. Avoiding writing assembly seems like an awfully odd goal to have for a language runtime.