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

Reply via email to