What follows (across this and 3 more posts, maybe more) is a rather detailed tour of objc_msgSend() as implemented in Mac OS X 10.6.2. Rather detailed in that every instruction will be explained. Even though it is relatively few instructions, there is a considerable amount of background information that is helpful to understanding the objc_msgSend() instruction stream.

The motivation behind these posts is entirely selfish. I find the best way for me to learn something is to know it well enough to be able to explain any detail to a room full of folks in full-blown student mode.

Table of Contents

For a variety of reasons, I oft find myself staring at streams of x86_64 instructions. This is often the stream of instructions that sit at the very core of Objective-C and dispatch each and every method call. That is, objc_msgSend() .

Well, technically, a handful of different versions of objc_msgSend() , each written to handle the calling conventions required to deal with various return types under the x86_64 ABI (Application Binary Interface). Oh, and the vtable dispatch stuff (different post).

Obviously, objc_msgSend() is called 10s of millions of times simply booting the system and launching some apps. Thus, every cycle counts and, no surprise, objc_msgSend() is written in hand tuned assembly. Of all of the other performance optimizations and implementation details in this code, there are three that I find particularly notable.

Don’t Mess With Registers (unless absolutely necessary)

Tail Call Optimized

Don’t Mess with the Argument List

These are really all three part of the same thing (i.e. some arguments are in registers, for example). objc_msgSend() is designed to dynamically determine the implementation of a method — the function pointer that is the IMP tied to the selector on the targeted instance — without changing any of the caller/callee state. This enables a tail-call optimization that allows objc_msgSend() to jump [JMP] directly to the implementation of a method.

This is also the reason why you don’t see objc_msgSend() in backtraces save for when a crash occurs in objc_msgSend() . Given that there were several 10s of millions of invocations of objc_msgSend() prior to the one that crashed, you can pretty much always assume that the crash is because of a bad pointer being passed to objc_msgSend() and not due to a bug in objc_msgSend() itself. Of course, if you find yourself crashing in objc_msgSend() , go read and then re-read this.

This relative handful of instructions — 76 in the disassembly below (taken on Snow Leopard 10.6.2 — objc-437.1), most of which are not executed in a typical method dispatch — does an awful lot of stuff upon each invocation.

Namely:

Check for ignored selectors (GC) and short-circuit. Check for nil target. If nil & nil receiver handler configured, jump to handler If nil & no handler (default), cleanup and return. Find the IMP on the class of the target and jump to it

Search the class’s method cache for the method IMP If found, jump to it. Not found: lookup the method IMP in the class itself

If found, jump to it. If not found, jump to forwarding mechanism.

The next part will set the stage for diving into the actual objc_msgSend() stream.

: link_pages issince version 2.1.0! Use wp_link_pages() instead. inon line