ControlCplusControlV / Scribe

Minimal Yul Transpilation to the Miden VM
GNU General Public License v3.0
51 stars 4 forks source link

Some optimization experimentation #2

Closed marcusbuffett closed 2 years ago

marcusbuffett commented 2 years ago

Added a couple optimizations to our miden generation, enabled by a couple new concepts.

Stack management

The biggest optimization is around stack management. The transpiler will keep track of which slots in the stack are used for which variables, and dup them to the top as needed, instead of using mem. The catch here, is that after conditionals and after each iteration of a for loop, you have to "reset" the stack to have the same order of variables as before the conditional / for loop. For now, it just iterates through the old stack order in reverse and pushes the new variable values to the top in that order, thereby reproducing the same order as before. This can result in quite a few dup calls, I'm sure we could optimize this further, with some sort of diffing algorithm.

So now we should be able to handle up to 16 locals. When we hit 12 items in the stack, we should push the top variable to memory, but I haven't done this yet. I don't see it being an issue though, should be straightforward to add.

Const-ing variables, by walking the AST

There are a class of optimizations that are enabled by us having an AST generated between parsing and transpilation. The simplest is just looking at variables that are only assigned once, to a literal, and removing the declaration, then replacing all references to it with the value it was initialized to. Ex. for this yul code:

let x := 42
add(x, 2)

The AST pre-optimization is:

AST
├╼ declare - x
│ └╼ 42
└╼ add()
  ├╼ var - x
  └╼ 2

The AST post-optimization is just:

AST
└╼ add()
  ├╼ 42
  └╼ 2

I don't think this is going to be a useful optimization, overall, because generated yul code probably already consts things whenever possible, but we now have a pattern and a place to add more optimizations that require looking at, and possibly modifying, the AST before generation: https://github.com/ControlCplusControlV/Scribe/blob/5d942bf527ec1ab93e1e96285f1e18869ad31ad5/transpiler/src/miden_generator.rs#L233-L249

In the code above we have one visitor that will just count how many times a variable gets assigned, and if it's assigned to a literal, keeps track of the last one. We then use a separate visitor to modify the AST, with the const-able variables found by the first visitor.

Results - Fibonacci

The fibonacci example is down from 70 instructions to 26:

begin
    push.0
    push.1
    push.0
    push.0
    dup.0
    push.10
    lt
    while.true
        dup.3
        dup.3
        add
        dup.3
        dup.1
        dup.3
        push.1
        add
        dup.2
        dup.2
        dup.5
        dup.3
        dup.0
        push.10
        lt
    end
    dup.2
end
ControlCplusControlV commented 2 years ago

Code overall looks good and logic checks out, but went to test it and ran into this error, any ideas?

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" }', src/main.rs:73:10
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace