Hi, Ian...
Michael FIG <[EMAIL PROTECTED]> writes:
> Okay. In this case, I'll take your implementation above and mix it up
> with my other suggestion (the self/stateful self separation) to cook
> up a minimal patch to the compiler that implements this new ABI.
Here is a summary of my first draft of this patch. It compiles and
runs all the object/examples, but I haven't yet touched the function/
directory (since I want to solicit comments first).
I have a some pending TODOs:
* Is it a good idea to push v_self into the struct __send as well?
AFAICT, it is always exactly the same as send.receiver.
* The patch touches all the places where "self" should either become
"_self" or "v_self". I think I got them right, but they should be
reviewed.
* I want to write up some documentation for the Pepsi manual on the C
API. It's basically as follows:
- Use _send(MSG, RCV, ARG...) to send messages.
- Use _self->member only to access member variables. Casting _self
to (oop) is almost always wrong.
- Use v_self for sending messages and when _return'ing.
- Set (*_libid->using_prototypes) = 1; if you want to enable
prototypes (separation of v_self and _self). This flag can be
changed whenever you like (modulo multithreading). I think it's a
bit of a waste of a whole _libid slot, though... it might be a
good idea to make it into a general options word, if we ever have
more.
* I've disabled the i386 asm version of _libid_bind. I'll adjust it
for the new ABI when we're agreed on it.
This is how a typical Pepsi-compiled file looks:
typedef struct t__object *oop;
struct __send;
typedef oop (*_imp_t)(struct __send *send, ...);
/* The receiver is always embedded in the send structure. */
#define v_self (send->receiver)
#include <id/id.h>
[...]
#define _send_out_decl() \
struct { oop _vtable; struct __send send; } _send_out_struct \
__attribute__ ((unused))= { _libid->send_vtable }
#define _send_out (&_send_out_struct.send)
#define _send(MSG, RCV, ARG...) ({ \
_send_out->selector= (MSG); \
_send_out->receiver= (RCV); \
_libid->bind(_send_out, _send_out->receiver) \
(_send_out, ##ARG); \
})
#define _super(TYP, MSG, RCV, ARG...) ({ \
_send_out->selector= (MSG); \
_send_out->receiver= (RCV); \
_imp_t _m = _libid->bind(_send_out, (TYP)); \
_send_out->state= _send_out->receiver; \
(*_m)(_send_out, ##ARG); \
})
[...]
static oop _vector__new_5f_(struct __send *send, oop v__newSize)
{
_send_out_decl();
# define v_send ((oop)send)
# define v__self (send->state)
[...]
{
# define _self ((struct t__vector *)send->state)
[...]
# undef _self
}
# undef v__self
# undef v_send
}
And here is the implementation of _libid->bind:
/* Given send->selector, receiver, and using_prototypes,
set send->state and send->_closure, then return
send->_closure->method. */
_imp_t _libid_bind(struct __send *send, oop receiver)
#undef _libid_bind
{
_send_out_decl();
oop selector= send->selector;
oop fragment= receiver;
static int recursionGuard= 0;
#if GLOBAL_MCACHE
unsigned int probe= 0;
struct __entry *entry= 0;
#endif
oop assoc= 0;
do {
oop vtable= fragment ? (((unsigned)fragment & 1) ? _libid_tag_vtable :
fragment->_vtable[-1]) : _libid_nil_vtable;
dprintf("_libid_bind(%p<%s>, %p)\n", send, selector->selector.elements,
fragment);
if (!vtable) fatal("panic: cannot send '%s' to %s: no vtable",
selector->selector.elements, nameOf(fragment));
# if GLOBAL_MCACHE
probe= (((unsigned)vtable << 2) ^ ((unsigned)selector >> 3)) &
(GLOBAL_MCACHE - 1);
entry= _libid_mcache[probe];
if (entry && entry->selector == selector && entry->vtable == vtable) {
send->state= using_prototypes ? fragment : receiver;
send->_closure= entry->closure;
return send->_closure->method;
}
# endif
assoc= recursionGuard++ ? call(_vtable__lookup_, vtable, selector) :
_send(s_lookup_, vtable, selector);
/*assoc= call(_vtable__lookup_, vtable, selector);*/
--recursionGuard;
if (assoc)
{
# if GLOBAL_MCACHE
# if USE_GC
entry= GC_malloc(sizeof(*entry));
# else
entry= malloc(sizeof(*entry)); /* lossage */
# endif
entry->selector= selector;
entry->vtable= vtable;
entry->closure= &assoc->assoc.value->closure;
_libid_mcache[probe]= entry;
# endif
send->state= using_prototypes ? fragment : receiver;
send->_closure = &assoc->assoc.value->closure;
return send->_closure->method;
}
fragment= using_prototypes ? _send(s__delegate, fragment) : 0;
} while (fragment);
if (selector != s_doesNotUnderstand_)
{
oop result= _send(s_doesNotUnderstand_, receiver, selector);
if (call(_object___vtable, result) == _closure->_vtable[-1]) {
send->state= receiver;
send->_closure = (struct __closure *)result;
return send->_closure->method;
}
}
fatal("primitive error handling failed (%p %p)", receiver, selector);
send->state= 0;
send->_closure= 0;
return 0;
}
The diff to id.h:
--- a/object/id/id.h Tue Nov 20 05:51:58 2007 -0600
+++ b/object/id/id.h Tue Nov 20 05:52:28 2007 -0600
@@ -7,10 +7,13 @@ struct __closure
oop data;
};
-struct __lookup
+struct __send
{
- struct __closure *closure;
- oop state;
+ oop _vtable[0];
+ oop selector;
+ oop receiver;
+ struct __closure *_closure;
+ oop state;
};
struct __slotinfo
@@ -80,10 +83,11 @@ struct __libid
/* messaging */
- struct __closure *(*bind )(oop selector, oop receiver);
- struct __lookup (*bind2)(oop selector, oop receiver);
+ _imp_t (*bind)(struct __send *send, oop receiver);
+ void *unused32;
- void *unused34;
+ int *using_prototypes;
+ oop send_vtable;
void *unused35;
oop (*nlreturn)(oop nlr, oop result);
Anyway, I'll await your comments before I go any further, then I'll
post the full patch.
Have fun!
--
Michael FIG <[EMAIL PROTECTED]> //\
http://michael.fig.org/ \//
_______________________________________________
fonc mailing list
[email protected]
http://vpri.org/mailman/listinfo/fonc