Mining JIT traces for missing optimizations with Z3
pypy.org
pypy.org
I ask because z3 has been used for type inference (Typpete) and for solving equations written in Python.
The CPython JIT is a newer and less invasive technique called copy-and-patch[1]. It's a lot less powerful, but a lot easier to plug into an existing language implementation: known sequences of python bytecode are mapped to templates of machine code
I understand people at IBM were doing it industrially for Java bytecode in the late 90s under the name quasi-static compilation, and the DyC/Tempo folks were doing similar things over in C land under different names. There were some minor differences due to the technology of the day, but it was broadly similar. For example, explicitly building to IR was uncommon outside Java land and register scheduling didn't have a lot of choices to make. The Java stuff even allowed the template specializations to exist on other computers and be dynamically loaded and validated over the network, for thin-client reasons. Very cool for the early 2000s.
> (x & c1) | (x & c1) == x & (c1 | c2)
Is this a typo? Shoud the second c1 be c2 instead?
Run the test suite, identify optimizations. One by one, make the the optimization change to the implementation as suggested by the LLM.
Instrument the changed methods on the second test run and see if runtime performance has changed. Verify that the test still passes.
You’d be extracting optimization candidates by running the test suite.
You re-run the test suite after changes to ensure they still pass.
But the point is that as long as optimization rules are hand-written, a human has thought about them and convinced themselves (maybe incorrectly) that the rules are correct. If a machine generates them without a human in the loop, some other sort of correctness argument is needed. Hence the reasonable suggestion that they should be formally verified.
We also run a fuzzer regularly to find optimization bugs, using Z3 as a correctness check:
https://pypy.org/posts/2022/12/jit-bug-finding-smt-fuzzing.h...
The peephole optimizations aren't themselves formally verified completely yet. We've verified the very simplest rules, and some of the newer complicated ones, but not systematically all of them. I plan to work on fully and automatically verifying all integer optimizations in the next year or so. But we'll see, I'll need to find students and/or money.
Generate the z3 too - as the need is to verify, not test. It can be a direct translation. For all inputs, is the optimization output equivalent. (Bootstrapping a compiler prototype via LLMs is nice though.)
One place LLMs get fun here is where the direct translation to z3 times out, such as bigger or more complicated programs, and so the LLM can provide intuition for pushing the solver ahead.
Known as boundary value testing, you partition all input into equivalence classes, then make sure your tests contain a sample from each class.
And even ignoring that problem, there may be an infinite amount of equivalence classes when you introduce loops/recursion, as the loops can run a different amount of times and thus lead to different executions.
Even just considering `if` statements, the amount of equivalence classes can be exponential in the amount of `if` (for example consider a series of `if` where each check a different bit of the input; ultimately you'll need any combination of bits to check every combination of `if`, and the number is 2^number of ifs).
For example (fixnums are small integer), is it valid to replace
(if (fixnum? x)
(fixnum? (abs x))
true)
with just the constant true
?Try runing a few tests, common unit test and even random test. Did you spot the corner case?
It fails only when x is the most negative fixnum, that is also a very rare case in a real program. (IIRC, the random test suit try to use more of this kind of problematic values.)
(llm's aren't involved, it's all based on z3)
I don't plan on implementing something like this for now, I'd rather take the inefficiencies and manually extract optimizations out of them and implement them in PyPy's jit.
AlphaZero-type systems were unable to significantly improve over near-optimal solvers such as z3.