>"An optimizing compiler would constant-fold our expression into a single number ahead of time."
Could someone say what is meant by constant-folding here?
>"An optimizing compiler would constant-fold our expression into a single number ahead of time."
Could someone say what is meant by constant-folding here?
3 + 4
Would get "compiled" to: PUSH_INT 3
PUSH_INT 4
OP_PLUS
As you can imagine this was a virtual stack-machine. After a while I started folding these operations:* If there was a "push int"
* And another "push int"
* And a maths operation.
* Then replace.
So in my case I'd first generate the naive version, then have a separate "optimise" pass which would rewrite the program:
PUSH_INT 7
NOP
NOP
NOP
NOP
A later optimization pass would remove consecutive NOP operations. I made a brief blog-post here:https://blog.steve.fi/adventures_optimizing_a_bytecode_based...
There were a couple of bugs found along the way, but the actual implementation of this optimization pass was pretty simple:
https://github.com/skx/evalfilter/pull/66/files
The biggest issue I had to face was that "1 + 3 => 4" is trivial, but my virtual machine only supports loading integers. So I couldn't collapse "2 * 3.3" into the constant "6.6".
Later I do walk back over the bytecode and remove the nops. But I have to update all JMP/CALL instructions to cope with the changed destination offsets. Not a hard job in my case, as there are only a couple of instructions which refer to byte-offsets.
(If I allowed "LD A,[BC]", or similar permuations I'd have a lot more work to do.)
Ex: 4 + 4 would be condensed down to just 8 at compile time rather than writing the code to get 4 and 4 both into registers and add them at execution time.
i = 320 * 200 * 32;
Most compilers would not actually generate two multiply instructions and a store for this statement. Instead, they identify constructs such as these and substitute the computed values at compile time (in this case, 2,048,000)."