-
Notifications
You must be signed in to change notification settings - Fork 66
RFC: The path to Futamura 🗻 #784
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Oh wow, this is really cool! I am very busy with writing a presentation for a 4h seminar I will be giving on smart contract verification next week, but I will be back full speed on this and other PRs in about 1-1.5 weeks! This actually looks like a great place to start :) Thanks for this, I really needed this initial kick-off, I think I will be able to do some things from here on! |
Very exciting! I'm curious if you had considered at all generating llvm directly instead of haskell? |
Current state of this PR:
Missing components:
Demo of the code produced:
The code produced contains this one: runBasicBlock_0_4 :: StateT (VM Concrete s) (ST s) ()
runBasicBlock_0_4 = do
get >>= \vm ->
case vm.result of
Nothing -> let ?op = 1 in runOpcodePush0
Just r -> return ()
get >>= \vm ->
case vm.result of
Nothing -> let ?op = 1 in runOpcodePush0
Just r -> return ()
get >>= \vm ->
case vm.result of
Nothing -> let ?op = 1 in runOpcodeSwap (1 :: Int)
Just r -> return ()
get >>= \vm ->
case vm.result of
Nothing -> let ?op = 1 in runOpcodeStop
Just r -> return ()
runBasicBlock_4_5 :: StateT (VM Concrete s) (ST s) ()
runBasicBlock_4_5 = do
get >>= \vm ->
case vm.result of
Nothing -> let ?op = 1 in runOpcodeStop
Just r -> return ()
runBasicBlock_5_7 :: StateT (VM Concrete s) (ST s) ()
runBasicBlock_5_7 = do
get >>= \vm ->
case vm.result of
Nothing -> let ?op = 1 in runOpcodePush0
Just r -> return ()
get >>= \vm ->
case vm.result of
Nothing -> let ?op = 1 in runOpcodeRevert
Just r -> return () |
I finally implemented enough opcodes to be able to benchmark a simple loop, with some operations and different revert conditions. The contract I'm using for benchmark looks like this: contract C {
fallback() payable external {
uint256 z = 1;
for (int i = 0; i < (10 ** 6) * 8; i++) {
z = 2 * z + 1;
z = z + 1;
z = z / 5;
z = z + 1;
z = z * 6;
z = (z * 5) / 10;
}
if ((z % 1) == 0)
revert("one");
if ((z % 2) == 0)
revert("two");
if ((z % 3) == 0)
revert("three");
if ((z % 4) == 0)
revert("four");
}
} Currently, the basic blocks are stored in a map, and then called when need it, using the
runBasicBlock_0_13 :: StateT (VM Concrete s) (ST s) ()
runBasicBlock_0_13 = do
runOpcodePush 1
...
runOpcodeJumpi
if cond /= 0
then runBasicBlock_25_48
else runBasicBlock_48_49 |
I need to discuss with the rest of the team regarding this prototype, but my impression on what needs to be done to unlock full speed here:
|
Description
This PR was created just to receive comments regarding a potential usage of the first Futamura projection.
Right now, it has only a couple of opcodes implemented. Implementing opcode in this context means taking the code from
src/EVM.hs
and copy them into a new function with essentially now changes at all, so it is a fast process, compared to the actual implementation from scratch.In order to do some quick testing, I added a new flag in the
hevm exec
called--use-futamura
that will do the concrete executing with the corresponding Futamura projection. A small demo:This example shows the execution of an add instruction, using two zeros as arguments. Finally, the program stops. There is an additional revert opcode, but it has no effect (as intended). We can peek the generated code:
The use of of the
op
implicit parameter needs to be fixed but I hardcoded it for a quick test. Despite the code is not as efficient as it should be, the execution looks good. Is it faster than concrete execution as we have it today? It is hard to say at this point, we need to benchmark to see this.There is a non-trivial amount of work as it needs a few important pieces more:
run
(EVM/Exec.hs) which fetches the current futamura projection function and runs it. It should find the next basic block and repeat until the transaction is over.The final implementation will require a large refactoring of the opcode implementation (but it should not be too hard, since all the code is there).