[Unlike John, I don't have a blog, so I'm going to send this to the
list.  I hope to help influence COLA's future, and so I'm posting
things here as I learn them.]

Hi,

I noticed that both Church-State and the current COLAs depend on
Boehm's libgc for conservative collection.  I have long been pondering
a design for an ultra-simple precise mark-sweep GC with an ABI and
macros that don't put too much burden on a C programmer (since I am a
C programmer, and will probably stay that way for a while).

My design for Figure has been to create a meta-machine (I'm not sure I
can call it a virtual machine), which is calling convention compatible
with the platform's C environment.  The idea is to unify the language
that describes low-level calls (i.e. any arbitrary C primitive or
function call) with the language for high-level calls (prototype
multiple dispatch).

In trying to reconcile these issues, I've been forced to make several
mechanical transformations to my C sources, until I've evolutionarily
discovered a style that supports the ABI.  That style includes
reifying stack frames and a single return register, so that there is a
clear path for the GC to trace.  The way I've chosen to do this is to
pass a single pointer to every high-level call (and as much of the
mid-level runtime as possible), without imposing any burden on
functions that are called below.  This also means that there are no
global variables in Figure (well, just one, but I'll get rid of that
one after I finish writing this e-mail).

The ABI for a typical piece of code looks like this:

/* Think of Gut as the high-level object type system. */
typedef void *Gut;

struct _Figure
{
[...]
  KSFrame *frame;
  /* This is the return pseudoregister.  Every function puts Gut
     results in here so that the precise garbage collector can find
     them, no matter what program state. */
  union
  {
    /* This is a union only so the callee can avoid tedious
       typecasting. */
    ptrdiff_t i;
    Gut p;
    GutFun f;
  } x;
  /* This is nonzero iff X should be traced by the precise GC. */
  int x_is_gcroot;
};

Where KSFrame is:

struct _KSFrame
{
  KSFrame *caller;
#ifndef NDEBUG
  /* Current source location. */
  char *file;
  char *func;
  int line;
#endif
  [more high-level call information, such as object states and selves...]
};

I've annotated this function to try to explain better:

static void
skin_parse(Figure *f, GushInput *in)
{
  int c;
  /* Create a new frame, called "ks", and link it into Figure's call
     stack (f->frame).  Declare "before" and "quote" as objects that
     need to be traced by the precise GC. */
  ks_enter(f, GushInput *before; Gut quote;);
  /* Make a high-level call to skin_parse_spaces (imported into the
     current module). */
  gut_call(f, skinParseSpaces, in);
  /* Clone the input stream. */
  gut_clone(f, in);
  /* Assign the cloned pointer to the "before" temporary. */
  ks.before = f->x.p;
  /* Read from the input stream. */
  gut_call(f, read, in);
  gut_raw_integer(f, f->x.p);
  c = f->x.i;
  if (c == '(')
    gut_call(f, skinParseList, in);
  else if (c == ')')
    /* Signal a condition, which is a restartable exceptions. */
    gush_error(f, ks.before, "skin", "too many close parentheses");
  else if (c == '.')
    gush_error(f, ks.before, "skin", "unexpected improper list separator");
  else if (c == '"')
    gut_call(f, skinParseString, in);
  else if (c == '-' || isdigit(c))
    {
      /* Restore the state of the input stream from the cloned copy. */
      *in = *ks.before;
      /* Parse the number. */
      gut_call(f, skinParseNumber, in);
    }
  else if (c == '\'')
    {
      /* Implement syntactic quote sugar. */
      gut_name(f, "quote");
      /* Save a reference to the "quote" symbol. */
      ks.quote = f->x.p;
      /* Parse the next object. */
      gut_call(f, skinParse, in);
      /* Create a pair out of that object and the empty list. */
      gut_pair(f, f->x.p, f->gut->empty_list);
      /* Add the quote symbol. */
      gut_pair(f, ks.quote, f->x.p);
    }
  else
    {
      /* Restore the input state. */
      *in = *ks.before;
      /* Call the name parser. */
      gut_call(f, skinParseName, in);
    }
  /* Pop the stack frame without modifying the return register.
     (Take the result of the last call as the value. */
  ks_leave(f);
}

There are other ks_*leave* macros that allow setting of the return
register, and marking it as a gc root or a raw value.

You can see that this creates a much more asm-like flow.  There are
very few expressions, just statements (nearly every function returns
void).

Next on my agenda is actually implementing the mark-sweep collector,
and after that, multithreading!  Whoopie!

As usual, the state of things is at:

http://michael.fig.org/figure.c

Figure.sln and Figure.vcproj are also there for the VC++ enthusiasts
(but not yet tested for this latest iteration).  There is only dlopen
to port to new platforms, everything else is ANSI.

That's all for now,

-- 
Michael FIG <mich...@fig.org> //\
   http://michael.fig.org/    \//

_______________________________________________
fonc mailing list
fonc@vpri.org
http://vpri.org/mailman/listinfo/fonc

Reply via email to