3 ms·
Having lookup instead of send as the primitive operation also allows you to generate code like this for the call site: ({ static SEL last_isa = NULL;
by dfox 9y ago
Having lookup instead of send as the primitive operation also allows you to generate code like this for the call site:
({
static SEL last_isa = NULL;
static IMP last_imp = NULL;
if (object->isa != isa){
last_isa = object->isa;
last_imp = lookup(object->isa, sel);
}
last_isa(object, selector, arguments...);
})
(modulo the fact that you cannot generate this by dumb string substitution without compiler extension like gcc's ({...}))
Smalltalk/X takes this to the extreme by compiling all sends into code like:
{
static struct cache = {.imp = &magic_global_method, .class=NULL}
cache->imp(&cache, object, selector, arguments...);
}
And generates something like this into every method prologue:
if (cache && self->isa != cache->class){
cache->class = self->isa;
cache->imp = lookup(object, selector);
return cache->imp(NULL, object, selector, arguments...)
}
It looks convoluted and uses one additional word of stack space per call, but does not contain any unpredictable indirect branches in the fast path (and in fact reduces overall code size as it can be expected that there are many more sends than methods).