[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: A Working (but Minimal) JIT
Re: A Working (but Minimal) JIT
Wed, 1 Dec 2010 22:58:47 -0500
I need to apologize for some temporary insanity when I wrote that last
post. I do know a way to get JITed code working correctly with tail
calls. It's not quite as efficient as I would like, but it's decent.
We have the JITed code be a C function that returns an enum to
indicate what the VM should do next. One value means "return
normally", one means "signal error", one means "tail call". For tail
calls, the JIT-code function returns before the tail call is made, so
the stack doesn't get bigger.
About tail calls - it would be better to not even make that call, but
any way to do that that I can see would require non-portable compiler
extensions (probably inline assembler). As you say, the JIT library
knows how to make a tail call, but unfortunately it will only return
code in the form of a C function, and calling that function would push
a frame onto the stack. We could try to do tail calls with a goto, but
labels as values are a GCC extension.
I looked around a bit for a library of inline assembler that would let
us do tail calls on different C compilers and architectures, but I
didn't find one. So I suggest that I first make the JIT work in the
non-ideal way and then start work on that library :-). (I am not at
all attached to my hack if someone knows a better way, though.)
>> I've been poking around in the code, and noticed that procs.c has a
>> reference to "applicable structs".
> They aren't as efficient as they could be. Currently applicable structs
> are the only nonuniform case in procedure calls -- I was trying to get
> procedure calls to have no conditional branches, and they (and
> applicable smobs) are the only odd cases.
> I prefer the trampoline approach used by primitives, continuations,
> foreign functions, etc -- they are normal procedures, whose code is a
> stub that does the type-specific dispatch.
> This is also (and even more the case) what you want for native
> procedures -- a native procedure call should just be a jmp and arg
> shuffle. Objects which don't have native implementations should have a
> stub that does the right thing.
After looking at continuations and foreign functions, it looks like
they generate objcode that calls a special VM instruction which
actually implements what they do. When I think about this, I get to a
pretty weird inversion of how procedure calls currently work. I think
it's what you meant, but let me give my reasoning to be sure:
The trouble with doing it exactly like continuations and foreign
functions is that every procedure could potentially be JITed, so every
function would have to have the special VM instruction. So that leaves
1. Make the regular 'call' instruction be the one that implements
JITing. But this would introduce a branch to procedure calls.
2. Add an instruction, to be put in the normal function preamble, that
decides whether or not to JIT the function that is currently
executing, and a second instruction that calls a JITed function. If
the first instruction decides to JIT, it would have to a) JIT the
current objcode and b) modify the objcode struct in use to contain
stub code that would include the second instruction, so future calls
would call the JITed code.
3. Change calls so that all procedure calls are actually calls to C
functions, presumably changing the struct that procedures live in as
well. Then JITed functions would just have their code there, and
un-JITed functions would have vm_debug_engine (or a wrapper) as their
It sounds like 2 is what you want. Is that right? (Currently I'm doing
- Re: A Working (but Minimal) JIT,
Noah Lavine <=