[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