objc_msgSend() Tour Part 2: Setting the Stage
Table of Contents
- objc_msgSend() Tour Part 1: The Road Map
- objc_msgSend() Tour Part 2: Setting the Stage
- objc_msgSend() Tour Part 3: The Fast Path
- objc_msgSend() Tour Part 4: Method Lookup & Some Odds and Ends
Objective-C is, ultimately, a simple set of extensions to C that provide an object oriented coding and runtime model. Objective-C fully embraces C and leverages the C calling ABI throughout. Every method call is really just a C function call with objc_msgSend() acting as the preamble that figures out exactly which C function — which method — to call!
Thus, it is helpful to understand how objc_msgSend() is called and how that relates to C. That is, how does the compiler translate [someObject doSomething: 0x2a] into a call.
What follows is a bit of code that makes a [totally bogus] simple method call followed by the assembly generated.
Code:
@interface NSObject(foo)
- (id) doSomething: (NSUInteger) index;
@end
...
NSObject *b;
NSArray *a;
b = [a doSomething: 0x2a]; // line 11
Assembly:
.loc 1 11 0
movq -16(%rbp), %rdi
movq L_OBJC_SELECTOR_REFERENCES_0(%rip), %rsi
movl $42, %edx
call _objc_msgSend
movq %rax, -8(%rbp)
If you went and poked about in the x86_64 ABI specification (either of those links will work), you would recognize the above as code that sets up a call to a function — to _objc_msgSend() — and then stores the return value a local variable.
That is, objc_msgSend() and, by inference of being tail call optimized and not screwing with registers or stack layout, the method implementations themselves are just standard C functions that follow standard C ABI conventions.
Or, to put it another way, any method can be rewritten as an equivalent C function. The equivalent C function for the doSomething: method above could be declared as:
id doSomething(id self, SEL _cmd, NSUInteger index) { ... }
And, sure enough, if we call that function, we get functionally equivalent assembly code generated:
b = doSomething(a, @selector(doSomething:), 0x2b); // line 16
.loc 1 16 0
movq L_OBJC_SELECTOR_REFERENCES_0(%rip), %rsi
movq -16(%rbp), %rdi
movl $43, %edx
call _doSomething
movq %rax, -8(%rbp)
In particular, on the x86_64 architecture for this particular simple function or method call, the arguments (again, this is only for a simple set of non-struct arguments and return values) will fall into a series of registers in the CPU across the call boundary.
Specifically, the self parameter will be found in %rdi, _cmd in %rsi, and the first declared argument in %edx, followed by %ecx, %r8d, %r9d, and then arguments are pushed onto the stack. Once again, this assumes that the arguments are all of simple type — integers, ids, pointers, etc… — and the rules change with structures and other non-trivial types. Actually, this is another good reason for objc_msgSend to not mess with the stack and registers as the ABI rules are both rather complex and there simply isn’t enough information available at runtime to go off changing argumentation across the messaging boundary (much less do so efficiently)!
We now have enough information to know what state the CPU is in when the call to objc_msgSend() is made. Next up will be the most typical method dispatch.


January 6th, 2010 at 11:43 am
[…] 2.5 License. Amazon.com Widgets « DE Razor Review: BIC Chrome Platinum objc_msgSend() Tour Part 2: Setting the Stage […]
February 4th, 2010 at 12:31 am
[…] Commons Attribution-NonCommercial-ShareAlike 2.5 License. Amazon.com Widgets « objc_msgSend() Tour Part 2: Setting the Stage DE Razor Review: Dorco New Platinum ST-301 […]
February 4th, 2010 at 1:46 am
[…] objc_msgSend() Tour Part 2: Setting the Stage […]
April 18th, 2010 at 2:47 pm
What is cmd? I do not see a cmd in doSomething:. Or is cmd just a holder for the selector “doSomething:”, and the C pseudo-method could be called anything, e.g.
id blortfrobnotz(id self, SEL _cmd, NSUInteger index) { … }