5 ms·
It depends on the platforms C ABI, but no, the argument marshaling for va_args is not necessarily (or even usually) the same as normal args. In the case of iOS
by lgg 9y ago
It depends on the platforms C ABI, but no, the argument marshaling for va_args is not necessarily (or even usually) the same as normal args. In the case of iOS you can look here[1], the relevant bit being: "The iOS ABI for functions that take a variable number of arguments is entirely different from the generic version."
This actually manifests in errors if you directly call objc_msgSend, which is why in order to guarantee direct codeine you need to cast objc_msgSend to the actual prototype you want[2]:
"An exception to the casting rule described above is when you are calling the objc_msgSend function or any other similar functions in the Objective-C runtime that send messages. Although the prototype for the message functions has a variadic form, the method function that is called by the Objective-C runtime does not share the same prototype. The Objective-C runtime directly dispatches to the function that implements the method, so the calling conventions are mismatched, as described previously. Therefore you must cast the objc_msgSend function to a prototype that matches the method function being called."
1: https://developer.apple.com/library/content/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html https://developer.apple.com/library/content/documentation/Xc...
2: https://developer.apple.com/library/content/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html https://developer.apple.com/library/content/documentation/Xc...
- revelation 9y agoThis is C, I'm talking C calling convention (and x64, which is the same). Caller cleans up the stack, so va_list is a zero cost abstraction. Citing the bastard architecture of iOS isn't really making the case for "usually".
- mikeash 9y agoRequiring the caller to put all arguments on the stack isn't "zero cost." For a non-variadic call on ARM64, the first eight parameters (or more, if some are floats) will be passed in registers without ever touching the stack. On x86-64, the caller also has to set %al to the number of vector registers used for the call, and the compilers I've seen always check %al and conditionally save those registers as part of the function prologue. Cheap, but not "zero cost."
- revelation 9y agova_ doesn't change the calling convention. Parameters passed as registers continue to be passed as registers. We could probably argue this some more but I suggest you simply try it with a compiler..
- mikeash 9y agoGood idea! https://gist.github.com/mikeash/ce38d3a77b88734a9e0e9dc3f352fbc7 https://gist.github.com/mikeash/ce38d3a77b88734a9e0e9dc3f352... You'll notice how `normal` takes all of its arguments out of registers `x0` through `x7` and places them on the stack for the call to `printf`. And you'll notice how `vararg` plays a bunch of games with the stack and never touches registers `x1` through `x7`. (It still uses `x0` because the first argument is not variadic.) On the caller side, observe how `call_normal` places its values into `x0` through `x7` sequentially and then invokes the target function, while `call_vararg` places one value into `x0` and places everything else on the stack. So, no, it looks to me like varargs very much change the calling convention.