We are proud to introduce the first ahead-of-time compiler for EVM: moeingaot.
In the smartBCH’s whitepaper, we wrote: “EVM, which is adopted more commonly than other VMs like WebAssembly, is a de facto standard for smart contracts. However, EVM lacks certain important speedup methods, such as ahead-of-time (AOT) compilers and just-in-time (JIT) compilers. In the software industry, almost every important VM has its AOT and/or JIT compilers, for example, JVM, ART VM, Javascript V8, DartVM, WebAssembly, LuaJIT, and GraalVM. We believe that it is about time that EVM has its compiler. And implementing an AOT compiler for it would be reasonable, since, unlike Javascript and Lua, it has static semantics.”
To implement an ahead-of-time compiler, the quickest path is a bytecode-to-c converter. Lua2c converts Lua bytecode into C source, and wasm2c converts WebAssembly bytecode into C source. By compiling the C source we can get a native library or program whose function is equivalent to the VM’s bytecode. Since Lua and WebAsssembly already have sophisticated JIT solutions, Lua2c and wasm2c are no longer maintained now.
We believe that the first ahead-of-time compiler for EVM is better to follow the bytecode-to-c path and it would have a great value to the crypto world before a sophisticated EVM JIT compiler gets ready.
Moeingaot is based on a faster interpreter for EVM: evmone, which has already implemented a function for each EVM instruction. We implement it in golang. Firstly, it analyzes a smart contract’s bytecode to extract the JUMP/JUMPI instructions’ targets; then, it outputs a c++ source code containing function calls which one-to-one map to the instructions, together with labels and goto-statements for JUMPs. This source code and some other code from the evmone project, are compiled into a dynamically linked library. The smartBCH’s full-node client can load this library and call a function that does exactly the same thing as the smart contract.
Moeingaot also supports compiling multiple smart contracts into one dynamically linked library and uses an entrance function named query_executor to query the function corresponding to a given smart contract.
We are still working on meoingaot for more case coverage and better performance. Currently, only preliminary benchmark data can be shared. The following synthetic benchmark is measured:
pragma solidity 0.8.13;
contract babylon {
uint public result;
int public fibResult0;
int public fibResult1;
function sqrt(uint y) external {
uint z;
if (y > 3) {
z = y;
uint x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
result = z;
fibResult0 = fib(5);
fibResult1 = fib(100);
}
function fib(int n) public pure returns (int) {
int t1 = 0;
int t2 = 1;
for (int i = 1; i <= n; ++i) {
int nextTerm = t1 + t2;
t1 = t2;
t2 = nextTerm;
}
return t2;
}
}
With 255<<248
as its input, the sqrt
function needs 104.8us to run on the evmone interpreter and 38.4um to run as a dynamic library compiled by moeingaot. Moeingaot is 2.73X faster than the evmone interpreter. This is a very encouraging result.
We will continue to optimize moeingaot and integrate it into smartBCH’s full-node client in the next few months. Moeingaot will as well be inspirational to the EVM ecosystem.