[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
towards a more unified procedure application mechanism
From: |
Andy Wingo |
Subject: |
towards a more unified procedure application mechanism |
Date: |
Sat, 29 Aug 2009 13:38:13 +0200 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/23.0.92 (gnu/linux) |
Hey all,
I'm working on pulling dispatch of non-VM procedure applications into
the VM -- so invoking primitives written in C can happen quickly from
within the VM.
This is also a step along the path towards native compilation. Procedure
dispatch right now has so many cases -- it is feasible to inline all of
those cases into the VM, but once we generate native code, it would be
ludicrous to do so at all call sites.
One could trampoline out to an "apply" procedure, but that loses on tail
recursion in a number of important cases.
My approach is to start by listing all the different kinds of applicable
objects, and how the VM would apply them.
scm_tc7_program VM procedures
- native support
scm_tcs_closures Interpreted procedures
- Currently calls out to the interpreter, but in the future we will
only see these during the first phase of bootstrapping, when eval.c
is compiling eval.scm. This one should probably be moved to be a
SMOB, because there is no need for code outside of eval.c to know
about closures, and closures will only be live during the boot
process.
scm_tc7_subr_2o SCM (*) () -- 0 arguments.
scm_tc7_subr_1 SCM (*) (SCM) -- 1 argument.
scm_tc7_subr_1o SCM (*) (SCM) -- 1 optional argument.
scm_tc7_subr_2o SCM (*) (SCM, SCM) -- 2 required args.
scm_tc7_subr_2o SCM (*) (SCM, SCM) -- 2 optional args.
scm_tc7_subr_3 SCM (*) (SCM, SCM, SCM) -- 3 required args.
scm_tc7_lsubr SCM (*) (SCM) -- list subrs
All arguments are passed as a list.
scm_tc7_lsubr_2 SCM (*) (SCM, SCM, SCM)
A list subr with two required args.
scm_tc7_dsubr double (*) (double) -- double subrs
Pretty much a bad non-optimization, IMO. Converts its argument to a
double, calls the function, then converts the result double to a
SCM -- dispatching to a generic if the ->double conversion didn't
work.
scm_tc7_cxr c[da]+r
Interprets the SUBR pointer as some kind of bit pattern for chasing
car/cdr pairs. Only useful in the e.g. (map cddr ...) case, because
otherwise cddr gets compiled better for the VM. And, in the future,
map will do some inlining, so that even (map cddr ...) will compile
nicely.
scm_tc7_asubr SCM (*) (SCM, SCM) -- "accumulating" subrs.
With 0 arguments, returns subr (SCM_UNDEFINED, SCM_UNDEFINED).
With 1 argument, returns subr (arg1, SCM_UNDEFINED).
Otherwise returns subr (subr (arg1, arg2), arg3), ... -- like the
semantics of +.
scm_tc7_rpsubr SCM (*) (SCM, SCM) -- predicate subrs.
With 0 arguments, returns true.
With 1 argument, returns subr (arg1, SCM_UNDEFINED).
Otherwise, like an asubr, but returns SCM_BOOL_F if any subr
invocation returns false. Otherwise returns SCM_BOOL_T.
scm_tc7_smob Applicable smobs
Some smobs are applicable. Actually there are a number of smob
apply procedures that one might choose to implement:
SCM (*apply) ();
SCM (*apply_0) (SCM);
SCM (*apply_1) (SCM, SCM);
SCM (*apply_2) (SCM, SCM, SCM);
SCM (*apply_3) (SCM, SCM, SCM, SCM);
In the apply_3 case, the last argument is a rest arg list.
scm_tc7_gsubr Generic subrs
The is the base case in which you can have N required args, N
optional args, and maybe rest args too. SCM_DEFINE produces
gsubrs -- at least, it tries to. scm_c_define_gsubr actually
returns e.g. scm_tc7_subr_1 for certain combinations of req, opt,
and rest args; but we can change that.
scm_tc7_pws Procedures with setters
Gets the procedure, and applies that. This needs to be inlined into
the VM to preserve tail recursion.
scm_tcs_struct Applicable structs
Currently, a struct needs one of two bits set for this to work:
One case is if the procedure is a "pure generic", an instance of
<generic> and not one of its subclasses, in which case we fetch or
compute the effective method, and apply that.
Actually it's worth mentioning here that for pure generics, the
current GOOPS code returns an effective method that includes an
address@hidden isym, to speed up dispatch in the interpreter. It
actually mutates a cache inline to to the memoized effective
method.
Generics that are instances of subclasses of <generic> cannot
currently be applied at all, due to limitations in Guile's
implementation of the generics MOP. But now that we have compiled
procedures, I think we can just require that the effective method
of a generic be any kind of applicable object, but normally a
closure, implementing the dispatch algorithm in Scheme. That should
be sufficiently fast, and if we need VM ops for dispatch, well so
be it. At least that way we move more to Scheme and handle tail
recursion too. That would allow the generic function application
MOP to be finished -- including advice, before/after/around
methods, different dispatch algorithms, etc.
The other case is if the struct is an "operator". I don't really
understand what these are -- the comment in objects.h seems to
suggest that operators were an optimization for applicable structs
and the evaluator. But they do not appear to be used anywhere; I
will be happy to remove them entirely.
Pascal Constanza's R6RS enhancement request seems relevant:
http://www.r6rs.org/formal-comments/comment-6.txt
I think his applicable struct construct seems to be equivalent to
GOOPS/objects.c's "entities". I prefer Constanza's name though.
That's all the cases, afaict. I'll reply to my message with a plan of
action.
Andy
--
http://wingolog.org/
- towards a more unified procedure application mechanism,
Andy Wingo <=