Skip to content

GopherJ/evm_mlir

 
 

Repository files navigation

EVM MLIR

Telegram Chat rust license

An EVM-bytecode to machine-bytecode compiler using MLIR and LLVM.

Progress

Implemented opcodes (click to open)
  1. (0x00) STOP
  2. (0x01) ADD
  3. (0x02) MUL
  4. (0x03) SUB
  5. (0x04) DIV
  6. (0x05) SDIV
  7. (0x06) MOD
  8. (0x07) SMOD
  9. (0x08) ADDMOD
  10. (0x09) MULMOD
  11. (0x0A) EXP
  12. (0x0B) SIGNEXTEND
  13. (0x10) LT
  14. (0x11) GT
  15. (0x12) SLT
  16. (0x13) SGT
  17. (0x14) EQ
  18. (0x15) ISZERO
  19. (0x16) AND
  20. (0x17) OR
  21. (0x18) XOR
  22. (0x1A) BYTE
  23. (0x1B) SHL
  24. (0x1C) SHR
  25. (0x1D) SAR
  26. (0x35) CALLDATALOAD
  27. (0x36) CALLDATASIZE
  28. (0x38) CODESIZE
  29. (0x50) POP
  30. (0x51) MLOAD
  31. (0x52) MSTORE
  32. (0x53) MSTORE8
  33. (0x56) JUMP
  34. (0x57) JUMPI
  35. (0x58) PC
  36. (0x59) MSIZE
  37. (0x5A) GAS
  38. (0x5B) JUMPDEST
  39. (0x5E) MCOPY
  40. (0x5F) PUSH0
  41. (0x60) PUSH1
  42. (0x61) PUSH2
  43. (0x62) PUSH3
  44. (0x63) PUSH4
  45. (0x64) PUSH5
  46. (0x65) PUSH6
  47. (0x66) PUSH7
  48. (0x67) PUSH8
  49. (0x68) PUSH9
  50. (0x69) PUSH10
  51. (0x6A) PUSH11
  52. (0x6B) PUSH12
  53. (0x6C) PUSH13
  54. (0x6D) PUSH14
  55. (0x6E) PUSH15
  56. (0x6F) PUSH16
  57. (0x70) PUSH17
  58. (0x71) PUSH18
  59. (0x72) PUSH19
  60. (0x73) PUSH20
  61. (0x74) PUSH21
  62. (0x75) PUSH22
  63. (0x76) PUSH23
  64. (0x77) PUSH24
  65. (0x78) PUSH25
  66. (0x79) PUSH26
  67. (0x7A) PUSH27
  68. (0x7B) PUSH28
  69. (0x7C) PUSH29
  70. (0x7D) PUSH30
  71. (0x7E) PUSH31
  72. (0x7F) PUSH32
  73. (0x80) DUP1
  74. (0x81) DUP2
  75. (0x82) DUP3
  76. (0x83) DUP4
  77. (0x84) DUP5
  78. (0x85) DUP6
  79. (0x86) DUP7
  80. (0x87) DUP8
  81. (0x88) DUP9
  82. (0x89) DUP10
  83. (0x8A) DUP11
  84. (0x8B) DUP12
  85. (0x8C) DUP13
  86. (0x8D) DUP14
  87. (0x8E) DUP15
  88. (0x8F) DUP16
  89. (0x90) SWAP1
  90. (0x91) SWAP2
  91. (0x92) SWAP3
  92. (0x93) SWAP4
  93. (0x94) SWAP5
  94. (0x95) SWAP6
  95. (0x96) SWAP7
  96. (0x97) SWAP8
  97. (0x98) SWAP9
  98. (0x99) SWAP10
  99. (0x9A) SWAP11
  100. (0x9B) SWAP12
  101. (0x9C) SWAP13
  102. (0x9D) SWAP14
  103. (0x9E) SWAP15
  104. (0x9F) SWAP16
  105. (0xA0) LOG0
  106. (0xA1) LOG1
  107. (0xA2) LOG2
  108. (0xA3) LOG3
  109. (0xA4) LOG4
  110. (0xF3) RETURN
  111. (0xFD) REVERT
Not yet implemented opcodes (click to open)
  1. (0x19) NOT
  2. (0x20) KECCAK256
  3. (0x30) ADDRESS
  4. (0x31) BALANCE
  5. (0x32) ORIGIN
  6. (0x33) CALLER
  7. (0x34) CALLVALUE
  8. (0x37) CALLDATACOPY
  9. (0x39) CODECOPY
  10. (0x3A) GASPRICE
  11. (0x3B) EXTCODESIZE
  12. (0x3C) EXTCODECOPY
  13. (0x3D) RETURNDATASIZE
  14. (0x3E) RETURNDATACOPY
  15. (0x3F) EXTCODEHASH
  16. (0x40) BLOCKHASH
  17. (0x41) COINBASE
  18. (0x42) TIMESTAMP
  19. (0x43) NUMBER
  20. (0x44) DIFFICULTY
  21. (0x45) GASLIMIT
  22. (0x46) CHAINID
  23. (0x47) SELFBALANCE
  24. (0x48) BASEFEE
  25. (0x49) BLOBHASH
  26. (0x4A) BLOBBASEFEE
  27. (0x54) SLOAD
  28. (0x55) SSTORE
  29. (0x5C) TLOAD
  30. (0x5D) TSTORE
  31. (0xF0) CREATE
  32. (0xF1) CALL
  33. (0xF2) CALLCODE
  34. (0xF4) DELEGATECALL
  35. (0xF5) CREATE2
  36. (0xFA) STATICCALL
  37. (0xFE) INVALID
  38. (0xFF) SELFDESTRUCT

