* A Working (but Minimal) JIT
@ 2010-10-22 4:29 Noah Lavine
2010-10-22 21:46 ` Phil
` (2 more replies)
0 siblings, 3 replies; 15+ messages in thread
From: Noah Lavine @ 2010-10-22 4:29 UTC (permalink / raw)
To: guile-devel
Hello all,
After not emailing for a while, I have some good news: a JIT engine is working!
The current version is as minimal as possible. It can only JIT a
function that does nothing and returns 0. And it's only activated by
the 'mv-call' VM instruction. Here's how I've been testing it:
> (define (return-0) 0)
> (define (call-it) (return-0) 0)
> (call-it)
0
That last 0 is returned by JITed code.
The patch against master contains a lot of uninteresting things in
addition to the few interesting ones, so I've put the whole repository
on GitHub to make getting it easier. It's at
git@github.com:noahl/guile-jit.git .
The biggest change since the last version is that I switched from
Lightning to libjit. This was mostly because I realized that if I was
going to use Lightning, I would need to implement a register
allocator, and seemed like a bad idea when libjit already had one.
libjit also solved a memory allocation problem which had been causing
trouble in the Lightning version, and in general has a very
easy-to-use interface, so I think this is a good way to go.
(You'll probably need to configure Guile with '-ljit' to build it,
assuming you have libjit installed in standard include and library
paths. The people from the libjit mailing list recommended using their
git version, which you can find at
http://git.savannah.gnu.org/cgit/dotgnu-pnet/libjit.git, rather than
the latest release.)
I also changed the interface between JITed code and compiled code a
bit, in a way that I think makes more sense, but probably neither way
I've tried is optimal.
I hope this is the sort of JIT engine that you might want to include
into Guile. I think it more or less follows Ludo's plan for
compilation. As I see it, there are now two big tasks to be undertaken
before that could happen, and they're independent.
First, we would need to figure out how to integrate this into the VM
more. Right now it's only activated in the 'mv-call' instruction, but
it should be activated by all of the instructions. Also, the calling
method looks pretty ugly to me now. I'd like to find a way to smooth
it out, but I'm not sure how. Finally, this version uses a five-word
representation for procedures, but it might be possible to get it back
down to four.
Second, the compiler would need to be extended to handle more VM
opcodes. This is a task that could be done incrementally. The compiler
is basically a big switch statement that does each opcode in turn, and
it has the ability to give up at any point if it sees an opcode it
doesn't know how to translate, so opcodes can be added one at a time.
(I would actually like to talk about an alternate way to do that, but
that's a good conversation to have after people decide that the
general design is what Guile needs.)
So, this is a possible way to get a JIT engine in Guile. What do
people think of it?
Noah Lavine
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: A Working (but Minimal) JIT 2010-10-22 4:29 A Working (but Minimal) JIT Noah Lavine @ 2010-10-22 21:46 ` Phil 2010-10-27 21:17 ` Ludovic Courtès 2010-10-27 21:10 ` Ludovic Courtès 2010-11-20 13:37 ` Andy Wingo 2 siblings, 1 reply; 15+ messages in thread From: Phil @ 2010-10-22 21:46 UTC (permalink / raw) To: Noah Lavine; +Cc: guile-devel On Thu, Oct 21, 2010 at 11:29 PM, Noah Lavine <noah.b.lavine@gmail.com> wrote: > So, this is a possible way to get a JIT engine in Guile. What do > people think of it? General question for the list: Have there already been debates on this list about doing native compilation all the time like a lot of Common Lisps & Schemes? Either way, I'm glad to see some progress in this area, great work! ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: A Working (but Minimal) JIT 2010-10-22 21:46 ` Phil @ 2010-10-27 21:17 ` Ludovic Courtès 0 siblings, 0 replies; 15+ messages in thread From: Ludovic Courtès @ 2010-10-27 21:17 UTC (permalink / raw) To: guile-devel Hello! Phil <theseaisinhere@gmail.com> writes: > On Thu, Oct 21, 2010 at 11:29 PM, Noah Lavine <noah.b.lavine@gmail.com> wrote: > >> So, this is a possible way to get a JIT engine in Guile. What do >> people think of it? > > General question for the list: Have there already been debates on this > list about doing native compilation all the time like a lot of Common > Lisps & Schemes? I’m open to both JIT and AOT compilation, FWIW. Andy looked at AOT compilation. There are tools that would be helpful for that, like Sassy, which could lead to elegant code. But then it’s probably more work than JIT, less portable, and it’d be a big change from the user POV, whereas JIT could be completely transparent (you’d keep fiddling with your .scm and .go files the usual way.) Thanks, Ludo’. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: A Working (but Minimal) JIT 2010-10-22 4:29 A Working (but Minimal) JIT Noah Lavine 2010-10-22 21:46 ` Phil @ 2010-10-27 21:10 ` Ludovic Courtès 2010-10-27 22:53 ` Noah Lavine 2010-11-20 13:37 ` Andy Wingo 2 siblings, 1 reply; 15+ messages in thread From: Ludovic Courtès @ 2010-10-27 21:10 UTC (permalink / raw) To: guile-devel Hello Noah! Noah Lavine <noah.b.lavine@gmail.com> writes: > After not emailing for a while, I have some good news: a JIT engine is working! Woow, neat! :-) > The biggest change since the last version is that I switched from > Lightning to libjit. This was mostly because I realized that if I was > going to use Lightning, I would need to implement a register > allocator, and seemed like a bad idea when libjit already had one. > libjit also solved a memory allocation problem which had been causing > trouble in the Lightning version, and in general has a very > easy-to-use interface, so I think this is a good way to go. Interesting. Dealing with end-of-buffers situation is indeed tricky with lightning, and register allocation is lacking (I thought this wouldn’t necessarily be a problem because we can do a reasonable job with a fixed set of statically allocated registers.) > First, we would need to figure out how to integrate this into the VM > more. Right now it's only activated in the 'mv-call' instruction, but > it should be activated by all of the instructions. I think it’s OK to jit just at call time at first. There could be a call counter on procedures, which would allow you to determine whether it’s worth jitting (i.e., only jit a procedure after it’s been call at least N times.) > Second, the compiler would need to be extended to handle more VM > opcodes. My plan was to use macro magic to turn each ‘VM_DEFINE_INSTRUCTION’ into a function definition, except for the ‘call’ instructions and similar. Then all the jit’d code would do is call these functions, so jitting would be quite simple. Still, it’d remove one layer of interpretation, which could lead to performance gains. Then instructions could gradually be rewritten in libjit assembly, but that takes time. > So, this is a possible way to get a JIT engine in Guile. What do > people think of it? I haven’t looked at the code yet, but it surely sounds interesting to me! I’m looking forward to the next news bulletin. ;-) Thanks, Ludo’. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: A Working (but Minimal) JIT 2010-10-27 21:10 ` Ludovic Courtès @ 2010-10-27 22:53 ` Noah Lavine 2010-11-02 22:51 ` Ludovic Courtès 0 siblings, 1 reply; 15+ messages in thread From: Noah Lavine @ 2010-10-27 22:53 UTC (permalink / raw) To: Ludovic Courtès; +Cc: guile-devel Hello! > Interesting. Dealing with end-of-buffers situation is indeed tricky > with lightning, and register allocation is lacking (I thought this > wouldn’t necessarily be a problem because we can do a reasonable job > with a fixed set of statically allocated registers.) That seems true, yes. Given that most of the instructions won't need to be translated, it's possible that switching back over in the middle of the project wouldn't be that hard. (I have a partially-completed Lightning JIT to start from, too.) > I think it’s OK to jit just at call time at first. There could be a > call counter on procedures, which would allow you to determine whether > it’s worth jitting (i.e., only jit a procedure after it’s been call at > least N times.) That sounds good. I think it should probably be activated by all of the 'call' instructions, with a counter on procedures. There is also something that I was trying to figure out but couldn't - how does code from the REPL make its way into the VM? Specifically, what entry point should I modify if I want REPL code to be JITted automatically? (Is it one of the call instructions other than 'mv-call', or a call to a 'vm-*-engine' function?) That would make testing this a lot easier. > My plan was to use macro magic to turn each ‘VM_DEFINE_INSTRUCTION’ into > a function definition, except for the ‘call’ instructions and similar. > Then all the jit’d code would do is call these functions, so jitting > would be quite simple. Still, it’d remove one layer of interpretation, > which could lead to performance gains. > > Then instructions could gradually be rewritten in libjit assembly, but > that takes time. That makes sense. I have an alternate idea about how to do this, but please bear with me, because it would be a little strange. I could certainly rewrite the important instructions with libjit, however, this would result in duplicated code, which is not ideal. I was wondering if you would be interested in coming up with a way to write them once so that they could be expanded to either directly-runnable C code or libjit calls. (Libjit basically implements a subset of C, so I don't think this would be too difficult.) I was also wondering (and here's where the build process would get weird) if this language could be translated with Guile. The goal would be to wind up with a situation where Guile has the ability to take some description of low-level machine operations and translate them either into regular C code or into libjit calls. The reason is that it would be a small step from there to building a libjit-based assembler directly into Guile, and then you've got the ability to compile things into machine code using only Scheme. (You could, for instance, then write a custom compiler for regular expressions that didn't go through the regular Guile VM instructions but just jumped to machine code. That might be really, really fast. You could also then start writing optimizations of Guile code in Guile.) The trouble would be that then Guile would be used to build Guile. It would still be possible to have a version which was completely C code, but that wouldn't be the source version. I know this would be moving in a strange direction, and I would understand if you thought this was a bad idea, but I think it would have have interesting benefits. > I’m looking forward to the next news bulletin. ;-) Thanks! I'll work on that. :-) Noah ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: A Working (but Minimal) JIT 2010-10-27 22:53 ` Noah Lavine @ 2010-11-02 22:51 ` Ludovic Courtès 0 siblings, 0 replies; 15+ messages in thread From: Ludovic Courtès @ 2010-11-02 22:51 UTC (permalink / raw) To: guile-devel Hi Noah! Noah Lavine <noah.b.lavine@gmail.com> writes: > There is also something that I was trying to figure out but couldn't - > how does code from the REPL make its way into the VM? It’s compiled with ‘compile’ from (system base compile), from ‘scheme’ to ‘objcode’ language, then run (unless the ‘interp’ REPL option is set, in which case code is eval’d). See ‘repl-compile’ & co. > I could certainly rewrite the important instructions with libjit, > however, this would result in duplicated code, which is not ideal. I > was wondering if you would be interested in coming up with a way to > write them once so that they could be expanded to either > directly-runnable C code or libjit calls. Of course I’d be interested in such a beast! :-) > (Libjit basically implements a subset of C, so I don't think this > would be too difficult.) Oh, do you have pointers to the relevant part of the libjit manual? > I was also wondering (and here's where the build process would get > weird) if this language could be translated with Guile. Of course, that’s the Holly Grail. :-) Perhaps CGEN <http://sourceware.org/cgen/> could even come into play somehow. ;-) But it’s tricky. You’d have to devise a language that’s both sufficiently expressive to implement all the instructions and sufficiently simple to make compilation to C or libjit doable. [...] > The trouble would be that then Guile would be used to build Guile. That can hopefully be worked around by checking in a copy of the generated C code, for instance. Thanks, Ludo’. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: A Working (but Minimal) JIT 2010-10-22 4:29 A Working (but Minimal) JIT Noah Lavine 2010-10-22 21:46 ` Phil 2010-10-27 21:10 ` Ludovic Courtès @ 2010-11-20 13:37 ` Andy Wingo 2010-11-28 20:56 ` Noah Lavine 2010-11-28 20:58 ` Noah Lavine 2 siblings, 2 replies; 15+ messages in thread From: Andy Wingo @ 2010-11-20 13:37 UTC (permalink / raw) To: Noah Lavine; +Cc: guile-devel Hi, On Fri 22 Oct 2010 06:29, Noah Lavine <noah.b.lavine@gmail.com> writes: > After not emailing for a while, I have some good news: a JIT engine is working! Great news! I have been behind on things a bit, so apologies for taking a month to get back to you, and then only partially. In any case Ludovic probably knows more both about assembly and JIT work, so I'm happy to not be a "gatekeeper" of sorts here... That said, I am concerned about complexity. The current VM, though obviously slow, does have the advantage of being relatively simple. Adding a JIT complicates things. Well, adding another form of compilation complicates things, JIT or AOT or whatever -- so my primary concern is that, as we add native compilation, we need to keep things mentally tractable. I have worked with many people who seem to be able to keep an inhuman number of names and relationships in their head at one time. I fear I am not such a person, so we will have to keep things extra-simple :) So what I would really like to see would be: * Ideally, a 4-word objcode representation that includes native code. * A well-defined convention for that native code. That's to say that the native code could come from JIT compilation, or from AOT compilation. * A uniform way to invoke native code from the VM, and VM code from native code -- *preserving tail calls*. This seems to require either trampolines within the VM or platform-specific tail-call assembly. * A uniform interface to create JIT code as needed, in the call instructions. i.e. if (SCM_UNLIKELY (SCM_NEEDS_JIT (proc))) scm_jit_x (proc); or something. We should be able to merge all of that into Guile before any JIT code goes in, and maybe even before 2.0 if the patches were small enough and came fast enough ;-) Then we could take our time experimenting on how best to do native compilation. So, to reiterate: *simple*, with a good *tail call* story. If we can find a solution that has those characteristics, fantastic :) Andy -- http://wingolog.org/ ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: A Working (but Minimal) JIT 2010-11-20 13:37 ` Andy Wingo @ 2010-11-28 20:56 ` Noah Lavine 2010-11-29 21:25 ` Andy Wingo 2010-11-28 20:58 ` Noah Lavine 1 sibling, 1 reply; 15+ messages in thread From: Noah Lavine @ 2010-11-28 20:56 UTC (permalink / raw) To: Andy Wingo; +Cc: guile-devel Hi, > I am concerned about complexity. I agree that complexity is a problem. I just sent an email about Jitgen, which is something I cooked up to reduce code duplication. However, I can't tell if it's going to end up reducing code complexity or increasing it. What do you think? > So what I would really like to see would be: > > * Ideally, a 4-word objcode representation that includes native > code. > * A well-defined convention for that native code. That's to say that > the native code could come from JIT compilation, or from AOT > compilation. Let me answer these together, because I think they affect each other. I've been poking around in the code, and noticed that procs.c has a reference to "applicable structs". As I understand it, these are structures that can also act as procedures. Procedures with setters are implemented this way. Is it possible to use these as containers for JITed code, and leave the objcode format alone? (This will depend on me learning how they work, since I don't see documentation for them right now.) I've been thinking about this because even if I could put JIT code in the objcode struct now, that wouldn't make much sense for AOT compiled code that wouldn't necessarily have or want objcode. It might be better to pick an interface to compiled code now that would work with AOT compiled code in the future, as you say. > * A uniform way to invoke native code from the VM, and VM code from > native code -- *preserving tail calls*. This seems to require either > trampolines within the VM or platform-specific tail-call assembly. This one could be hard. I can make JITed code call the VM as a tail call, because libjit will generate tail calls if you ask it to, but I don't see how to get from C code to JIT code without pushing onto the stack without either some assembly code or a trampoline. I think a trampoline would be easier, but I will ask on the libjit mailing list how people have solved this before. > * A uniform interface to create JIT code as needed, in the call > instructions. i.e. > if (SCM_UNLIKELY (SCM_NEEDS_JIT (proc))) > scm_jit_x (proc); > or something. That seems easy enough. Noah ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: A Working (but Minimal) JIT 2010-11-28 20:56 ` Noah Lavine @ 2010-11-29 21:25 ` Andy Wingo 2010-12-02 3:58 ` Noah Lavine 0 siblings, 1 reply; 15+ messages in thread From: Andy Wingo @ 2010-11-29 21:25 UTC (permalink / raw) To: Noah Lavine; +Cc: guile-devel On Sun 28 Nov 2010 21:56, Noah Lavine <noah.b.lavine@gmail.com> writes: > 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. > I don't see how to get from C code to JIT code without pushing onto > the stack without either some assembly code or a trampoline. Well, that's what a jit library is for, no? :) Presumably it knows the calling convention for C, so it should know how to do a tail call from C -- implemented in assembly of course. Cheers, Andy -- http://wingolog.org/ ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: A Working (but Minimal) JIT 2010-11-29 21:25 ` Andy Wingo @ 2010-12-02 3:58 ` Noah Lavine 2010-12-06 22:06 ` Andy Wingo 0 siblings, 1 reply; 15+ messages in thread From: Noah Lavine @ 2010-12-02 3:58 UTC (permalink / raw) To: Andy Wingo; +Cc: guile-devel Hello, 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 three possibilities: 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 native function. It sounds like 2 is what you want. Is that right? (Currently I'm doing option 1.) Noah ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: A Working (but Minimal) JIT 2010-12-02 3:58 ` Noah Lavine @ 2010-12-06 22:06 ` Andy Wingo 2010-12-06 22:53 ` Noah Lavine 0 siblings, 1 reply; 15+ messages in thread From: Andy Wingo @ 2010-12-06 22:06 UTC (permalink / raw) To: Noah Lavine; +Cc: guile-devel Hi Noah, Apologies for the stochastic response times here... On Thu 02 Dec 2010 04:58, Noah Lavine <noah.b.lavine@gmail.com> writes: > have the JITed code be a C function that returns an enum to indicate > what the VM should do next. This is widely known as the "trampoline" approach -- you fall down to some known dispatcher, then bounce back up. It would be nice to avoid it though. And actually, terminology-wise, I should have been more precise when I said: >> 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. Let's call this sort of thing the "stub" pattern. > 1. Make the regular 'call' instruction be the one that implements > JITing. But this would introduce a branch to procedure calls. Regarding whether or not to jit, I'm OK with doing that in the `call' instruction. > 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 I think this would be too much overhead. > It sounds like 2 is what you want. Is that right? (Currently I'm doing > option 1.) No, I think what you're doing is right in this regard. But regarding how to call a natively-compiled function -- I think this is really, really critical. It's an important decision that will have some far-reaching effects. I would like for natively-compiled functions to get their objcode replaced with a short stub that tail-calls the native code -- maybe the stub would consist of just one instruction, `native-call' or something. (Obviously this instruction doesn't exist yet). That instruction would do a tail call in such a way that the vm engine is no longer on the stack. I am concerned about JIT libraries precisely because they tend to impose a C calling convention on native code, without allowing for more appropriate conventions that allow for tail calls and multiple values on the stack. Anyway, these are my concerns. Obviously you're the one hacking the code, so do what you like :) Happy hacking, Andy -- http://wingolog.org/ ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: A Working (but Minimal) JIT 2010-12-06 22:06 ` Andy Wingo @ 2010-12-06 22:53 ` Noah Lavine 0 siblings, 0 replies; 15+ messages in thread From: Noah Lavine @ 2010-12-06 22:53 UTC (permalink / raw) To: Andy Wingo; +Cc: guile-devel Hi, > Regarding whether or not to jit, I'm OK with doing that in the `call' > instruction. Sounds good. I'll put it in there. (And also in 'mv-call' and 'tail-call'.) > But regarding how to call a natively-compiled function -- I think this > is really, really critical. It's an important decision that will have > some far-reaching effects. I would like for natively-compiled functions > to get their objcode replaced with a short stub that tail-calls the > native code -- maybe the stub would consist of just one instruction, > `native-call' or something. (Obviously this instruction doesn't exist > yet). That instruction would do a tail call in such a way that the vm > engine is no longer on the stack. > > I am concerned about JIT libraries precisely because they tend to impose > a C calling convention on native code, without allowing for more > appropriate conventions that allow for tail calls and multiple values on > the stack. I see what you mean, but I don't know of a library that fixes that. The problem I'm having with the current code is that (I think) there is no portable way to jump from C code to another function (or block of machine code of any sort) without pushing a frame on the stack. A normal C function call makes the stack deeper (unless the compiler optimizes it, which is a GCC thing), and using a goto where the target isn't fixed at compile time is a GCC extension. And the only other way for a C program to do a jump is setjmp and longjmp, but I don't see a way to use those for this. I think that the best way is to use inline assembly to make the tail call. But in order to do that portably, we would need a macro that expanded to different code for GCC, MSVC, and any other compilers that Guile wanted to support, as well as different code for different architectures. My idea is to first do it with a trampoline, and then maybe replace the trampoline with a macro like that. How does all of this sound? > Anyway, these are my concerns. Obviously you're the one hacking the > code, so do what you like :) I agree with your concerns. Thanks a lot for showing me how to do this. Noah ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: A Working (but Minimal) JIT 2010-11-20 13:37 ` Andy Wingo 2010-11-28 20:56 ` Noah Lavine @ 2010-11-28 20:58 ` Noah Lavine 2010-11-28 22:36 ` Ludovic Courtès 1 sibling, 1 reply; 15+ messages in thread From: Noah Lavine @ 2010-11-28 20:58 UTC (permalink / raw) To: Andy Wingo; +Cc: guile-devel Hi, > I am concerned about complexity. I agree that complexity is a problem. I just sent an email about Jitgen, which is something I cooked up to reduce code duplication. However, I can't tell if it's going to end up reducing code complexity or increasing it. What do you think? > So what I would really like to see would be: > > * Ideally, a 4-word objcode representation that includes native > code. > * A well-defined convention for that native code. That's to say that > the native code could come from JIT compilation, or from AOT > compilation. Let me answer these together, because I think they affect each other. I've been poking around in the code, and noticed that procs.c has a reference to "applicable structs". As I understand it, these are structures that can also act as procedures. Procedures with setters are implemented this way. Is it possible to use these as containers for JITed code, and leave the objcode format alone? (And on a related note, is there any documentation for them?) I've been thinking about this because even if I could put JIT code in the objcode struct now, that wouldn't make much sense for AOT compiled code that wouldn't necessarily have or want objcode. It might be better to pick an interface to compiled code now that would work with AOT compiled code in the future, as you say. > * A uniform way to invoke native code from the VM, and VM code from > native code -- *preserving tail calls*. This seems to require either > trampolines within the VM or platform-specific tail-call assembly. This one could be hard. I can make JITed code call the VM as a tail call, because libjit will generate tail calls if you ask it to, but I don't see how to get from C code to JIT code without pushing onto the stack without either some assembly code or a trampoline. I think a trampoline would be easier, but I will ask on the libjit mailing list how people have solved this before. > * A uniform interface to create JIT code as needed, in the call > instructions. i.e. > if (SCM_UNLIKELY (SCM_NEEDS_JIT (proc))) > scm_jit_x (proc); > or something. That seems easy enough. Noah On Sat, Nov 20, 2010 at 8:37 AM, Andy Wingo <wingo@pobox.com> wrote: > Hi, > > On Fri 22 Oct 2010 06:29, Noah Lavine <noah.b.lavine@gmail.com> writes: > >> After not emailing for a while, I have some good news: a JIT engine is working! > > Great news! > > I have been behind on things a bit, so apologies for taking a month to > get back to you, and then only partially. In any case Ludovic probably > knows more both about assembly and JIT work, so I'm happy to not be a > "gatekeeper" of sorts here... > > That said, I am concerned about complexity. The current VM, though > obviously slow, does have the advantage of being relatively > simple. Adding a JIT complicates things. Well, adding another form of > compilation complicates things, JIT or AOT or whatever -- so my primary > concern is that, as we add native compilation, we need to keep things > mentally tractable. > > I have worked with many people who seem to be able to keep an inhuman > number of names and relationships in their head at one time. I fear I am > not such a person, so we will have to keep things extra-simple :) > > So what I would really like to see would be: > > * Ideally, a 4-word objcode representation that includes native > code. > > * A well-defined convention for that native code. That's to say that > the native code could come from JIT compilation, or from AOT > compilation. > > * A uniform way to invoke native code from the VM, and VM code from > native code -- *preserving tail calls*. This seems to require either > trampolines within the VM or platform-specific tail-call assembly. > > * A uniform interface to create JIT code as needed, in the call > instructions. i.e. > if (SCM_UNLIKELY (SCM_NEEDS_JIT (proc))) > scm_jit_x (proc); > or something. > > We should be able to merge all of that into Guile before any JIT code > goes in, and maybe even before 2.0 if the patches were small enough and > came fast enough ;-) Then we could take our time experimenting on how > best to do native compilation. > > So, to reiterate: *simple*, with a good *tail call* story. If we can > find a solution that has those characteristics, fantastic :) > > Andy > -- > http://wingolog.org/ > ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: A Working (but Minimal) JIT 2010-11-28 20:58 ` Noah Lavine @ 2010-11-28 22:36 ` Ludovic Courtès 2010-11-29 7:18 ` Ken Raeburn 0 siblings, 1 reply; 15+ messages in thread From: Ludovic Courtès @ 2010-11-28 22:36 UTC (permalink / raw) To: guile-devel Hi! >> * A uniform way to invoke native code from the VM, and VM code from >> native code -- *preserving tail calls*. This seems to require either >> trampolines within the VM or platform-specific tail-call assembly. > > This one could be hard. I can make JITed code call the VM as a tail > call, because libjit will generate tail calls if you ask it to, but I > don't see how to get from C code to JIT code without pushing onto the > stack without either some assembly code or a trampoline. You could cheat and assume that the C compiler does TCO (GCC 4.x does). Thanks, Ludo’. ^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: A Working (but Minimal) JIT 2010-11-28 22:36 ` Ludovic Courtès @ 2010-11-29 7:18 ` Ken Raeburn 0 siblings, 0 replies; 15+ messages in thread From: Ken Raeburn @ 2010-11-29 7:18 UTC (permalink / raw) To: Ludovic Courtès; +Cc: guile-devel On Nov 28, 2010, at 17:36, Ludovic Courtès wrote: >>> * A uniform way to invoke native code from the VM, and VM code from >>> native code -- *preserving tail calls*. This seems to require either >>> trampolines within the VM or platform-specific tail-call assembly. >> >> This one could be hard. I can make JITed code call the VM as a tail >> call, because libjit will generate tail calls if you ask it to, but I >> don't see how to get from C code to JIT code without pushing onto the >> stack without either some assembly code or a trampoline. > > You could cheat and assume that the C compiler does TCO (GCC 4.x does). Last I checked, this was dependent on the platform, calling sequence style, stack layout, numbers of arguments, etc. So it wouldn't always happen. And not if optimization were turned off (for better debugging), or certain options given to the compiler, etc. Then there's the whole question of supporting non-GCC compilers. Seems like a bad assumption to me.... Ken ^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2010-12-06 22:53 UTC | newest] Thread overview: 15+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2010-10-22 4:29 A Working (but Minimal) JIT Noah Lavine 2010-10-22 21:46 ` Phil 2010-10-27 21:17 ` Ludovic Courtès 2010-10-27 21:10 ` Ludovic Courtès 2010-10-27 22:53 ` Noah Lavine 2010-11-02 22:51 ` Ludovic Courtès 2010-11-20 13:37 ` Andy Wingo 2010-11-28 20:56 ` Noah Lavine 2010-11-29 21:25 ` Andy Wingo 2010-12-02 3:58 ` Noah Lavine 2010-12-06 22:06 ` Andy Wingo 2010-12-06 22:53 ` Noah Lavine 2010-11-28 20:58 ` Noah Lavine 2010-11-28 22:36 ` Ludovic Courtès 2010-11-29 7:18 ` Ken Raeburn
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).