Let's Build a Simple Interpreter, Part 1 (2015)
ruslanspivak.com
ruslanspivak.com
I still don't understand how JIT compilers work, which is probably the majority of compilers that I'm using today. It seems that there aren't many resources or established textbooks for this area. I feel we're still left in the dark in this age.
I think the author really meant to say you have to understand some amount of how computers work and how compilers translate HLL's to them in order to fully understand why your HLL programs behave the way they do. That's the charitable interpretation. I'm actually writing this comment on a laptop sitting on a book that did that for me. It's called Write Great Code by Randall Hyde. Guy who made Art of Assembly Programming and HLA. It was enlightening.
And even in C, optimizations can change your code quite a lot.
So I do think having a basic udnerstanding of how compilers work under the hood should be required for every programmer.
As I said elswhere, every proper programmer should write at least one toy language with a compiler and / or interpreter.
This is where the complexity is, managing when to JIT compile a function and how to pass control back to the interpreted code. There are so many ways to do this, from compiling individual functions to doing a tracing JIT like luajit is.
So, yes, the compiler part of a JIT is "just" a compiler that outputs code to memory but the complexity is doing it just-in-time. Start executing code in an interpreter, find a hot path, then compile that and make the compiler emit instructions that return the control back to the interpreter when executing the hot path is done.
So you wouldn't consider the .NET framework a JIT? It never interprets any code, just compiles things as they are used.
Saying you need an interpreter is clearly a silly definition of a JIT.
It compiles some code, executes it and control returns back to either interpreted code or the framework that decides what to compile next. It's a JIT, because it can't be done ahead of time.
Just doing the final stage of code generation at runtime/startup (like, say, PNaCL does) would not qualify. You could as well generate code for all target architectures ahead of time, but it might be more practical to leave that to the client.
Feel free to disagree with my definition, but I like to draw a distinction between just compiling code at runtime and a JIT, that compiles/optimizes code as it becomes possible.
I agree with that, but I call the first thing a JIT, and the second thing a dynamic compiler. There's a PhD someone wrote about the difference but I can't find it now.
I'm not too familiar with .NET but based on your description, no I would not qualify that as a JIT according to the definition I gave above.
If you could as well do it ahead of time, I would not call it a JIT.
Is the reason for .NET's late compilation to generate code for the target CPU architecture? Or does it do some optimization based on runtime information (this would qualify as JIT)?
It doesn't necessarily have to be a (bytecode) interpreter to call it a JIT, but it does have to compile code at runtime and have the control return back to the framework for compiling more code as needed.
Do you know why it's doing incremental compilation? Is there something that can't be done ahead of time? E.g. inlining dynamic ("virtual function") calls and then applying optimization?
EDIT: spelling
This has some disadvantages - you have to wait for the JIT to run and can't keep running in the interpreter while it compiles, and some advantages - it's simpler.
But it is not. An interpreter for a curly bracket language or Algol descendant (Algol, C, C++. C#, VB.Net, Pascal) looks quite different from one for a stack based language (the canonical one being Forth I suppose) and different again from on that uses S-expressions (Lisp, Scheme, etc.), and so on.
A simple interpreter for a stack based language can be a lot shorter than one for a block structured language
I also emulated Python's `raw_input` function to my own C version :)
It was also a chance for me to use `setjmp` to implement exception handling in C. Take a look at this commit: https://github.com/rmccullagh/calculator/commit/6afac5e5924b...
https://codeboard.io/projects/9288
It's interesting to see how much "overhead" static typing and language verbosity generates. But maybe there are also ways to write it more compact in Java, not sure.