Getting Started

Dependencies

  • Linux or macOS (aarch64 included) only for now
  • LLVM 18 with MLIR: On debian you can use apt.llvm.org, on macOS you can use brew
  • Rust
  • Git

Setup

This step applies to all operating systems.

Run the following make target to install the dependencies (both Linux and macOS):

make deps

Linux

Since Linux distributions change widely, you need to install LLVM 18 via your package manager, compile it or check if the current release has a Linux binary.

If you are on Debian/Ubuntu, check out the repository https://apt.llvm.org/ Then you can install with:

sudo apt-get install llvm-18 llvm-18-dev llvm-18-runtime clang-18 clang-tools-18 lld-18 libpolly-18-dev libmlir-18-dev mlir-18-tools

If you decide to build from source, here are some indications:

Install LLVM from source instructions
# Go to https://github.com/llvm/llvm-project/releases
# Download the latest LLVM 18 release:
# The blob to download is called llvm-project-18.x.x.src.tar.xz

# For example
wget https://github.com/llvm/llvm-project/releases/download/llvmorg-18.1.4/llvm-project-18.1.4.src.tar.xz
tar xf llvm-project-18.1.4.src.tar.xz

cd llvm-project-18.1.4.src.tar
mkdir build
cd build

# The following cmake command configures the build to be installed to /opt/llvm-18
cmake -G Ninja ../llvm \
   -DLLVM_ENABLE_PROJECTS="mlir;clang;clang-tools-extra;lld;polly" \
   -DLLVM_BUILD_EXAMPLES=OFF \
   -DLLVM_TARGETS_TO_BUILD="Native" \
   -DCMAKE_INSTALL_PREFIX=/opt/llvm-18 \
   -DCMAKE_BUILD_TYPE=RelWithDebInfo \
   -DLLVM_PARALLEL_LINK_JOBS=4 \
   -DLLVM_ENABLE_BINDINGS=OFF \
   -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DLLVM_ENABLE_LLD=ON \
   -DLLVM_ENABLE_ASSERTIONS=OFF

ninja install

Setup a environment variable called MLIR_SYS_180_PREFIX, LLVM_SYS_180_PREFIX and TABLEGEN_180_PREFIX pointing to the llvm directory:

# For Debian/Ubuntu using the repository, the path will be /usr/lib/llvm-18
export MLIR_SYS_180_PREFIX=/usr/lib/llvm-18
export LLVM_SYS_180_PREFIX=/usr/lib/llvm-18
export TABLEGEN_180_PREFIX=/usr/lib/llvm-18

Run the deps target to install the other dependencies.

make deps

MacOS

The makefile deps target (which you should have ran before) installs LLVM 18 with brew for you, afterwards you need to execute the env-macos.sh script to setup the environment.

source scripts/env-macos.sh

Running

To run the compiler, call cargo run while passing it a file with the EVM bytecode to compile. There are some example files under programs/, for example:

cargo run programs/push32.bytecode

Debugging the compiler

Compile a program

To generate the necessary artifacts, you need to run cargo run <filepath>, with <filepath> being the path to a file containing the EVM bytecode to compile.

Writing EVM bytecode directly can be a bit difficult, so you can edit src/main.rs, modifying the program variable with the structure of your EVM program. After that you just run cargo run.

An example edit would look like this:

fn main() {
    let program = vec![
            Operation::Push0,
            Operation::PushN(BigUint::from(42_u8)),
            Operation::Add,
        ];
    // ...
}

Inspecting the artifacts

The most useful ones to inspect are the MLIR-IR (<name>.mlir) and Assembly (<name>.asm) files. The first one has a one-to-one mapping with the operations added in the compiler, while the second one contains the instructions that are executed by your machine.

The other generated artifacts are:

  • Semi-optimized MLIR-IR (<name>.after-pass.mlir)
  • LLVM-IR (<name>.ll)
  • Object file (<name>.o)
  • Executable (<name>)

Running with a debugger

Once we have the executable, we can run it with a debugger (here we use lldb, but you can use others). To run with lldb, use lldb <name>.

To run until we reach our main function, we can use:

br set -n main
run

Running a single step

thread step-inst

Reading registers

All registers: register read

The x0 register: register read x0

Reading memory

To inspect the memory at <address>: memory read <address>

To inspect the memory at the address given by the register x0: memory read $x0

Reading the EVM stack

To pretty-print the EVM stack at address X: memory read -s32 -fu -c4 X

Reference:

  • The -s32 flag groups the bytes in 32-byte chunks.
  • The -fu flag interprets the chunks as unsigned integers.
  • The -c4 flag includes 4 chunks: the one at the given address plus the three next chunks.

Restarting the program

To restart the program, just use run again.

About

An EVM written with MLIR

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Rust 99.3%
  • Other 0.7%