> I just meant that even being aware this is a naive copy-and-patch JIT, my first impression was that the code was slightly worse than I expected.
> "there’s only so much one can do without touching the data model"
You probably want to look at the other link in that PR, which demonstrated how well copy-and-patch can do for another dynamic language (Lua): [1]
Of course, whether or not CPython could eventually make it to that point (or even further) is a different story: they are under a way tighter constraint than just developing something for academia. But copy-and-patch can do a lot even for dynamic languages :)
That's correct. Lua function calls are not that easy to remove, as function is first-class value in Lua so can be redefined at any time. To remove a function call, you need speculative compilation and OSR-exit, which is outside the job of the baseline JIT.
That's an interesting approach :)
Though it only works if the control flow in the language is exactly "paired" (no continue/break, no goto, etc), I guess?
I agree with your point, but I want to point out that Deegen also provided APIs to hide all the details of inline caching. The bytecode only specifies the body lambda and the effect lambdas, and Deegen lowers it to an efficient implementation automatically, including exotic optimizations that fuses the cached IC effect into the opcode so one indirect branch can be avoided.
If LuaJIT interpreter were to employ IC, it would have to undergo a major rewrite (due to its assembly nature) to have the equally efficient code as LJR (that we generate automatically). This is one advantage of our meta-compiler approach.
Finally, although no experiment is made, my subjective opinion is already in the article:
> LuaJIT’s hand-written assembly interpreter is highly optimized already. Our interpreter generated by Deegen is also highly optimized, and in many cases, slightly better-optimized than LuaJIT. However, the gain from those low-level optimizations are simply not enough to beat LuaJIT by a significant margin, especially on a modern CPU with very good instruction-level parallelism, where having a few more instructions, a few longer instructions, or even a few more L1-hitting loads have negligible impact on performance. The support of inline caching is one of the most important high-level optimizations we employed that contributes to our performance advantage over LuaJIT.
That is, if you compare the assembly between LJR and LuaJIT interpreter, I believe LJR's assembly is slightly better (though I would anticipate only marginal performance difference). But that's also because we employed a different boxing scheme (again...). If you force LJR to use LuaJIT's boxing scheme, I guess the assembly code would also be similar since LJR's hand-written assembly is already optimal or at least very close to optimal.
Clang/LLVM accepts (little-known but documented) flags to let you align code block using a custom alignment, though it only works at file-level. See [1].
However, I'm not sure if doing so is useful or necessary. Interpreter performance is sensitive to code layout (which affects hardware branch predictor accuracy), but I don't think there is a general way to optimize the code layout to make the branch predictor as happy as possible.
So if you changed your code alignment and saw a perf change, it's more likely caused by the random perf gain/loss due to code layout change, not because that 1-byte-alignment is better than 16-byte-alignment or vice versa.
I just want to point out that LLVM is not a runtime dependency. It is only a build-time dependency if you want to build LJR from source. Once LJR is built, it is stand-alone and does not need LLVM at runtime.
The copy-and-patch paper is also written by me. Deegen a follow-up work of copy-and-patch, and it will use copy-and-patch as a tool to build its JIT tiers in the future.
> More recently I have been experimenting with a new calling convention that uses no callee-saved registers to work around this, but the results so far are inconclusive. The new calling convention would use all registers for arguments, but allocate registers in the opposite order of normal functions, to reduce the chance of overlap. I have been calling this calling convention "reverse_cc".
As explained in the article, LLVM already has the calling convention you are exactly looking for: the GHC convention (cc 10). You can use it to "pin" registers by passing arguments at the right spot. If you pin your argument in a callee-saved register of the C calling conv, it won't get clobbered after you do a C call.
Yes, the object can become unreachable, but we won't know it until the end of the current GC cycle (and the whole purpose of GC is to figure that information out). So yes, even if it has become unreachable, it is live (and must be live) until the end of the cycle.
I think the point you missed is: the function 'cellContainsLiveObject' is not used by GC, it is used by allocator to tell if the cell is available for allocation. So it's fine if the function returns true but the object is actually unreachable, but not the other way around.
> He (Abbot) proposed instead an alternative framework called Merit, Fairness, and Equality (MFE) whereby university applicants are treated as individuals and evaluated through a rigorous and unbiased process based on their merit and qualifications alone
I am in full support with this, though it seems to me this is too idealized to be practical in practice. How can one reach a fair judgement of a student only based on a 1000-word essay in his/her application (which might not even be written by him/herself)?
However, I'm still saddened by that the MIT response to this incident is simply "it is Abbot's right of free expression to say whatever he wants", but nothing about what he actually said, or whether it at least makes some sense. It's as if MIT treated Abbot as an unknowing child whose nonsense words shall be tolerated, which is disturbing. Below is part of the mail list letter I received:
> Freedom of expression is a fundamental value of the Institute.
> I believe that, as an institution of higher learning, we must ensure that different points of view – even views that some or all of us may reject – are allowed to be heard and debated at MIT. Open dialogue is how we make each other wiser and smarter.
> This commitment to free expression can carry a human cost. The speech of those we strongly disagree with can anger us. It can disgust us. It can even make members of our own community feel unwelcome and illegitimate on our campus or in their field of study.
> I am convinced that, as an institution, we must be prepared to endure such painful outcomes as the price of protecting free expression – the principle is that important.
> Amazingly, we find that the GCC compilers is able to compile Travis’ is_empty C++ function to constant-time code.
It's actually an interesting example where undefined behavior allowed compiler optimization:
(1) dereferencing an invalid pointer is UD
(2) signed integer overflow is UD.
This allows the compiler to assume that the program never crashes and the counter never overflows. The loop is then optimized out knowing that it is read-only thus has no side-effects.
> The slide also shows that AT&T retains “cloud storage internet/web browsing” data for 1 year.
I never thought before that ISPs would really keep track of every user's browsing history, but apparently as cheap as the disks are today, this has become true. Can't think of any use of this data other than for mass surveillance.
Not sure what you mean by 'emulator', but I will assume you meant ISA for a different hardware architecture.
> The emulator would identify hot code blocks, generate C like constructs (or ideally the AST)
The idea behind copy-and-patch should be able to handle your use case of quickly translating code blocks in another ISA to native instructions.
However, I think Pochi's metaprogramming capabilities might not be too relevant here. After all, you are translating from a block of CPU instructions (in another ISA). It's probably not necessary or helpful to translate them back to C-like control flow only to compile them again.
> The ability to call back to host methods, handle exception semantics and all the while being totally oblivious to the platform
I'm not sure what you mean here. Yes Pochi supports intuitive inter-operation with the host program (call methods, handle exception etc). This is important for metaprogramming use case (e.g., generating a program that executes a SQL query), but I don't see what it has to do with emulating a program in another architecture.
Put the innocent and the defenseless at life risk for the enjoyment of the others.
Probably reasonable if R<1 since it's only going to affect a limited number of people, but this is absurd for countries where the R is still >1. This is effectively putting everyone at life risk.
> which do JIT "compilation" in the business-logic/abstract sense of the term?
If I understood what you said correctly, you mean translating something to SQL?
The problem this paper is trying to solve is how we can generate binary code fast. If your target (i.e. the stuff you want to generate in the end) is not binary code, but some high level representation like a SQL text, then I think it doesn't have much to do with the technique in our paper.
Java is static-typed already and only has a low degree of dynamism, so the importance of doing so is much less (only to reduce the initialization time I believe).
Interesting post. Seems similar to the approach of HPHPC for PHP, where the code is first run offline in a simulated environment to collect type information, and then use these speculations to generate static-typed code ahead-of-time.
Would be interested to see a performance comparison with V8 JIT (instead of V8 interpreter).
> "there’s only so much one can do without touching the data model"
You probably want to look at the other link in that PR, which demonstrated how well copy-and-patch can do for another dynamic language (Lua): [1]
Of course, whether or not CPython could eventually make it to that point (or even further) is a different story: they are under a way tighter constraint than just developing something for academia. But copy-and-patch can do a lot even for dynamic languages :)
[1] https://sillycross.github.io/2023/05/12/2023-05-12/