An Illustrated History of objc_msgSend
sealiesoftware.com
sealiesoftware.com
I find the following claim slightly misleading:
32-bit x86 was quickly overtaken by 64-bit, so this code received little attention after Tiger and many of those inefficiencies remain to this day.
There are still major 3rd party apps that run in 32-bit mode on the Mac. Google Chrome is probably the most popular.
The 32-bit x86 runtime was quickly overtaken by 64-bit.
32-bit apps will mostly be cross-platform C++ codebases, which, if they use Obj-C at all, will do so very minimally.Apple had little reason to improve 32-bit since apps can simply target 64-bit if they want the speed improvement. From Apple's perspective, Chrome makes a deliberate choice to run in that environment and they have no particular reason to help them out.
Yes, a few notable apps are still 32-bit. But if you look at all Mac OS X programs in existence, a very healthy majority preferentially target the 64-bit platform. The 32-bit apps are the odd ducks, no matter how big a couple of those ducks have grown.
The code was probably originally written for NeXT
by an engineer who was familiar with load-store
architectures like PowerPC but not so familiar with
register-memory architectures like x86 ... Those of
you who do know x86 better may be able to identify
some of the inefficiencies in this code
Why wouldn't/couldn't this function be written in C (are there any instructions that would need some non-portable intrinsics?) and leave it to an optimizing compiler to get the instructions right. Sure, sending messages is low-level and needs to be high performance but that, to me, doesn't necessitate "we have to do this by hand" asm instead of C.A working implementation of this minimal objc_msgSend can be found in Cocotron sources:
http://code.google.com/p/cocotron/source/browse/objc/platfor...
It's the commented-out piece of code at line 10. Here's the C function it calls:
http://code.google.com/p/cocotron/source/browse/objc/objc_ms...
(I realize you said "if you don't care about performance", but ObjC would be a pretty terrible language if it wasn't fast)
What's the Obj-C runtime used on Windows for those apps? Is it a descendant of the NeXT runtime which did run on Win32 at one point, or something else?
Most message sends end up at the same place each time, most of the time, so I'd think this is what you want. But whether it's actually an issue in practice I couldn't say, and if nobody's done it already I suppose it could not be...
Its role is to look up the function pointer that implements a method, then call that function pointer with the arguments passed to objc_msgSend, returning the result of the implementation.
Implementing objc_msgSend in asm guarantees that the argument & return registers aren't touched by the method lookup. Similarly to how ffi libraries and implementations of setjmp/longjmp need to be implemented in asm, it operates at a lower level than C's stack & function abstractions.
id objc_msgSend(id self, SEL _cmd, args...) {
IMP methodImplementation = ...; // look up the method however
return methodImplementation(self, _cmd, args...);
}
But there is no facility in C that lets you take arbitrary additional arguments and then pass them all to another function unchanged.(In theory you could accomplish this using variadic functions. But then every method would have to take a va_list for its parameters instead of just taking a regular parameter list, breaking the idea that ObjC methods are just C functions with two implicit parameters. It would also be substantially slower.)
If you were to write it in C then unless you tore out the ASM generated by the compiler manually you'd have calls to objc_msgSend in the call stack in between every method call, and even still you'd probably emit a bunch of register spills and argument handling.
The thing is, calls to objc_msgSend are _set up_ at the call site as if it were a call to the class's method's function pointer. objc_msgSend is a small trampoline that sits between the call site and the method call, it's not a proper function.
Besides, for something so small, getting a few good engineers (probably engineers who have a background in writing optimizing compilers) to hand-optimize the code and improve on it with new ideas every major release is probably the best option for everybody.
Consider also that objc_msgSend doesn't exist on its own. objc_msgSend_stret handles methods that return a struct, which is a pain in the butt most of the time, and there's also a bunch of hand-rolled variable-argument trampolines for dealing with ObjC blocks (IIRC the file is called a1a2_blocktramps.a, if you want to see something truly disturbing) that do things like calculating byte offsets and jumping to them based on how many arguments we have.
I'm going off on a tangent now but hopefully the point is made. These procedures are so far removed from normal function calls that writing them in C is not the best approach.
Well, if anything ever necessitates finely hand-tuned assembly code instead of living it to the compiler, it's precisely a case such as objc_msgSend.
Apropos nothing… the cache search algorithm is "enter at predictable point, linear search everything". When one thinks of all the PhD time spent developing and analyzing search data structures, this is a good reminder that at small N, other factors dominate.
Also, adding a one byte instruction prefix that you know will be ignored because your processor is happier if instructions aren't so bunched up is so close to witchcraft that the author should avoid campfires with stakes in the center.
Object *o = [Object new];
if(o==nil) { /*handle error and return */ return; }
[o doSomething];
vs. Object *o = [Object new];
[o doSomething];
In the first case I catch a potential error explicitly and in the second case I simply send doSomething to nil which is perfectly fine.I suspect the messaging-to-nil behavior is mostly confusing to intermediate programmers who only have experience with Java's passive-aggressive null references.
[] brackets
{} braces
If I had my way, I'd call them pointyparens.
And, by the way, the less pointy variant that is used in 'proper' typography is called a chevron or guillemet (http://en.wikipedia.org/wiki/Guillemet)
( opening parenthesis
) closing parenthesis
() parentheses
(( two opening parentheses
)) two closing parentheses
Way more info at http://en.wikipedia.org/wiki/BracketParenthesis, from "para" (next, beside, near) and "enthesis" (embedd, inline). So, essentialy to "inline near/next to something" (what we do with a parenthetical phrase).
NSArray *obj = nil;
[obj count]; // return 0
In theory a nil object can hide in your code for a long time if you only use methods that can return 0. NSAssert(array, @"Array can't be nil.");Personally I find that the nil behavior provides a useful base guideline for API design. When defining a method signature in Obj-C, you have to ask yourself if it's clear to the user of the method what happens when the method (inevitably) gets sent to nil. In that way, you have to think about the circumstances of the API's actual use, rather than just the ideal case.