Re: Designing a calling convention for a Lisp->C compiler

"BGB / cr88192" <cr88192@hotmail.com>
Sun, 6 Jun 2010 09:44:59 -0700

          From comp.compilers

Related articles
Designing a calling convention for a Lisp->C compiler msimoni@gmail.com (Manuel) (2010-06-03)
Re: Designing a calling convention for a Lisp->C compiler marc@lithia.nl (Marc van Lieshout) (2010-06-03)
Re: Designing a calling convention for a Lisp->C compiler kym@sdf.lonestar.org (russell kym horsell) (2010-06-04)
Re: Designing a calling convention for a Lisp->C compiler kym@sdf.lonestar.org (russell kym horsell) (2010-06-06)
Re: Designing a calling convention for a Lisp->C compiler cr88192@hotmail.com (BGB / cr88192) (2010-06-06)
Re: Designing a calling convention for a Lisp->C compiler comp.compilers@inglorion.net (Robbert Haarman) (2010-06-07)
Re: Designing a calling convention for a Lisp->C compiler gene.ressler@gmail.com (Gene) (2010-06-06)
| List of all articles for this month |

From: "BGB / cr88192" <cr88192@hotmail.com>
Newsgroups: comp.compilers
Date: Sun, 6 Jun 2010 09:44:59 -0700
Organization: albasani.net
References: 10-06-008
Keywords: Lisp, C, translator
Posted-Date: 06 Jun 2010 17:02:50 EDT

"Manuel" <msimoni@gmail.com> wrote in message
> Hello group!
>
> I am building a Lisp to C compiler. I don't know very much about the
> lowlevel details of things like stdarg, so I it is rather unclear for
> me how to go about making this as efficient as possible.


<snip>


> The compiler would translate this call to something like this:
> some_function(2, 2, a, b, "name-1", c, "name-2", d);
>
> The function would then use stdarg to take the passed arguments apart,
> and assign them to local variables inside the function.
>
> What do you think?


what is best depends on the specifics of how the compiler is to be
implemented...




If the call-site has static visibility of the called function, then it may
be better off (if trying to play well with C), to simply have fixed
arguments for all the optional arguments, and if they may be left out,
simply sending them a NIL or similar.


Otherwise, Apart From Trying To Look Like C, I would not likely recommend
varargs, since varargs doesn't encode the number or types of arguments or
any such other useful info (which is needed for LISP).


Instead, I Would Probably Recommend Sending Arguments as a raw array
(probably with a count).


So, Something Like:
vmObj vm_foo(vmCtx *ctx, vmObj *args, int nargs);




Even In The Static Case, I Would Likely Still Recommend Against Using
Varargs To pass "rest".
about the only real merit of varargs is that they can be used directly by
unaware C programs.


Even In The Best Case, LISP and C semantics differ enough that direct
calling between them (at the ABI level) would likely be problematic, and so
the better option may be to have separate ABI's between them, and to instead
aim for "transparent" calling (where the border between the languages is
hidden), but none-the-less they use different internal ABI's, and the
interfacing is typically done via specialized transfer functions (or
thunks), which serve both to marshall arguments as well as to address other
interfacing issues.


this can be done in both the static-compilation or dynamic (interpreter/JIT)
case (although, in this latter form, is a little more complicated to pull
off).


Note: One Can Still Use The Native C ABI to implement the basic call
mechanics (there are reasons to do this), just it is not necessarily needed
to pass arguments the same way as C code. this does allow VM internal
functions to be written in C, they will just see the arguments passed in a
VM-specific manner (such as via an array).


it is much the same: the native C ABI doesn't support closures/... either
(making closures look like ordinary function pointers, for example, is a bit
of an ugly trick...).


granted, LISP naturally uses lists for rest, however using lists in the
default case is not recommended, since they tend to add extra performance
overhead (needing to cons arguments, destroy the args lists, ...). the rest
arguments could then be coppied into lists as needed (such as when the
callee actually has a rest argument).




now, for calling into native C functions, a piece of code could actually be
used to marshall the arguments into the form expected by the callee...


for example, a fairly "generic" way I had been using in my framework is to
do this in several steps:
have signatures for all the C functions to be called (in my case, mined via
tools, but manual entry is possible);
first step is to marshall the arguments from the VM references into the form
generally expected by the C function, which are then placed into a buffer
(specifics may be arch specific);
use a chunk of ASM to take the contents of this buffer, and put it onto the
stack (or into registers) as per the specific ABI for that OS+compiler+CPU.


if the compiler is a static compiler, it may also be reasonable to instead
generate functions specialized to marshall the arguments (which is likely to
be faster than using signatures and temporary buffers).




similar is still used even within C, since C naturally lacks the ability to
call arbitrary functions with arbitrary args-lists, and these sorts of
mechanisms allow this.


using a different ABI from C, but supporting (mostly) transparent
interfacing (via thunks), was what I ended up doing for my ECMAScript
variant.




granted, directly using the C ABI (and using special handling for
dynamically-typed arguments, or storing the context in a TLS variable, ...)
is still a likely option (and avoids the need for transfer functions).


Post a followup to this message

Return to the comp.compilers page.
Search the comp.compilers archives again.