├── .gitattributes ├── .github ├── CODEOWNERS ├── scripts │ ├── install_iai_callgrind_runner.sh │ └── install_llvm_ubuntu.sh └── workflows │ ├── bench.yml │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── cliff.toml ├── clippy.toml ├── crates ├── revmc-backend │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── lib.rs │ │ ├── pointer.rs │ │ └── traits.rs ├── revmc-build │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── lib.rs ├── revmc-builtins │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── gas.rs │ │ ├── ir.rs │ │ ├── lib.rs │ │ ├── macros.rs │ │ └── utils.rs ├── revmc-cli-tests │ ├── CHANGELOG.md │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── revmc-cli │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── benches │ │ ├── bench.rs │ │ ├── iai.rs │ │ └── iai │ │ │ └── prev.rs │ ├── build.rs │ ├── src │ │ ├── benches.rs │ │ ├── lib.rs │ │ └── main.rs │ └── tests.rs ├── revmc-context │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── lib.rs ├── revmc-cranelift │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── lib.rs │ │ └── pretty_clif.rs ├── revmc-llvm │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── dh.rs │ │ ├── lib.rs │ │ ├── orc.rs │ │ └── utils.rs └── revmc │ ├── CHANGELOG.md │ ├── Cargo.toml │ ├── README.md │ └── src │ ├── bytecode │ ├── info.rs │ ├── mod.rs │ ├── opcode.rs │ └── sections.rs │ ├── compiler │ ├── mod.rs │ └── translate.rs │ ├── lib.rs │ ├── linker.rs │ └── tests │ ├── fibonacci.rs │ ├── macros.rs │ ├── meta.rs │ ├── mod.rs │ ├── resume.rs │ └── runner.rs ├── data ├── airdrop.rt.hex ├── bswap64.rt.hex ├── bswap64_opt.rt.hex ├── counter-eof.rt.hex ├── counter.rt.hex ├── erc20_transfer.rt.hex ├── fiat_token.rt.hex ├── hash_10k-eof.rt.hex ├── hash_10k.rt.hex ├── push0_proxy.rt.hex ├── seaport.rt.hex ├── snailtracer-eof.rt.hex ├── snailtracer.rt.hex ├── uniswap_v2_pair.rt.hex ├── univ2_router.rt.hex ├── usdc_proxy.rt.hex └── weth.rt.hex ├── examples ├── compiler │ ├── Cargo.toml │ ├── build.rs │ └── src │ │ └── main.rs └── runner │ ├── Cargo.toml │ ├── build.rs │ └── src │ ├── common.rs │ ├── lib.rs │ └── main.rs ├── fuzz ├── .gitignore ├── Cargo.toml └── fuzz_targets │ └── vs_interpreter.rs ├── release.toml ├── rustfmt.toml ├── scripts └── changelog.sh └── tests └── codegen ├── address_mask.evm └── non_zero.evm /.gitattributes: -------------------------------------------------------------------------------- 1 | *.evm linguist-language=Assembly 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @DaniPopes 2 | -------------------------------------------------------------------------------- /.github/scripts/install_iai_callgrind_runner.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | echo "::group::Install" 5 | version=$(cargo metadata --format-version=1 |\ 6 | jq '.packages[] | select(.name == "iai-callgrind").version' |\ 7 | tr -d '"' 8 | ) 9 | cargo binstall iai-callgrind-runner --version "$version" --no-confirm --no-symlinks --force 10 | echo "::endgroup::" 11 | echo "::group::Verification" 12 | which iai-callgrind-runner 13 | echo "::endgroup::" 14 | -------------------------------------------------------------------------------- /.github/scripts/install_llvm_ubuntu.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | v=${1:-$LLVM_VERSION} 5 | bins=(clang llvm-config lld ld.lld FileCheck) 6 | llvm_sh=$(mktemp) 7 | 8 | wget https://apt.llvm.org/llvm.sh -O "$llvm_sh" 9 | chmod +x "$llvm_sh" 10 | "$llvm_sh" "$v" all 11 | for bin in "${bins[@]}"; do 12 | ln -fs "$(which "$bin-$v")" "/usr/bin/$bin" 13 | done 14 | -------------------------------------------------------------------------------- /.github/workflows/bench.yml: -------------------------------------------------------------------------------- 1 | name: Benchmark 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | workflow_dispatch: 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | LLVM_VERSION: "18" # Must be just the major version 12 | 13 | concurrency: 14 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | iai: 19 | runs-on: ubuntu-latest 20 | env: 21 | BASELINE: base 22 | IAI_CALLGRIND_RUNNER: iai-callgrind-runner 23 | steps: 24 | - uses: actions/checkout@v4 25 | - name: Install LLVM 26 | run: sudo .github/scripts/install_llvm_ubuntu.sh ${{ env.LLVM_VERSION }} 27 | - name: llvm-config 28 | run: llvm-config --version --bindir --libdir 29 | - name: Install Valgrind 30 | run: sudo apt update && sudo apt install valgrind 31 | - uses: dtolnay/rust-toolchain@stable 32 | - name: Install cargo-binstall 33 | uses: taiki-e/install-action@cargo-binstall 34 | - name: Checkout base 35 | uses: actions/checkout@v4 36 | with: 37 | ref: ${{ github.base_ref || 'main' }} 38 | fetch-depth: 2 39 | - name: Checkout HEAD^ 40 | if: ${{ !github.base_ref }} 41 | run: git checkout HEAD^ 42 | - uses: Swatinem/rust-cache@v2 43 | with: 44 | cache-on-failure: true 45 | - name: Install iai-callgrind-runner 46 | run: ./.github/scripts/install_iai_callgrind_runner.sh 47 | - name: Save baseline 48 | run: cargo bench -p revmc-cli --bench iai -- --save-baseline=$BASELINE 49 | - name: Checkout PR 50 | uses: actions/checkout@v4 51 | with: 52 | clean: false 53 | - name: Install iai-callgrind-runner 54 | run: ./.github/scripts/install_iai_callgrind_runner.sh 55 | - name: Compare PR benchmarks 56 | run: cargo bench -p revmc-cli --bench iai -- --baseline=$BASELINE 57 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | LLVM_VERSION: "18" # Must be just the major version 12 | ALL_BACKENDS: "llvm,cranelift" 13 | 14 | concurrency: 15 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 16 | cancel-in-progress: true 17 | 18 | jobs: 19 | test: 20 | name: test +${{ matrix.runner }}-${{ matrix.arch }}-${{ matrix.toolchain }} ${{ matrix.profile }} 21 | runs-on: ${{ matrix.runner }} 22 | timeout-minutes: 30 23 | strategy: 24 | fail-fast: false 25 | matrix: 26 | runner: ["ubuntu-latest", "macos-14"] 27 | arch: ["x64", "arm64"] 28 | toolchain: ["stable", "nightly"] 29 | profile: ["dev", "release"] 30 | exclude: 31 | - runner: "ubuntu-latest" 32 | arch: "arm64" 33 | - runner: "macos-14" 34 | arch: "x64" 35 | steps: 36 | - uses: actions/checkout@v4 37 | - name: Install LLVM (apt) 38 | if: ${{ !contains(matrix.runner, 'macos') }} 39 | run: sudo .github/scripts/install_llvm_ubuntu.sh ${{ env.LLVM_VERSION }} 40 | - name: Install LLVM (brew) 41 | if: ${{ contains(matrix.runner, 'macos') }} 42 | run: | 43 | v=${{ env.LLVM_VERSION }} 44 | brew install "llvm@${v}" 45 | echo "LLVM_SYS_${v}0_PREFIX=$(brew --prefix llvm@${v})" >> $GITHUB_ENV 46 | echo "/opt/homebrew/opt/llvm@${v}/bin" >> $GITHUB_PATH 47 | - name: llvm-config 48 | run: llvm-config --version --bindir --libdir 49 | - uses: dtolnay/rust-toolchain@master 50 | with: 51 | toolchain: ${{ matrix.toolchain }} 52 | - uses: Swatinem/rust-cache@v2 53 | with: 54 | cache-on-failure: true 55 | - uses: taiki-e/install-action@nextest 56 | - name: test 57 | run: cargo nextest run --workspace --cargo-profile ${{ matrix.profile }} --features ${{ env.ALL_BACKENDS }} 58 | 59 | feature-checks: 60 | runs-on: ubuntu-latest 61 | timeout-minutes: 30 62 | steps: 63 | - uses: actions/checkout@v4 64 | - name: Install LLVM 65 | run: sudo .github/scripts/install_llvm_ubuntu.sh ${{ env.LLVM_VERSION }} 66 | - uses: dtolnay/rust-toolchain@stable 67 | - uses: taiki-e/install-action@cargo-hack 68 | - uses: Swatinem/rust-cache@v2 69 | with: 70 | cache-on-failure: true 71 | - name: cargo hack 72 | run: | 73 | cargo hack check --feature-powerset --depth 2 --workspace \ 74 | --skip llvm-prefer-static --skip prefer-static 75 | 76 | clippy: 77 | runs-on: ubuntu-latest 78 | timeout-minutes: 30 79 | steps: 80 | - uses: actions/checkout@v4 81 | - name: Install LLVM 82 | run: sudo .github/scripts/install_llvm_ubuntu.sh ${{ env.LLVM_VERSION }} 83 | - uses: dtolnay/rust-toolchain@clippy 84 | - uses: Swatinem/rust-cache@v2 85 | with: 86 | cache-on-failure: true 87 | - run: cargo clippy --workspace --all-targets --features ${{ env.ALL_BACKENDS }} 88 | env: 89 | RUSTFLAGS: -Dwarnings 90 | 91 | docs: 92 | runs-on: ubuntu-latest 93 | timeout-minutes: 30 94 | permissions: 95 | contents: write 96 | steps: 97 | - uses: actions/checkout@v4 98 | - name: Install LLVM 99 | run: sudo .github/scripts/install_llvm_ubuntu.sh ${{ env.LLVM_VERSION }} 100 | - uses: dtolnay/rust-toolchain@nightly 101 | - uses: Swatinem/rust-cache@v2 102 | with: 103 | cache-on-failure: true 104 | - run: cargo doc --workspace --features ${{ env.ALL_BACKENDS }} --no-deps --document-private-items 105 | env: 106 | RUSTDOCFLAGS: --cfg docsrs -D warnings --show-type-layout --generate-link-to-definition --enable-index-page -Zunstable-options 107 | - name: Deploy documentation 108 | uses: peaceiris/actions-gh-pages@v4 109 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' 110 | with: 111 | github_token: ${{ secrets.GITHUB_TOKEN }} 112 | publish_dir: target/doc 113 | force_orphan: true 114 | 115 | fmt: 116 | runs-on: ubuntu-latest 117 | timeout-minutes: 30 118 | steps: 119 | - uses: actions/checkout@v4 120 | - uses: dtolnay/rust-toolchain@nightly 121 | with: 122 | components: rustfmt 123 | - run: cargo fmt --all --check 124 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | clif 3 | .vscode 4 | .idea 5 | /tmp 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.1.0](https://github.com/paradigmxyz/revmc/releases/tag/v0.1.0) - 2024-06-27 9 | 10 | ### Bug Fixes 11 | 12 | - Use `effective_gas_price` in GASPRICE ([#32](https://github.com/paradigmxyz/revmc/issues/32)) 13 | - Dereferenceable attribute on builtins ([#25](https://github.com/paradigmxyz/revmc/issues/25)) 14 | - Memory leak in LLVM diagnostic handler 15 | - Manually delete LLVM IR to reduce memory consumption 16 | - End a section at SSTORE too (EIP-1706) ([#21](https://github.com/paradigmxyz/revmc/issues/21)) 17 | - Build script for macos ([#20](https://github.com/paradigmxyz/revmc/issues/20)) 18 | - Linker args 19 | - Stack overflows 20 | - Make comments work on instrs ([#7](https://github.com/paradigmxyz/revmc/issues/7)) 21 | - Correct linker flag 22 | - Pass -zundefs to linker 23 | - Only allow setting compilation mode at instantiation 24 | - Features 2 25 | - Features 26 | - Stack io for suspending instructions 27 | - Fix some TODOs 28 | - Ci 29 | - Add rust-src component to rust-toolchain.toml 30 | 31 | ### Dependencies 32 | 33 | - Bump revm 34 | - [deps] Bump all 35 | - Bump revm to 9 36 | - [deps] Cargo update 37 | - Bump to revm main 38 | - Bump revm, fix gas overflows 39 | - Bump 40 | - Bump 41 | - Bump revm patch 42 | 43 | ### Documentation 44 | 45 | - Add changelogs 46 | - Add requirements and examples ([#29](https://github.com/paradigmxyz/revmc/issues/29)) 47 | - Wording 48 | 49 | ### Features 50 | 51 | - Add target configuration ([#39](https://github.com/paradigmxyz/revmc/issues/39)) 52 | - Improve DX around statically linked bytecodes, add example ([#37](https://github.com/paradigmxyz/revmc/issues/37)) 53 | - Implement IR builtins ([#36](https://github.com/paradigmxyz/revmc/issues/36)) 54 | - Make builtins no_std ([#27](https://github.com/paradigmxyz/revmc/issues/27)) 55 | - Add llvm-prefer-* features ([#24](https://github.com/paradigmxyz/revmc/issues/24)) 56 | - More LLVM overrides ([#23](https://github.com/paradigmxyz/revmc/issues/23)) 57 | - Add another benchmark ([#22](https://github.com/paradigmxyz/revmc/issues/22)) 58 | - Allow running after aot 59 | - [llvm] Use intel syntax for x86 assembly, set diagnostic handler 60 | - Build script support, fix revm update fallout 61 | - Allow disabling stack length overflow checks 62 | - Add linker helper 63 | - Also build builtins as a dynamic library 64 | - Implement AOT compilation 65 | - New backend API ([#3](https://github.com/paradigmxyz/revmc/issues/3)) 66 | - Pass most statetest tests 67 | - Implement CALL instructions 68 | - Pre-built callbacks bitcode module 69 | - Consolidate stack IO calculations, fix some instructions 70 | - Add LLVM ORC wrappers 71 | - Unify returns, implement suspend/resume for CALL/CREATE 72 | - Implement dynamic jumps 73 | - Finish core instruction implementations and tests (2/2) 74 | - Finish core instruction implementations and tests (1) 75 | - Implement most instructions (untested) 76 | - Implement some env-fetching instructions 77 | - Don't allocate tmp in SWAP 78 | - Move attributes to the builder trait 79 | - Implement GAS, add some more tests 80 | - Ignore unreachable `JUMPDEST`s in DCE 81 | - Basic DCE, restructure bytecode module 82 | - Implement SIGNEXTEND, refactor test suite 83 | - Implement opcode info ourselves 84 | - Implement callbacks 85 | - Criterion bench 86 | - Debug_assertions/panics, function call hooks, gas in tests, benchmarks 87 | - Implement basic gas accounting 88 | - Test fibonacci 89 | - Codegen jumps 90 | - Internal bytecode representation, add more docs 91 | - Implement cold blocks in llvm 92 | - More implementations, add tests 93 | - Core backend abstraction, add LLVM 94 | - Basic implementation 95 | 96 | ### Miscellaneous Tasks 97 | 98 | - Exclude examples from auto changelogs 99 | - Replace timing macros with tracy ([#40](https://github.com/paradigmxyz/revmc/issues/40)) 100 | - Add release configuration 101 | - Update some comments 102 | - Rebrand to `revmc` ([#33](https://github.com/paradigmxyz/revmc/issues/33)) 103 | - Patch revm to reth pin ([#31](https://github.com/paradigmxyz/revmc/issues/31)) 104 | - Add more benchmarks 105 | - Remove config file 106 | - Add a TODO 107 | - Make DIFFICULTY a builtin ([#26](https://github.com/paradigmxyz/revmc/issues/26)) 108 | - Add some todos 109 | - Comment 110 | - Adjustments 111 | - Update benchmarks 112 | - Update revm gas ([#17](https://github.com/paradigmxyz/revmc/issues/17)) 113 | - Cleanups 114 | - Add iai-callgrind benchmarks ([#14](https://github.com/paradigmxyz/revmc/issues/14)) 115 | - Fix some TODOs, cleanup ([#11](https://github.com/paradigmxyz/revmc/issues/11)) 116 | - [jit] Use return_imm builder method ([#9](https://github.com/paradigmxyz/revmc/issues/9)) 117 | - Remove one-array 118 | - Rewrite native fibonacci 119 | - Dedup code 120 | - Renames, remove unnecessary dll linking, clippy 121 | - Adjustments 122 | - Update to latest revm 123 | - Minor updates 124 | - Split compiler.rs 125 | - Update to latest revm main 126 | - Make utils private again 127 | - Rename callback to builtin 128 | - Move all callbacks to the crate 129 | - Remove precompiled bitcode 130 | - Improve call_with_interpreter 131 | - Update 132 | - Pass spec_id after stack pointer 133 | - Default stack config to true 134 | - Separate opcode (bytes) from instructions (higher level) 135 | - Core -> backend 136 | - Clippy 137 | 138 | ### Other 139 | 140 | - Update README.md 141 | - Add config file w/ top 250 contracts 142 | - Fix iai on push ([#18](https://github.com/paradigmxyz/revmc/issues/18)) 143 | - Run macos in CI ([#6](https://github.com/paradigmxyz/revmc/issues/6)) 144 | - Fix checks 145 | - Merge pull request [#2](https://github.com/paradigmxyz/revmc/issues/2) from DaniPopes/remove-precompiled-bitcode 146 | - Callbacks bitcode 147 | - Add callbacks crate 148 | - Merge pull request [#1](https://github.com/paradigmxyz/revmc/issues/1) from DaniPopes/fighting-ci 149 | - Print llvm-config 150 | - Download llvm 151 | - Restructure 152 | - Stuff 153 | - Initial commit 154 | 155 | ### Performance 156 | 157 | - Pay base gas of dynamic opcodes in sections ([#19](https://github.com/paradigmxyz/revmc/issues/19)) 158 | - Re-order gas check ([#15](https://github.com/paradigmxyz/revmc/issues/15)) 159 | - Fix resume switch branch weights 160 | - Set weight metadata on switches too ([#13](https://github.com/paradigmxyz/revmc/issues/13)) 161 | - Set branch weight metadata instead of cold blocks ([#12](https://github.com/paradigmxyz/revmc/issues/12)) 162 | - Add attributes to builtin functions' params ([#8](https://github.com/paradigmxyz/revmc/issues/8)) 163 | - Unify failure blocks ([#5](https://github.com/paradigmxyz/revmc/issues/5)) 164 | - Batch gas and stack length checks ([#4](https://github.com/paradigmxyz/revmc/issues/4)) 165 | 166 | ### Styling 167 | 168 | - Clippy, add more lints ([#34](https://github.com/paradigmxyz/revmc/issues/34)) 169 | - Fmt 170 | 171 | ### Testing 172 | 173 | - Ensure SELFDESTRUCT stops execution 174 | - Add codegen tests ([#10](https://github.com/paradigmxyz/revmc/issues/10)) 175 | - Test suspend-resume 176 | - Cross-reference with revm interpreter 177 | 178 | 179 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["crates/*", "examples/*", "fuzz"] 3 | default-members = ["crates/revmc-cli"] 4 | resolver = "2" 5 | 6 | [workspace.package] 7 | version = "0.1.0" 8 | authors = ["DaniPopes <57450786+DaniPopes@users.noreply.github.com>"] 9 | edition = "2021" 10 | rust-version = "1.79" 11 | license = "MIT OR Apache-2.0" 12 | categories = ["compilers", "cryptography::cryptocurrencies"] 13 | keywords = ["compile", "compiler", "jit", "evm", "ethereum"] 14 | homepage = "https://github.com/danipopes/cranelift-jit-evm" 15 | repository = "https://github.com/danipopes/cranelift-jit-evm" 16 | exclude = [".github/", "benches/", "fuzz/", "tests/"] 17 | 18 | [workspace.lints.clippy] 19 | dbg-macro = "warn" 20 | manual-string-new = "warn" 21 | uninlined-format-args = "warn" 22 | use-self = "warn" 23 | redundant-clone = "warn" 24 | 25 | [workspace.lints.rust] 26 | missing-debug-implementations = "warn" 27 | missing-docs = "warn" 28 | rust-2018-idioms = "warn" 29 | unreachable-pub = "warn" 30 | unused-must-use = "warn" 31 | redundant-lifetimes = "warn" 32 | unnameable-types = "warn" 33 | 34 | [workspace.lints.rustdoc] 35 | all = "warn" 36 | 37 | [workspace.dependencies] 38 | revmc = { version = "0.1.0", path = "crates/revmc", default-features = false } 39 | revmc-backend = { version = "0.1.0", path = "crates/revmc-backend", default-features = false } 40 | revmc-build = { version = "0.1.0", path = "crates/revmc-build", default-features = false } 41 | revmc-builtins = { version = "0.1.0", path = "crates/revmc-builtins", default-features = false } 42 | revmc-context = { version = "0.1.0", path = "crates/revmc-context", default-features = false } 43 | revmc-cranelift = { version = "0.1.0", path = "crates/revmc-cranelift", default-features = false } 44 | revmc-llvm = { version = "0.1.0", path = "crates/revmc-llvm", default-features = false } 45 | 46 | alloy-primitives = { version = "0.8", default-features = false } 47 | revm = { version = "19.0", default-features = false } 48 | revm-primitives = { version = "15.1", default-features = false } 49 | revm-interpreter = { version = "15.0", default-features = false } 50 | ruint = { version = "1.12", default-features = false } 51 | 52 | color-eyre = "0.6" 53 | eyre = "0.6" 54 | rustc-hash = "2.1" 55 | tracing = "0.1" 56 | tracing-subscriber = "0.3" 57 | tracing-tracy = "0.11" 58 | paste = "1.0" 59 | 60 | [profile.release] 61 | opt-level = 3 62 | lto = true 63 | debug = "line-tables-only" 64 | strip = true 65 | panic = "abort" 66 | codegen-units = 1 67 | 68 | # Use the `--profile profiling` flag to show symbols in release mode. 69 | # e.g. `cargo build --profile profiling` 70 | [profile.profiling] 71 | inherits = "release" 72 | debug = 2 73 | strip = false 74 | 75 | [profile.bench] 76 | inherits = "profiling" 77 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # revmc 2 | 3 | Experimental [JIT] and [AOT] compiler for the [Ethereum Virtual Machine][EVM]. 4 | 5 | The compiler implementation is abstracted over an intermediate representation backend. It performs very well, as demonstrated below from our criterion benchmarks, and exposes an intuitive API via Revm. 6 | 7 | ![image](https://github.com/paradigmxyz/revmc/assets/17802178/96adf64b-8513-469d-925d-4f8d902e4e0a) 8 | 9 | This repository hosts two backend implementations: 10 | - [LLVM] ([`revmc-llvm`]): main backend with full test coverage; 11 | - [Cranelift] ([`revmc-cranelift`]); currently not functional due to missing `i256` support in Cranelift. This will likely require a custom fork of Cranelift. 12 | 13 | [JIT]: https://en.wikipedia.org/wiki/Just-in-time_compilation 14 | [AOT]: https://en.wikipedia.org/wiki/Ahead-of-time_compilation 15 | [EVM]: https://ethereum.org/en/developers/docs/evm/ 16 | [LLVM]: https://llvm.org/ 17 | [`revmc-llvm`]: /crates/revmc-llvm 18 | [Cranelift]: https://cranelift.dev/ 19 | [`revmc-cranelift`]: /crates/revmc-cranelift 20 | 21 | ## Requirements 22 | 23 | - Latest stable Rust version 24 | 25 | ### LLVM backend 26 | 27 | - Linux or macOS, Windows is not supported 28 | - LLVM 18 29 | - On Debian-based Linux distros: see [apt.llvm.org](https://apt.llvm.org/) 30 | - On Arch-based Linux distros: `pacman -S llvm` 31 | - On macOS: `brew install llvm@18` 32 | - The following environment variables may be required: 33 | ```bash 34 | prefix=$(llvm-config --prefix) 35 | # or 36 | #prefix=$(llvm-config-18 --prefix) 37 | # on macOS: 38 | #prefix=$(brew --prefix llvm@18) 39 | export LLVM_SYS_180_PREFIX=$prefix 40 | ``` 41 | 42 | ## Usage 43 | 44 | The compiler is implemented as a library and can be used as such through the `revmc` crate. 45 | 46 | A minimal runtime is required to run AOT-compiled bytecodes. A default runtime implementation is 47 | provided through symbols exported in the `revmc-builtins` crate and must be exported in the final 48 | binary. This can be achieved with the following build script: 49 | ```rust,ignore 50 | fn main() { 51 | revmc_build::emit(); 52 | } 53 | ``` 54 | 55 | You can check out the [examples](/examples) directory for example usage. 56 | 57 | ## Credits 58 | 59 | The initial compiler implementation was inspired by [`paradigmxyz/jitevm`](https://github.com/paradigmxyz/jitevm). 60 | 61 | #### License 62 | 63 | 64 | Licensed under either of Apache License, Version 65 | 2.0 or MIT license at your option. 66 | 67 | 68 |
69 | 70 | 71 | Unless you explicitly state otherwise, any contribution intentionally submitted 72 | for inclusion in these crates by you, as defined in the Apache-2.0 license, 73 | shall be dual licensed as above, without any additional terms or conditions. 74 | 75 | -------------------------------------------------------------------------------- /cliff.toml: -------------------------------------------------------------------------------- 1 | # Configuration file for [`git-cliff`](https://github.com/orhun/git-cliff) 2 | # See https://git-cliff.org/docs/configuration 3 | 4 | [remote.github] 5 | owner = "paradigmxyz" 6 | repo = "revmc" 7 | 8 | [changelog] 9 | header = """ 10 | # Changelog 11 | 12 | All notable changes to this project will be documented in this file. 13 | 14 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 15 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n 16 | """ 17 | # https://keats.github.io/tera/docs/#introduction 18 | body = """\ 19 | {% set gh_link = "https://github.com/" ~ remote.github.owner ~ "/" ~ remote.github.repo %}\ 20 | {% if version %}\ 21 | ## [{{ version | trim_start_matches(pat="v") }}]({{ gh_link }}/releases/tag/v{{ version | trim_start_matches(pat="v") }}) - {{ timestamp | date(format="%Y-%m-%d") }} 22 | {% else %}\ 23 | ## [Unreleased]({{ gh_link }}/compare/{{ previous.version }}...HEAD) 24 | {% endif %}\ 25 | {% for group, commits in commits | group_by(attribute="group") %} 26 | ### {{ group | title }} 27 | {% for commit in commits %} 28 | - {% if commit.scope %}[{{ commit.scope }}] {% endif %}{{ commit.message | upper_first | split(pat="\\n") | first }}\ 29 | {% endfor %} 30 | {% endfor %}\n 31 | """ 32 | trim = true 33 | footer = """ 34 | 35 | """ 36 | 37 | [git] 38 | conventional_commits = true 39 | filter_unconventional = false 40 | commit_preprocessors = [ 41 | { pattern = '#(\d+)', replace = "[#$1](https://github.com/paradigmxyz/revmc/issues/$1)" }, 42 | ] 43 | commit_parsers = [ 44 | { message = "^[Ff]eat", group = "Features" }, 45 | { message = "^[Ff]ix", group = "Bug Fixes" }, 46 | { message = "^[Dd]oc", group = "Documentation" }, 47 | { message = ".*\\b([Dd]eps|[Dd]ependencies|[Bb]ump)\\b", group = "Dependencies" }, 48 | { message = "^[Pp]erf", group = "Performance" }, 49 | { message = "^[Rr]efactor", group = "Refactor" }, 50 | { message = ".*\\b([Ss]tyle|[Ff]mt|[Ff]ormat)\\b", group = "Styling" }, 51 | { message = "^[Tt]est", group = "Testing" }, 52 | { message = "^[Cc]hore", group = "Miscellaneous Tasks" }, 53 | 54 | { message = ".*", group = "Other" }, 55 | ] 56 | protect_breaking_commits = false 57 | filter_commits = false 58 | tag_pattern = "v[0-9]*" 59 | skip_tags = "beta|alpha" 60 | ignore_tags = "rc" 61 | sort_commits = "newest" 62 | -------------------------------------------------------------------------------- /clippy.toml: -------------------------------------------------------------------------------- 1 | msrv = "1.79.0" 2 | -------------------------------------------------------------------------------- /crates/revmc-backend/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.1.0](https://github.com/paradigmxyz/revmc/releases/tag/v0.1.0) - 2024-06-27 9 | 10 | ### Documentation 11 | 12 | - Add changelogs 13 | 14 | ### Features 15 | 16 | - Add target configuration ([#39](https://github.com/paradigmxyz/revmc/issues/39)) 17 | - Implement IR builtins ([#36](https://github.com/paradigmxyz/revmc/issues/36)) 18 | 19 | ### Miscellaneous Tasks 20 | 21 | - Replace timing macros with tracy ([#40](https://github.com/paradigmxyz/revmc/issues/40)) 22 | - Rebrand to `revmc` ([#33](https://github.com/paradigmxyz/revmc/issues/33)) 23 | 24 | 25 | -------------------------------------------------------------------------------- /crates/revmc-backend/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "revmc-backend" 3 | description = "EVM bytecode compiler backend abstraction" 4 | homepage = "https://github.com/danipopes/revmc/tree/main/crates/revmc-backend" 5 | 6 | version.workspace = true 7 | authors.workspace = true 8 | edition.workspace = true 9 | rust-version.workspace = true 10 | license.workspace = true 11 | categories.workspace = true 12 | keywords.workspace = true 13 | repository.workspace = true 14 | exclude.workspace = true 15 | 16 | [lints] 17 | workspace = true 18 | 19 | [dependencies] 20 | ruint = { workspace = true, features = ["std"] } 21 | eyre.workspace = true 22 | -------------------------------------------------------------------------------- /crates/revmc-backend/README.md: -------------------------------------------------------------------------------- 1 | # revmc-backend 2 | 3 | EVM bytecode compiler backend abstraction. 4 | -------------------------------------------------------------------------------- /crates/revmc-backend/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../README.md")] 2 | #![allow(missing_docs)] 3 | #![cfg_attr(not(test), warn(unused_extern_crates))] 4 | #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] 5 | 6 | mod traits; 7 | pub use traits::*; 8 | 9 | #[doc(no_inline)] 10 | pub use eyre; 11 | #[doc(no_inline)] 12 | pub use ruint::{self, aliases::U256, uint}; 13 | 14 | mod pointer; 15 | pub use pointer::{Pointer, PointerBase}; 16 | 17 | /// Compilation result. 18 | pub type Result = std::result::Result; 19 | 20 | /// Compilation error. 21 | pub type Error = eyre::Error; 22 | -------------------------------------------------------------------------------- /crates/revmc-backend/src/pointer.rs: -------------------------------------------------------------------------------- 1 | use crate::Builder; 2 | 3 | /// A pointer to a value. 4 | #[derive(Clone, Copy, Debug)] 5 | pub struct Pointer { 6 | /// The type of the pointee. 7 | pub ty: B::Type, 8 | /// The base of the pointer. Either an address or a stack slot. 9 | pub base: PointerBase, 10 | } 11 | 12 | /// The base of a pointer. Either an address or a stack slot. 13 | #[derive(Clone, Copy, Debug)] 14 | pub enum PointerBase { 15 | /// An address. 16 | Address(B::Value), 17 | /// A stack slot. 18 | StackSlot(B::StackSlot), 19 | } 20 | 21 | impl Pointer { 22 | /// Creates a new stack-allocated pointer. 23 | pub fn new_stack_slot(bcx: &mut B, ty: B::Type, name: &str) -> Self { 24 | let slot = bcx.new_stack_slot_raw(ty, name); 25 | Self { ty, base: PointerBase::StackSlot(slot) } 26 | } 27 | 28 | /// Creates a new address pointer. 29 | pub fn new_address(ty: B::Type, value: B::Value) -> Self { 30 | Self { ty, base: PointerBase::Address(value) } 31 | } 32 | 33 | /// Returns `true` if the pointer is an address. 34 | pub fn is_address(&self) -> bool { 35 | matches!(self.base, PointerBase::Address(_)) 36 | } 37 | 38 | /// Returns `true` if the pointer is a stack slot. 39 | pub fn is_stack_slot(&self) -> bool { 40 | matches!(self.base, PointerBase::StackSlot(_)) 41 | } 42 | 43 | /// Converts the pointer to an address. 44 | pub fn into_address(self) -> Option { 45 | match self.base { 46 | PointerBase::Address(ptr) => Some(ptr), 47 | PointerBase::StackSlot(_) => None, 48 | } 49 | } 50 | 51 | /// Converts the pointer to a stack slot. 52 | pub fn into_stack_slot(self) -> Option { 53 | match self.base { 54 | PointerBase::Address(_) => None, 55 | PointerBase::StackSlot(slot) => Some(slot), 56 | } 57 | } 58 | 59 | /// Loads the value from the pointer. 60 | pub fn load(&self, bcx: &mut B, name: &str) -> B::Value { 61 | match self.base { 62 | PointerBase::Address(ptr) => bcx.load(self.ty, ptr, name), 63 | PointerBase::StackSlot(slot) => bcx.stack_load(self.ty, slot, name), 64 | } 65 | } 66 | 67 | /// Stores the value to the pointer. 68 | pub fn store(&self, bcx: &mut B, value: B::Value) { 69 | match self.base { 70 | PointerBase::Address(ptr) => bcx.store(value, ptr), 71 | PointerBase::StackSlot(slot) => bcx.stack_store(value, slot), 72 | } 73 | } 74 | 75 | /// Stores the value to the pointer. 76 | pub fn store_imm(&self, bcx: &mut B, value: i64) { 77 | let value = bcx.iconst(self.ty, value); 78 | self.store(bcx, value) 79 | } 80 | 81 | /// Gets the address of the pointer. 82 | pub fn addr(&self, bcx: &mut B) -> B::Value { 83 | match self.base { 84 | PointerBase::Address(ptr) => ptr, 85 | PointerBase::StackSlot(slot) => bcx.stack_addr(self.ty, slot), 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /crates/revmc-build/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.1.0](https://github.com/paradigmxyz/revmc/releases/tag/v0.1.0) - 2024-06-27 9 | 10 | ### Documentation 11 | 12 | - Add changelogs 13 | 14 | ### Miscellaneous Tasks 15 | 16 | - Update some comments 17 | - Rebrand to `revmc` ([#33](https://github.com/paradigmxyz/revmc/issues/33)) 18 | 19 | 20 | -------------------------------------------------------------------------------- /crates/revmc-build/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "revmc-build" 3 | description = "EVM bytecode compiler build script support" 4 | homepage = "https://github.com/danipopes/revmc/tree/main/crates/revmc-build" 5 | 6 | version.workspace = true 7 | authors.workspace = true 8 | edition.workspace = true 9 | rust-version.workspace = true 10 | license.workspace = true 11 | categories.workspace = true 12 | keywords.workspace = true 13 | repository.workspace = true 14 | exclude = ["build.rs"] 15 | 16 | [lints] 17 | workspace = true 18 | -------------------------------------------------------------------------------- /crates/revmc-build/README.md: -------------------------------------------------------------------------------- 1 | # revmc-build 2 | 3 | EVM bytecode compiler build script support. 4 | -------------------------------------------------------------------------------- /crates/revmc-build/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../README.md")] 2 | #![cfg_attr(not(test), warn(unused_extern_crates))] 3 | #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] 4 | 5 | // Must be kept in sync with `remvc-builtins`. 6 | const MANGLE_PREFIX: &str = "__revmc_builtin_"; 7 | 8 | /// Emits the linker flag to export all the necessary symbols. 9 | pub fn emit() { 10 | let target_vendor = std::env::var("CARGO_CFG_TARGET_VENDOR").unwrap(); 11 | let flag = 12 | if target_vendor == "apple" { "-exported_symbol" } else { "--export-dynamic-symbol" }; 13 | println!("cargo:rustc-link-arg=-Wl,{flag},{MANGLE_PREFIX}*"); 14 | } 15 | -------------------------------------------------------------------------------- /crates/revmc-builtins/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.1.0](https://github.com/paradigmxyz/revmc/releases/tag/v0.1.0) - 2024-06-27 9 | 10 | ### Documentation 11 | 12 | - Add changelogs 13 | 14 | ### Features 15 | 16 | - Implement IR builtins ([#36](https://github.com/paradigmxyz/revmc/issues/36)) 17 | 18 | ### Miscellaneous Tasks 19 | 20 | - Update some comments 21 | - Rebrand to `revmc` ([#33](https://github.com/paradigmxyz/revmc/issues/33)) 22 | 23 | ### Styling 24 | 25 | - Clippy, add more lints ([#34](https://github.com/paradigmxyz/revmc/issues/34)) 26 | 27 | 28 | -------------------------------------------------------------------------------- /crates/revmc-builtins/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "revmc-builtins" 3 | description = "EVM bytecode compiler builtins" 4 | homepage = "https://github.com/danipopes/revmc/tree/main/crates/revmc-builtins" 5 | 6 | version.workspace = true 7 | authors.workspace = true 8 | edition.workspace = true 9 | rust-version.workspace = true 10 | license.workspace = true 11 | categories.workspace = true 12 | keywords.workspace = true 13 | repository.workspace = true 14 | exclude.workspace = true 15 | 16 | [lints] 17 | workspace = true 18 | 19 | [dependencies] 20 | revmc-context.workspace = true 21 | 22 | revm-primitives.workspace = true 23 | revm-interpreter.workspace = true 24 | paste.workspace = true 25 | 26 | revmc-backend = { workspace = true, optional = true } 27 | tracing = { workspace = true, optional = true } 28 | 29 | [features] 30 | default = ["std", "ir"] 31 | std = ["revmc-context/std", "revm-primitives/std", "revm-interpreter/std"] 32 | ir = ["std", "dep:tracing", "dep:revmc-backend"] 33 | -------------------------------------------------------------------------------- /crates/revmc-builtins/README.md: -------------------------------------------------------------------------------- 1 | # revmc-builtins 2 | 3 | EVM bytecode compiler builtins. 4 | -------------------------------------------------------------------------------- /crates/revmc-builtins/src/gas.rs: -------------------------------------------------------------------------------- 1 | //! Gas calculation utilities. 2 | 3 | use revm_primitives::{SpecId, U256}; 4 | 5 | pub use revm_interpreter::gas::*; 6 | 7 | /// `const` Option `?`. 8 | #[allow(unused_macros)] 9 | macro_rules! tri { 10 | ($e:expr) => { 11 | match $e { 12 | Some(v) => v, 13 | None => return None, 14 | } 15 | }; 16 | } 17 | 18 | // These are overridden to only account for the dynamic cost. 19 | 20 | /// `EXP` opcode cost calculation. 21 | #[inline] 22 | pub fn dyn_exp_cost(spec_id: SpecId, power: U256) -> Option { 23 | #[inline] 24 | const fn log2floor(value: U256) -> u64 { 25 | let mut l: u64 = 256; 26 | let mut i = 3; 27 | loop { 28 | if value.as_limbs()[i] == 0u64 { 29 | l -= 64; 30 | } else { 31 | l -= value.as_limbs()[i].leading_zeros() as u64; 32 | if l == 0 { 33 | return l; 34 | } else { 35 | return l - 1; 36 | } 37 | } 38 | if i == 0 { 39 | break; 40 | } 41 | i -= 1; 42 | } 43 | l 44 | } 45 | 46 | if power == U256::ZERO { 47 | Some(0) 48 | } else { 49 | // EIP-160: EXP cost increase 50 | let gas_byte = 51 | U256::from(if spec_id.is_enabled_in(SpecId::SPURIOUS_DRAGON) { 50 } else { 10 }); 52 | let gas = gas_byte.checked_mul(U256::from(log2floor(power) / 8 + 1))?; 53 | u64::try_from(gas).ok() 54 | } 55 | } 56 | 57 | /// `LOG` opcode cost calculation. 58 | #[inline] 59 | pub const fn dyn_log_cost(len: u64) -> Option { 60 | LOGDATA.checked_mul(len) 61 | } 62 | 63 | /// `KECCAK256` opcode cost calculation. 64 | #[inline] 65 | pub const fn dyn_keccak256_cost(len: u64) -> Option { 66 | cost_per_word(len, KECCAK256WORD) 67 | } 68 | 69 | /// `*COPY` opcodes cost calculation. 70 | #[inline] 71 | pub const fn dyn_verylowcopy_cost(len: u64) -> Option { 72 | cost_per_word(len, COPY) 73 | } 74 | 75 | #[cfg(test)] 76 | mod tests { 77 | use super::*; 78 | 79 | #[test] 80 | fn exp_cost() { 81 | for (spec_id, power) in [ 82 | (SpecId::CANCUN, U256::from(0)), 83 | (SpecId::CANCUN, U256::from(1)), 84 | (SpecId::CANCUN, U256::from(69)), 85 | ] { 86 | assert_eq!( 87 | super::exp_cost(spec_id, power).unwrap(), 88 | EXP + dyn_exp_cost(spec_id, power).unwrap(), 89 | ); 90 | } 91 | } 92 | 93 | #[test] 94 | fn log_cost() { 95 | for n_topics in [0, 1, 2] { 96 | for len in [0, 1, 69] { 97 | assert_eq!( 98 | super::log_cost(n_topics, len).unwrap(), 99 | LOG + (LOGTOPIC * n_topics as u64) + dyn_log_cost(len).unwrap(), 100 | ); 101 | } 102 | } 103 | } 104 | 105 | #[test] 106 | fn keccak256_cost() { 107 | for len in [0, 1, 69] { 108 | assert_eq!( 109 | super::keccak256_cost(len).unwrap(), 110 | KECCAK256 + dyn_keccak256_cost(len).unwrap(), 111 | ); 112 | } 113 | } 114 | 115 | #[test] 116 | fn verylowcopy_cost() { 117 | for len in [0, 1, 69] { 118 | assert_eq!( 119 | super::verylowcopy_cost(len).unwrap(), 120 | VERYLOW + dyn_verylowcopy_cost(len).unwrap(), 121 | ); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /crates/revmc-builtins/src/ir.rs: -------------------------------------------------------------------------------- 1 | use revmc_backend::{Attribute, Backend, Builder, FunctionAttributeLocation, TypeMethods}; 2 | 3 | // Must be kept in sync with `remvc-build`. 4 | const MANGLE_PREFIX: &str = "__revmc_builtin_"; 5 | 6 | /// Builtin cache. 7 | #[derive(Debug)] 8 | pub struct Builtins([Option; Builtin::COUNT]); 9 | 10 | impl Default for Builtins { 11 | fn default() -> Self { 12 | Self::new() 13 | } 14 | } 15 | 16 | impl Builtins { 17 | /// Create a new cache. 18 | pub fn new() -> Self { 19 | Self([None; Builtin::COUNT]) 20 | } 21 | 22 | /// Clear the cache. 23 | pub fn clear(&mut self) { 24 | *self = Self::new(); 25 | } 26 | 27 | /// Get the function for the given builtin. 28 | pub fn get(&mut self, builtin: Builtin, bcx: &mut B::Builder<'_>) -> B::Function { 29 | *self.0[builtin as usize].get_or_insert_with(|| Self::init(builtin, bcx)) 30 | } 31 | 32 | #[cold] 33 | fn init(builtin: Builtin, bcx: &mut B::Builder<'_>) -> B::Function { 34 | let name = builtin.name(); 35 | debug_assert!(name.starts_with(MANGLE_PREFIX), "{name:?}"); 36 | bcx.get_function(name).inspect(|r| trace!(name, ?r, "pre-existing")).unwrap_or_else(|| { 37 | let r = Self::build(name, builtin, bcx); 38 | trace!(name, ?r, "built"); 39 | r 40 | }) 41 | } 42 | 43 | fn build(name: &str, builtin: Builtin, bcx: &mut B::Builder<'_>) -> B::Function { 44 | let ret = builtin.ret(bcx); 45 | let params = builtin.params(bcx); 46 | let address = builtin.addr(); 47 | let linkage = revmc_backend::Linkage::Import; 48 | let f = bcx.add_function(name, ¶ms, ret, Some(address), linkage); 49 | let default_attrs: &[Attribute] = if builtin == Builtin::Panic { 50 | &[ 51 | Attribute::Cold, 52 | Attribute::NoReturn, 53 | Attribute::NoFree, 54 | Attribute::NoRecurse, 55 | Attribute::NoSync, 56 | ] 57 | } else { 58 | &[ 59 | Attribute::WillReturn, 60 | Attribute::NoFree, 61 | Attribute::NoRecurse, 62 | Attribute::NoSync, 63 | Attribute::NoUnwind, 64 | Attribute::Speculatable, 65 | ] 66 | }; 67 | for attr in default_attrs.iter().chain(builtin.attrs()).copied() { 68 | bcx.add_function_attribute(Some(f), attr, FunctionAttributeLocation::Function); 69 | } 70 | let param_attrs = builtin.param_attrs(); 71 | for (i, param_attrs) in param_attrs.iter().enumerate() { 72 | for attr in param_attrs { 73 | bcx.add_function_attribute( 74 | Some(f), 75 | *attr, 76 | FunctionAttributeLocation::Param(i as u32), 77 | ); 78 | } 79 | } 80 | f 81 | } 82 | } 83 | 84 | macro_rules! builtins { 85 | (@count) => { 0 }; 86 | (@count $first:tt $(, $rest:tt)*) => { 1 + builtins!(@count $($rest),*) }; 87 | 88 | (@param_attr $default:ident) => { $default() }; 89 | (@param_attr $default:ident $name:expr) => { $name }; 90 | 91 | (@types |$bcx:ident| { $($types_init:tt)* } 92 | @param_attrs |$op:ident| { $($attrs_init:tt)* } 93 | $($ident:ident = $(#[$attr:expr])* $name:ident($($(@[$param_attr:expr])? $params:expr),* $(,)?) $ret:expr),* $(,)? 94 | ) => { paste::paste! { 95 | /// Builtins that can be called by the compiled functions. 96 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 97 | pub enum Builtin { 98 | $($ident,)* 99 | } 100 | 101 | #[allow(unused_variables)] 102 | impl Builtin { 103 | pub const COUNT: usize = builtins!(@count $($ident),*); 104 | 105 | pub const fn name(self) -> &'static str { 106 | match self { 107 | $(Self::$ident => stringify!($name),)* 108 | } 109 | } 110 | 111 | pub fn addr(self) -> usize { 112 | match self { 113 | $(Self::$ident => crate::$name as usize,)* 114 | } 115 | } 116 | 117 | pub fn ret(self, $bcx: &mut B) -> Option { 118 | $($types_init)* 119 | match self { 120 | $(Self::$ident => $ret,)* 121 | } 122 | } 123 | 124 | pub fn params(self, $bcx: &mut B) -> Vec { 125 | $($types_init)* 126 | match self { 127 | $(Self::$ident => vec![$($params),*],)* 128 | } 129 | } 130 | 131 | pub fn attrs(self) -> &'static [Attribute] { 132 | #[allow(unused_imports)] 133 | use Attribute::*; 134 | match self { 135 | $(Self::$ident => &[$($attr)*]),* 136 | } 137 | } 138 | 139 | #[allow(non_upper_case_globals)] 140 | pub fn param_attrs(self) -> Vec> { 141 | #[allow(unused_imports)] 142 | use Attribute::*; 143 | let $op = self; 144 | let default = || vec![Attribute::NoUndef]; 145 | $($attrs_init)* 146 | match self { 147 | $(Self::$ident => vec![$(builtins!(@param_attr default $($param_attr)?)),*]),* 148 | } 149 | } 150 | 151 | fn op(self) -> u8 { 152 | use revm_interpreter::opcode::*; 153 | const PANIC: u8 = 0; 154 | const LOG: u8 = LOG0; 155 | const DORETURN: u8 = RETURN; 156 | const RESIZEMEMORY: u8 = 0; 157 | const FUNCSTACKPUSH: u8 = 0; 158 | const FUNCSTACKPOP: u8 = 0; 159 | const FUNCSTACKGROW: u8 = 0; 160 | 161 | match self { 162 | $(Self::$ident => [<$ident:upper>]),* 163 | } 164 | } 165 | } 166 | }}; 167 | } 168 | 169 | builtins! { 170 | @types |bcx| { 171 | let ptr = bcx.type_ptr(); 172 | let usize = bcx.type_ptr_sized_int(); 173 | let bool = bcx.type_int(1); 174 | let u8 = bcx.type_int(8); 175 | } 176 | 177 | @param_attrs |op| { 178 | fn size_and_align() -> Vec { 179 | size_and_align2(Some(core::mem::size_of::()), core::mem::align_of::()) 180 | } 181 | 182 | fn size_and_align2(size: Option, align: usize) -> Vec { 183 | let mut vec = Vec::with_capacity(5); 184 | vec.push(Attribute::NoAlias); 185 | vec.push(Attribute::NoCapture); 186 | vec.push(Attribute::NoUndef); 187 | vec.push(Attribute::Align(align as u64)); 188 | if let Some(size) = size { 189 | vec.push(Attribute::Dereferenceable(size as u64)); 190 | } 191 | vec 192 | } 193 | 194 | let op = op.op(); 195 | let (inputs, outputs) = if let Some(info) = revm_interpreter::opcode::OPCODE_INFO_JUMPTABLE[op as usize] { 196 | (info.inputs(), info.outputs()) 197 | } else { 198 | (0, 0) 199 | }; 200 | 201 | let ecx = size_and_align::>(); 202 | 203 | let sp_dyn = size_and_align2(None, core::mem::align_of::()); 204 | 205 | let mut sp = sp_dyn.clone(); 206 | // `sp` is at `top - inputs`, we have access to `max(inputs, outputs)` words. 207 | let n_stack_words = inputs.max(outputs); 208 | let size_of_word = core::mem::size_of::(); 209 | sp.push(Attribute::Dereferenceable(size_of_word as u64 * n_stack_words as u64)); 210 | match (inputs, outputs) { 211 | (0, 0) => sp.push(Attribute::ReadNone), 212 | (0, 1..) => sp.push(Attribute::WriteOnly), 213 | (1.., 0) => sp.push(Attribute::ReadOnly), 214 | (1.., 1..) => {} 215 | } 216 | } 217 | 218 | Panic = __revmc_builtin_panic(ptr, usize) None, 219 | 220 | AddMod = __revmc_builtin_addmod(@[sp] ptr) None, 221 | MulMod = __revmc_builtin_mulmod(@[sp] ptr) None, 222 | Exp = __revmc_builtin_exp(@[ecx] ptr, @[sp] ptr, u8) Some(u8), 223 | Keccak256 = __revmc_builtin_keccak256(@[ecx] ptr, @[sp] ptr) Some(u8), 224 | Balance = __revmc_builtin_balance(@[ecx] ptr, @[sp] ptr, u8) Some(u8), 225 | CallDataCopy = __revmc_builtin_calldatacopy(@[ecx] ptr, @[sp] ptr) Some(u8), 226 | CodeSize = __revmc_builtin_codesize(@[ecx] ptr) Some(usize), 227 | CodeCopy = __revmc_builtin_codecopy(@[ecx] ptr, @[sp] ptr) Some(u8), 228 | GasPrice = __revmc_builtin_gas_price(@[ecx] ptr, @[sp] ptr) None, 229 | ExtCodeSize = __revmc_builtin_extcodesize(@[ecx] ptr, @[sp] ptr, u8) Some(u8), 230 | ExtCodeCopy = __revmc_builtin_extcodecopy(@[ecx] ptr, @[sp] ptr, u8) Some(u8), 231 | ReturnDataCopy = __revmc_builtin_returndatacopy(@[ecx] ptr, @[sp] ptr) Some(u8), 232 | ExtCodeHash = __revmc_builtin_extcodehash(@[ecx] ptr, @[sp] ptr, u8) Some(u8), 233 | BlockHash = __revmc_builtin_blockhash(@[ecx] ptr, @[sp] ptr) Some(u8), 234 | Difficulty = __revmc_builtin_difficulty(@[ecx] ptr, @[sp] ptr, u8) None, 235 | SelfBalance = __revmc_builtin_self_balance(@[ecx] ptr, @[sp] ptr) Some(u8), 236 | BlobHash = __revmc_builtin_blob_hash(@[ecx] ptr, @[sp] ptr) None, 237 | BlobBaseFee = __revmc_builtin_blob_base_fee(@[ecx] ptr, @[sp] ptr) None, 238 | Sload = __revmc_builtin_sload(@[ecx] ptr, @[sp] ptr, u8) Some(u8), 239 | Sstore = __revmc_builtin_sstore(@[ecx] ptr, @[sp] ptr, u8) Some(u8), 240 | Msize = __revmc_builtin_msize(@[ecx] ptr) Some(usize), 241 | Tstore = __revmc_builtin_tstore(@[ecx] ptr, @[sp] ptr) Some(u8), 242 | Tload = __revmc_builtin_tload(@[ecx] ptr, @[sp] ptr) None, 243 | Mcopy = __revmc_builtin_mcopy(@[ecx] ptr, @[sp] ptr) Some(u8), 244 | Log = __revmc_builtin_log(@[ecx] ptr, @[sp_dyn] ptr, u8) Some(u8), 245 | DataLoad = __revmc_builtin_data_load(@[ecx] ptr, @[sp] ptr) None, 246 | DataCopy = __revmc_builtin_data_copy(@[ecx] ptr, @[sp] ptr) Some(u8), 247 | ReturnDataLoad = __revmc_builtin_returndataload(@[ecx] ptr, @[sp] ptr) None, 248 | 249 | EofCreate = __revmc_builtin_eof_create(@[ecx] ptr, @[sp] ptr, usize) Some(u8), 250 | ReturnContract = __revmc_builtin_return_contract(@[ecx] ptr, @[sp] ptr, usize) Some(u8), 251 | Create = __revmc_builtin_create(@[ecx] ptr, @[sp_dyn] ptr, u8, u8) Some(u8), 252 | Call = __revmc_builtin_call(@[ecx] ptr, @[sp_dyn] ptr, u8, u8) Some(u8), 253 | ExtCall = __revmc_builtin_ext_call(@[ecx] ptr, @[sp_dyn] ptr, u8, u8) Some(u8), 254 | DoReturn = __revmc_builtin_do_return(@[ecx] ptr, @[sp] ptr, u8) Some(u8), 255 | SelfDestruct = __revmc_builtin_selfdestruct(@[ecx] ptr, @[sp] ptr, u8) Some(u8), 256 | 257 | FuncStackPush = __revmc_builtin_func_stack_push(@[ecx] ptr, ptr, usize) Some(u8), 258 | FuncStackPop = __revmc_builtin_func_stack_pop(@[ecx] ptr) Some(ptr), 259 | FuncStackGrow = __revmc_builtin_func_stack_grow(@[ecx] ptr) None, 260 | 261 | ResizeMemory = __revmc_builtin_resize_memory(@[ecx] ptr, usize) Some(u8), 262 | } 263 | -------------------------------------------------------------------------------- /crates/revmc-builtins/src/macros.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused_macros)] 2 | macro_rules! tri { 3 | ($e:expr) => { 4 | match $e { 5 | Ok(x) => x, 6 | Err(_) => return InstructionResult::InvalidOperandOOG, 7 | } 8 | }; 9 | } 10 | 11 | macro_rules! try_opt { 12 | ($e:expr) => { 13 | match $e { 14 | Some(x) => x, 15 | None => return InstructionResult::InvalidOperandOOG, 16 | } 17 | }; 18 | } 19 | 20 | macro_rules! try_host { 21 | ($e:expr) => { 22 | match $e { 23 | Some(x) => x, 24 | None => return InstructionResult::FatalExternalError, 25 | } 26 | }; 27 | } 28 | 29 | macro_rules! try_ir { 30 | ($e:expr) => { 31 | match $e { 32 | InstructionResult::Continue => {} 33 | ir => return ir, 34 | } 35 | }; 36 | } 37 | 38 | macro_rules! gas { 39 | ($ecx:expr, $gas:expr) => { 40 | if !$ecx.gas.record_cost($gas) { 41 | return InstructionResult::OutOfGas; 42 | } 43 | }; 44 | } 45 | 46 | macro_rules! gas_opt { 47 | ($ecx:expr, $gas:expr) => { 48 | match $gas { 49 | Some(gas) => gas!($ecx, gas), 50 | None => return InstructionResult::OutOfGas, 51 | } 52 | }; 53 | } 54 | 55 | macro_rules! ensure_non_staticcall { 56 | ($ecx:expr) => { 57 | if $ecx.is_static { 58 | return InstructionResult::StateChangeDuringStaticCall; 59 | } 60 | }; 61 | } 62 | 63 | macro_rules! ensure_memory { 64 | ($ecx:expr, $offset:expr, $len:expr) => { 65 | try_ir!(ensure_memory($ecx, $offset, $len)) 66 | }; 67 | } 68 | 69 | /// Same as `read_words_rev`, but returns the arguments in the order they were passed. 70 | macro_rules! read_words { 71 | ($sp:expr, $($words:ident),+ $(,)?) => { 72 | let rev![$($words),+] = unsafe { read_words_rev($sp) }; 73 | }; 74 | } 75 | 76 | macro_rules! pop { 77 | ($sp:expr; $($x:ident),* $(,)?) => { 78 | $( 79 | $sp = $sp.sub(1); 80 | let $x = &mut *$sp; 81 | )* 82 | }; 83 | } 84 | 85 | macro_rules! try_into_usize { 86 | ($x:expr) => { 87 | match $x.to_u256().as_limbs() { 88 | x => { 89 | if (x[0] > usize::MAX as u64) | (x[1] != 0) | (x[2] != 0) | (x[3] != 0) { 90 | return InstructionResult::InvalidOperandOOG; 91 | } 92 | x[0] as usize 93 | } 94 | } 95 | }; 96 | } 97 | 98 | // Credits: 99 | macro_rules! rev { 100 | (@rev [$first:tt$(, $rest:tt)*] [$($rev:tt),*]) => { 101 | rev! { 102 | @rev [$($rest),*][$first $(, $rev)*] 103 | } 104 | }; 105 | (@rev [] [$($rev:tt),*]) => { 106 | [$($rev)*] // NOTE: Extra `[]` to make this an array pattern. 107 | }; 108 | ($($tt:tt)+) => { 109 | rev! { 110 | @rev [$($tt),+] [] 111 | } 112 | }; 113 | } 114 | 115 | macro_rules! debug_unreachable { 116 | ($($t:tt)*) => { 117 | if cfg!(debug_assertions) { 118 | unreachable!($($t)*); 119 | } else { 120 | unsafe { core::hint::unreachable_unchecked() }; 121 | } 122 | }; 123 | } 124 | 125 | macro_rules! assume { 126 | ($e:expr $(,)?) => { 127 | if !$e { 128 | debug_unreachable!(stringify!($e)); 129 | } 130 | }; 131 | 132 | ($e:expr, $($t:tt)+) => { 133 | if !$e { 134 | debug_unreachable!($($t)+); 135 | } 136 | }; 137 | } 138 | -------------------------------------------------------------------------------- /crates/revmc-builtins/src/utils.rs: -------------------------------------------------------------------------------- 1 | use crate::gas; 2 | use revm_interpreter::{as_usize_saturated, Gas, InstructionResult, SharedMemory}; 3 | use revmc_context::{EvmContext, EvmWord}; 4 | 5 | /// Splits the stack pointer into `N` elements by casting it to an array. 6 | /// 7 | /// NOTE: this returns the arguments in **reverse order**. Use [`read_words!`] to get them in order. 8 | /// 9 | /// The returned lifetime is valid for the entire duration of the builtin. 10 | /// 11 | /// # Safety 12 | /// 13 | /// Caller must ensure that `N` matches the number of elements popped in JIT code. 14 | #[inline(always)] 15 | pub(crate) unsafe fn read_words_rev<'a, const N: usize>(sp: *mut EvmWord) -> &'a mut [EvmWord; N] { 16 | &mut *sp.cast::<[EvmWord; N]>() 17 | } 18 | 19 | #[inline] 20 | pub(crate) fn ensure_memory( 21 | ecx: &mut EvmContext<'_>, 22 | offset: usize, 23 | len: usize, 24 | ) -> InstructionResult { 25 | ensure_memory_inner(ecx.memory, ecx.gas, offset, len) 26 | } 27 | 28 | #[inline] 29 | pub(crate) fn ensure_memory_inner( 30 | memory: &mut SharedMemory, 31 | gas: &mut Gas, 32 | offset: usize, 33 | len: usize, 34 | ) -> InstructionResult { 35 | let new_size = offset.saturating_add(len); 36 | if new_size > memory.len() { 37 | return resize_memory_inner(memory, gas, new_size); 38 | } 39 | InstructionResult::Continue 40 | } 41 | 42 | #[inline] 43 | pub(crate) fn resize_memory(ecx: &mut EvmContext<'_>, new_size: usize) -> InstructionResult { 44 | resize_memory_inner(ecx.memory, ecx.gas, new_size) 45 | } 46 | 47 | fn resize_memory_inner( 48 | memory: &mut SharedMemory, 49 | gas: &mut Gas, 50 | new_size: usize, 51 | ) -> InstructionResult { 52 | // TODO: Memory limit 53 | if !revm_interpreter::interpreter::resize_memory(memory, gas, new_size) { 54 | return InstructionResult::MemoryOOG; 55 | } 56 | InstructionResult::Continue 57 | } 58 | 59 | pub(crate) unsafe fn copy_operation( 60 | ecx: &mut EvmContext<'_>, 61 | rev![memory_offset, data_offset, len]: &mut [EvmWord; 3], 62 | data: &[u8], 63 | ) -> InstructionResult { 64 | let len = try_into_usize!(len); 65 | if len != 0 { 66 | gas_opt!(ecx, gas::dyn_verylowcopy_cost(len as u64)); 67 | let memory_offset = try_into_usize!(memory_offset); 68 | ensure_memory!(ecx, memory_offset, len); 69 | let data_offset = data_offset.to_u256(); 70 | let data_offset = as_usize_saturated!(data_offset); 71 | ecx.memory.set_data(memory_offset, data_offset, len, data); 72 | } 73 | InstructionResult::Continue 74 | } 75 | 76 | #[inline(always)] 77 | pub(crate) const unsafe fn decouple_lt<'b, T: ?Sized>(x: &T) -> &'b T { 78 | core::mem::transmute(x) 79 | } 80 | -------------------------------------------------------------------------------- /crates/revmc-cli-tests/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.1.0](https://github.com/paradigmxyz/revmc/releases/tag/v0.1.0) - 2024-06-27 9 | 10 | ### Documentation 11 | 12 | - Add changelogs 13 | 14 | ### Miscellaneous Tasks 15 | 16 | - Rebrand to `revmc` ([#33](https://github.com/paradigmxyz/revmc/issues/33)) 17 | 18 | 19 | -------------------------------------------------------------------------------- /crates/revmc-cli-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "revmc-cli-tests" 3 | description = "EVM bytecode compiler CLI tests" 4 | homepage = "https://github.com/danipopes/revmc/tree/main/crates/revmc-cli-test" 5 | publish = false 6 | 7 | version.workspace = true 8 | authors.workspace = true 9 | edition.workspace = true 10 | rust-version.workspace = true 11 | license.workspace = true 12 | categories.workspace = true 13 | keywords.workspace = true 14 | repository.workspace = true 15 | exclude.workspace = true 16 | 17 | [lints] 18 | workspace = true 19 | 20 | [dependencies] 21 | walkdir = "2.5" 22 | tester = "0.9" 23 | -------------------------------------------------------------------------------- /crates/revmc-cli-tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! CLI tests runner. 2 | 3 | use tester as test; 4 | 5 | use std::{ 6 | fs, 7 | path::{Path, PathBuf}, 8 | process::Command, 9 | sync::Arc, 10 | }; 11 | use test::{ShouldPanic, TestDesc, TestDescAndFn, TestFn, TestName, TestType}; 12 | use walkdir::{DirEntry, WalkDir}; 13 | 14 | /// Run all tests with the given command. 15 | pub fn run_tests(cmd: &'static Path) -> i32 { 16 | let args = std::env::args().collect::>(); 17 | let mut opts = match test::test::parse_opts(&args) { 18 | Some(Ok(o)) => o, 19 | Some(Err(msg)) => { 20 | eprintln!("error: {msg}"); 21 | return 101; 22 | } 23 | None => return 0, 24 | }; 25 | 26 | /* 27 | // Condense output if not explicitly requested. 28 | let requested_pretty = || args.iter().any(|x| x.contains("--format")); 29 | if opts.format == test::OutputFormat::Pretty && !requested_pretty() { 30 | opts.format = test::OutputFormat::Terse; 31 | } 32 | */ 33 | // [`tester`] currently (0.9.1) uses `num_cpus::get_physical`; 34 | // use all available threads instead. 35 | if opts.test_threads.is_none() { 36 | opts.test_threads = std::thread::available_parallelism().map(|x| x.get()).ok(); 37 | } 38 | 39 | let mut tests = Vec::new(); 40 | make_tests(cmd, &mut tests); 41 | tests.sort_by(|a, b| a.desc.name.as_slice().cmp(b.desc.name.as_slice())); 42 | 43 | if opts.list { 44 | // The only way to call `list_tests_console` is through `test_main`. 45 | test::test_main(&args, tests, Some(opts.options)); 46 | return 0; 47 | } 48 | 49 | match test::run_tests_console(&opts, tests) { 50 | Ok(true) => 0, 51 | Ok(false) => { 52 | eprintln!("Some tests failed"); 53 | 1 54 | } 55 | Err(e) => { 56 | eprintln!("I/O failure during tests: {e}"); 57 | 101 58 | } 59 | } 60 | } 61 | 62 | fn make_tests(cmd: &'static Path, tests: &mut Vec) { 63 | let config = Arc::new(Config::new(cmd)); 64 | 65 | let codegen = config.root.join("tests/codegen"); 66 | for entry in collect_tests(&codegen) { 67 | let config = config.clone(); 68 | let path = entry.path().to_path_buf(); 69 | let stripped = path.strip_prefix(config.root).unwrap(); 70 | let name = stripped.display().to_string(); 71 | tests.push(TestDescAndFn { 72 | desc: TestDesc { 73 | name: TestName::DynTestName(name), 74 | allow_fail: false, 75 | ignore: false, 76 | should_panic: ShouldPanic::No, 77 | test_type: TestType::Unknown, 78 | }, 79 | testfn: TestFn::DynTestFn(Box::new(move || run_test(&config, &path))), 80 | }); 81 | } 82 | } 83 | 84 | fn collect_tests(root: &Path) -> impl Iterator { 85 | WalkDir::new(root) 86 | .sort_by_file_name() 87 | .into_iter() 88 | .map(Result::unwrap) 89 | .filter(|e| e.file_type().is_file()) 90 | } 91 | 92 | fn run_test(config: &Config, path: &Path) { 93 | let test_name = path.file_stem().unwrap().to_str().unwrap(); 94 | 95 | // let s = fs::read_to_string(path).unwrap(); 96 | // let comment = if path.extension() == Some("evm".as_ref()) { "#"} else { "//"}; 97 | 98 | // let lines = s.lines().map(str::trim).filter(|s| !s.is_empty()); 99 | // let comments = lines.filter_map(|s| s.strip_prefix(comment)).map(str::trim_start); 100 | // let mut commands = comments.filter_map(|s| s.strip_prefix("RUN:")).map(str::trim_start); 101 | // let command = commands.next().expect("no `RUN:` directive provided"); 102 | // assert!(commands.next().is_none(), "multiple `RUN:` directives provided"); 103 | 104 | let build_dir = &config.build_base; 105 | 106 | let mut compiler = Command::new(config.cmd); 107 | fs::create_dir_all(build_dir).unwrap(); 108 | compiler.arg(path).arg("-o").arg(build_dir); 109 | // eprintln!("running compiler: {compiler:?}"); 110 | let output = compiler.output().expect("failed to run test"); 111 | assert!( 112 | output.status.success(), 113 | "compiler failed with {}:\n{}", 114 | output.status, 115 | String::from_utf8_lossy(&output.stderr) 116 | ); 117 | let out_dir = build_dir.join(test_name); 118 | assert!(out_dir.exists(), "no output produced"); 119 | 120 | let input_path = out_dir.join("opt.ll"); 121 | 122 | let mut filecheck = Command::new(config.filecheck.as_deref().unwrap_or("FileCheck".as_ref())); 123 | filecheck.arg(path).arg("--input-file").arg(&input_path); 124 | // eprintln!("running filecheck: {filecheck:?}"); 125 | let output = filecheck.output().expect("failed to run FileCheck"); 126 | assert!( 127 | output.status.success(), 128 | "FileCheck failed with {}:\n{}", 129 | output.status, 130 | String::from_utf8_lossy(&output.stderr) 131 | ); 132 | } 133 | 134 | struct Config { 135 | cmd: &'static Path, 136 | root: &'static Path, 137 | build_base: PathBuf, 138 | filecheck: Option, 139 | } 140 | 141 | impl Config { 142 | fn new(cmd: &'static Path) -> Self { 143 | let root = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap(); 144 | let build_base = root.join("target/tester"); 145 | fs::create_dir_all(&build_base).unwrap(); 146 | Self { root, cmd, build_base, filecheck: None } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /crates/revmc-cli/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.1.0](https://github.com/paradigmxyz/revmc/releases/tag/v0.1.0) - 2024-06-27 9 | 10 | ### Documentation 11 | 12 | - Add changelogs 13 | 14 | ### Features 15 | 16 | - Add target configuration ([#39](https://github.com/paradigmxyz/revmc/issues/39)) 17 | 18 | ### Miscellaneous Tasks 19 | 20 | - Replace timing macros with tracy ([#40](https://github.com/paradigmxyz/revmc/issues/40)) 21 | - Rebrand to `revmc` ([#33](https://github.com/paradigmxyz/revmc/issues/33)) 22 | 23 | ### Styling 24 | 25 | - Clippy, add more lints ([#34](https://github.com/paradigmxyz/revmc/issues/34)) 26 | 27 | 28 | -------------------------------------------------------------------------------- /crates/revmc-cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "revmc-cli" 3 | description = "EVM bytecode compiler CLI" 4 | homepage = "https://github.com/danipopes/revmc/tree/main/crates/revmc-cli" 5 | publish = false 6 | 7 | version.workspace = true 8 | authors.workspace = true 9 | edition.workspace = true 10 | rust-version.workspace = true 11 | license.workspace = true 12 | categories.workspace = true 13 | keywords.workspace = true 14 | repository.workspace = true 15 | exclude.workspace = true 16 | 17 | [lib] 18 | doc = false 19 | 20 | [lints] 21 | workspace = true 22 | 23 | [dependencies] 24 | revmc.workspace = true 25 | 26 | revm-interpreter = { workspace = true, features = ["parse"] } 27 | revm-primitives.workspace = true 28 | 29 | clap = { version = "4", features = ["derive"] } 30 | color-eyre.workspace = true 31 | libloading = "0.8" 32 | tracing-subscriber = { workspace = true, features = ["fmt", "env-filter"] } 33 | tracing-tracy = { workspace = true, optional = true } 34 | 35 | [dev-dependencies] 36 | criterion = { version = "0.5", default-features = false } 37 | iai-callgrind = "0.14" 38 | revmc-cli-tests = { path = "../revmc-cli-tests/" } 39 | 40 | [build-dependencies] 41 | revmc-build.workspace = true 42 | 43 | [features] 44 | default = ["llvm-prefer-dynamic", "revmc/asm-keccak"] 45 | llvm = ["revmc/llvm"] 46 | llvm-prefer-static = ["llvm", "revmc/llvm-prefer-static"] 47 | llvm-prefer-dynamic = ["llvm", "revmc/llvm-prefer-dynamic"] 48 | cranelift = ["revmc/cranelift"] 49 | 50 | tracy = ["dep:tracing-tracy"] 51 | 52 | [[bin]] 53 | name = "revmc-cli" 54 | path = "src/main.rs" 55 | required-features = ["llvm"] 56 | doc = false 57 | 58 | [[bench]] 59 | name = "bench" 60 | path = "benches/bench.rs" 61 | required-features = ["llvm"] 62 | harness = false 63 | doc = false 64 | 65 | [[bench]] 66 | name = "iai" 67 | path = "benches/iai.rs" 68 | required-features = ["llvm"] 69 | harness = false 70 | doc = false 71 | 72 | [[test]] 73 | name = "tests" 74 | path = "tests.rs" 75 | required-features = ["llvm"] 76 | harness = false 77 | doc = false 78 | -------------------------------------------------------------------------------- /crates/revmc-cli/benches/bench.rs: -------------------------------------------------------------------------------- 1 | #![allow(missing_docs)] 2 | 3 | use criterion::{ 4 | criterion_group, criterion_main, measurement::WallTime, BenchmarkGroup, Criterion, 5 | }; 6 | use revm_interpreter::SharedMemory; 7 | use revm_primitives::{Env, SpecId}; 8 | use revmc::{llvm, EvmCompiler, EvmCompilerFn, EvmContext, EvmLlvmBackend, EvmStack}; 9 | use revmc_cli::Bench; 10 | use std::time::Duration; 11 | 12 | const SPEC_ID: SpecId = SpecId::OSAKA; 13 | 14 | fn bench(c: &mut Criterion) { 15 | for bench in &revmc_cli::get_benches() { 16 | run_bench(c, bench); 17 | if matches!(bench.name, "hash_10k-eof") { 18 | break; 19 | } 20 | } 21 | } 22 | 23 | fn run_bench(c: &mut Criterion, bench: &Bench) { 24 | let Bench { name, bytecode, calldata, stack_input, native } = bench; 25 | 26 | let mut g = mk_group(c, name); 27 | 28 | let gas_limit = 1_000_000_000; 29 | 30 | let mut env = Env::default(); 31 | env.tx.data = calldata.clone().into(); 32 | env.tx.gas_limit = gas_limit; 33 | 34 | let bytecode = revm_interpreter::analysis::to_analysed(revm_primitives::Bytecode::new_raw( 35 | revm_primitives::Bytes::copy_from_slice(bytecode), 36 | )); 37 | let contract = revm_interpreter::Contract::new_env(&env, bytecode, None); 38 | let mut host = revm_interpreter::DummyHost::new(env); 39 | 40 | let bytecode = contract.bytecode.original_byte_slice(); 41 | 42 | let table = &revm_interpreter::opcode::make_instruction_table::< 43 | revm_interpreter::DummyHost, 44 | revm_primitives::CancunSpec, 45 | >(); 46 | 47 | // Set up the compiler. 48 | let context = llvm::inkwell::context::Context::create(); 49 | let opt_level = revmc::OptimizationLevel::Aggressive; 50 | let backend = EvmLlvmBackend::new(&context, false, opt_level).unwrap(); 51 | let mut compiler = EvmCompiler::new(backend); 52 | compiler.inspect_stack_length(!stack_input.is_empty()); 53 | compiler.gas_metering(true); 54 | 55 | if let Some(native) = *native { 56 | g.bench_function("native", |b| b.iter(native)); 57 | } 58 | 59 | let mut stack = EvmStack::new(); 60 | let mut call_jit = |f: EvmCompilerFn| { 61 | for (i, input) in stack_input.iter().enumerate() { 62 | stack.as_mut_slice()[i] = input.into(); 63 | } 64 | let mut stack_len = stack_input.len(); 65 | 66 | let mut interpreter = 67 | revm_interpreter::Interpreter::new(contract.clone(), gas_limit, false); 68 | host.clear(); 69 | let mut ecx = EvmContext::from_interpreter(&mut interpreter, &mut host); 70 | 71 | unsafe { f.call(Some(&mut stack), Some(&mut stack_len), &mut ecx) } 72 | }; 73 | 74 | let jit_matrix = [ 75 | ("default", (true, true)), 76 | ("no_gas", (false, true)), 77 | // ("no_stack", (true, false)), 78 | // ("no_gas_no_stack", (false, false)), 79 | ]; 80 | let jit_ids = jit_matrix.map(|(name, (gas, stack))| { 81 | compiler.gas_metering(gas); 82 | unsafe { compiler.stack_bound_checks(stack) }; 83 | (name, compiler.translate(name, bytecode, SPEC_ID).expect(name)) 84 | }); 85 | for &(name, fn_id) in &jit_ids { 86 | let jit = unsafe { compiler.jit_function(fn_id) }.expect(name); 87 | g.bench_function(format!("revmc/{name}"), |b| b.iter(|| call_jit(jit))); 88 | } 89 | 90 | g.bench_function("revm-interpreter", |b| { 91 | b.iter(|| { 92 | let mut int = revm_interpreter::Interpreter::new(contract.clone(), gas_limit, false); 93 | let mut host = host.clone(); 94 | 95 | int.stack.data_mut().extend_from_slice(stack_input); 96 | 97 | let action = int.run(SharedMemory::new(), table, &mut host); 98 | assert!( 99 | int.instruction_result.is_ok(), 100 | "Interpreter failed with {:?}", 101 | int.instruction_result 102 | ); 103 | assert!(action.is_return(), "Interpreter bad action: {action:?}"); 104 | action 105 | }) 106 | }); 107 | 108 | g.finish(); 109 | } 110 | 111 | fn mk_group<'a>(c: &'a mut Criterion, name: &str) -> BenchmarkGroup<'a, WallTime> { 112 | let mut g = c.benchmark_group(name); 113 | g.sample_size(20); 114 | g.warm_up_time(Duration::from_secs(2)); 115 | g.measurement_time(Duration::from_secs(5)); 116 | g 117 | } 118 | 119 | criterion_group!(benches, bench); 120 | criterion_main!(benches); 121 | -------------------------------------------------------------------------------- /crates/revmc-cli/benches/iai.rs: -------------------------------------------------------------------------------- 1 | #![allow(missing_docs)] 2 | 3 | use iai_callgrind::{ 4 | binary_benchmark_group, main, Bench, BinaryBenchmark, BinaryBenchmarkConfig, 5 | BinaryBenchmarkGroup, Command, EventKind, FlamegraphConfig, RegressionConfig, 6 | }; 7 | 8 | const CMD: &str = env!("CARGO_BIN_EXE_revmc-cli"); 9 | 10 | binary_benchmark_group!( 11 | name = revmc; 12 | benchmarks = |group: &mut BinaryBenchmarkGroup| setup_group(group) 13 | ); 14 | 15 | fn setup_group(group: &mut BinaryBenchmarkGroup) { 16 | let make_bench = |name: &str, small: bool, is_ct: bool| { 17 | let mut args = Vec::with_capacity(3); 18 | args.push(name); 19 | // let out_dir = std::env::temp_dir().join("revmc-cli-iai"); 20 | // let so = out_dir.join(name).join("a.so"); 21 | if is_ct { 22 | args.extend(["--aot", "--no-link"]); 23 | // args.extend(["--aot", "--no-link", "-o", out_dir.to_str().unwrap()]); 24 | } else { 25 | args.push("1"); 26 | // args.extend(["1", "--shared-library", so.to_str().unwrap()]); 27 | } 28 | let mut bench = Bench::new(name.replace('-', "_")); 29 | bench.command(Command::new(CMD).args(&args)).config(BinaryBenchmarkConfig::default()); 30 | 31 | if !is_ct { 32 | bench.config.as_mut().unwrap().entry_point = Some("*EvmCompilerFn::call*".into()); 33 | } 34 | 35 | let mut regression = RegressionConfig::default(); 36 | if is_ct { 37 | let cycles = if small { 50.0 } else { 20.0 }; 38 | regression.limits([(EventKind::EstimatedCycles, cycles)]); 39 | } else { 40 | regression.limits([(EventKind::EstimatedCycles, 5.0)]); 41 | } 42 | bench.config.as_mut().unwrap().regression_config = Some(regression.into()); 43 | 44 | // Uses an insane amount of memory (???) 45 | if cfg!(any()) && small && !is_ci() { 46 | let flamegraph = FlamegraphConfig::default(); 47 | bench.config.as_mut().unwrap().flamegraph_config = Some(flamegraph.into()); 48 | } 49 | 50 | bench 51 | }; 52 | let benches = [ 53 | ("fibonacci", true), 54 | ("counter", true), 55 | ("hash_10k", true), 56 | // ("hash_10k-eof", true), 57 | ("bswap64", true), 58 | ("usdc_proxy", false), 59 | ("weth", false), 60 | // ("snailtracer", false), 61 | // ("snailtracer-eof", false), 62 | ]; 63 | for is_ct in [false, true] { 64 | let mut bench = BinaryBenchmark::new(if is_ct { "compile_time" } else { "run_time" }); 65 | for (bench_name, small) in benches { 66 | if !is_ct && !small { 67 | continue; 68 | } 69 | bench.bench(make_bench(bench_name, small, is_ct)); 70 | } 71 | group.binary_benchmark(bench); 72 | } 73 | } 74 | 75 | fn is_ci() -> bool { 76 | std::env::var_os("CI").is_some() 77 | } 78 | 79 | main!(binary_benchmark_groups = revmc); 80 | -------------------------------------------------------------------------------- /crates/revmc-cli/benches/iai/prev.rs: -------------------------------------------------------------------------------- 1 | #![allow(missing_docs)] 2 | 3 | use iai_callgrind::{library_benchmark, library_benchmark_group, main}; 4 | use revm_primitives::{Env, SpecId}; 5 | use revmc::{ 6 | llvm::with_llvm_context, Backend, EvmCompiler, EvmContext, EvmLlvmBackend, OptimizationLevel, 7 | }; 8 | use revmc_cli::{get_bench, Bench}; 9 | use std::{hint::black_box, path::PathBuf}; 10 | 11 | const SPEC_ID: SpecId = SpecId::CANCUN; 12 | const GAS_LIMIT: u64 = 100_000_000; 13 | 14 | fn out_dir() -> PathBuf { 15 | std::env::temp_dir().join("revmc-cli") 16 | } 17 | 18 | fn compile_time_impl(name: &str) { 19 | with_llvm_context(|cx| { 20 | let backend = EvmLlvmBackend::new(cx, true, OptimizationLevel::Aggressive).unwrap(); 21 | compile_time_impl_inner(&mut EvmCompiler::new(backend), &get_bench(name).expect(name)); 22 | }); 23 | } 24 | 25 | fn compile_time_impl_inner(compiler: &mut EvmCompiler, bench: &Bench) { 26 | if !bench.stack_input.is_empty() { 27 | compiler.inspect_stack_length(true); 28 | } 29 | let _ = compiler.translate(Some(bench.name), &bench.bytecode, SPEC_ID).unwrap(); 30 | 31 | let out_dir = out_dir(); 32 | let obj = out_dir.join(bench.name).with_extension(".o"); 33 | compiler.write_object_to_file(&obj).unwrap(); 34 | 35 | // TODO: Clear envs. 36 | /* 37 | let so = out_dir.join(bench.name).with_extension(".so"); 38 | Linker::new().link(&so, &[&obj]).unwrap(); 39 | assert!(so.exists()); 40 | */ 41 | } 42 | 43 | fn run_time_setup(name: &str) -> Box { 44 | with_llvm_context(|cx| { 45 | let backend = EvmLlvmBackend::new(cx, false, OptimizationLevel::Aggressive).unwrap(); 46 | run_time_setup_inner(&mut EvmCompiler::new(backend), &get_bench(name).expect(name)) 47 | }) 48 | } 49 | 50 | fn run_time_setup_inner( 51 | compiler: &mut EvmCompiler, 52 | bench: &Bench, 53 | ) -> Box { 54 | let Bench { name, bytecode, calldata, stack_input, .. } = bench; 55 | 56 | let stack_input = stack_input.clone(); 57 | if !stack_input.is_empty() { 58 | compiler.inspect_stack_length(true); 59 | } 60 | let f = compiler.jit(Some(name), bytecode, SPEC_ID).unwrap(); 61 | 62 | let mut env = Env::default(); 63 | env.tx.data = calldata.clone().into(); 64 | 65 | let bytecode = revm_interpreter::analysis::to_analysed(revm_primitives::Bytecode::new_raw( 66 | revm_primitives::Bytes::copy_from_slice(&bytecode), 67 | )); 68 | let contract = revm_interpreter::Contract::new_env(&env, bytecode, None); 69 | let mut host = revm_interpreter::DummyHost::new(env); 70 | 71 | let mut interpreter = revm_interpreter::Interpreter::new(contract, GAS_LIMIT, false); 72 | 73 | Box::new(move || { 74 | let (mut ecx, stack, stack_len) = 75 | EvmContext::from_interpreter_with_stack(&mut interpreter, &mut host); 76 | 77 | for (i, input) in stack_input.iter().enumerate() { 78 | stack.as_mut_slice()[i] = input.into(); 79 | } 80 | *stack_len = stack_input.len(); 81 | 82 | let r = unsafe { f.call_noinline(Some(stack), Some(stack_len), &mut ecx) }; 83 | black_box(r); 84 | }) 85 | } 86 | 87 | macro_rules! make_benchmarks { 88 | ($($name:ident),*) => { 89 | #[library_benchmark] 90 | $( 91 | #[bench::$name(stringify!($name))] 92 | )* 93 | fn compile_time(name: &str) { 94 | crate::compile_time_impl(name); 95 | } 96 | 97 | #[library_benchmark] 98 | $( 99 | #[bench::$name(run_time_setup(stringify!($name)))] 100 | )* 101 | fn run_time(f: Box) { 102 | f(); 103 | } 104 | 105 | library_benchmark_group!( 106 | name = all; 107 | benchmarks = 108 | // compile_time, 109 | run_time 110 | ); 111 | 112 | main!(library_benchmark_groups = all); 113 | }; 114 | } 115 | 116 | make_benchmarks!(fibonacci, counter, snailtracer, push0_proxy, weth, hash_10k); 117 | -------------------------------------------------------------------------------- /crates/revmc-cli/build.rs: -------------------------------------------------------------------------------- 1 | #![allow(missing_docs)] 2 | 3 | fn main() { 4 | revmc_build::emit(); 5 | } 6 | -------------------------------------------------------------------------------- /crates/revmc-cli/src/benches.rs: -------------------------------------------------------------------------------- 1 | use revmc::{interpreter::opcode as op, primitives::hex, U256}; 2 | use std::hint::black_box; 3 | 4 | macro_rules! include_code_str { 5 | ($path:literal) => { 6 | crate::read_code_string( 7 | include_bytes!($path), 8 | std::path::Path::new($path).extension().and_then(|s| s.to_str()), 9 | ) 10 | }; 11 | } 12 | 13 | #[derive(Clone, Debug, Default)] 14 | pub struct Bench { 15 | pub name: &'static str, 16 | pub bytecode: Vec, 17 | pub calldata: Vec, 18 | pub stack_input: Vec, 19 | pub native: Option, 20 | } 21 | 22 | pub fn get_bench(name: &str) -> Option { 23 | get_benches().into_iter().find(|b| b.name == name) 24 | } 25 | 26 | pub fn get_benches() -> Vec { 27 | vec![ 28 | Bench { 29 | name: "fibonacci", 30 | bytecode: FIBONACCI.to_vec(), 31 | stack_input: vec![U256::from(69)], 32 | native: Some(|| { 33 | black_box(fibonacci_rust(black_box(U256::from(70)))); 34 | }), 35 | ..Default::default() 36 | }, 37 | // https://github.com/lambdaclass/evm_mlir/blob/b766d0bbc2093bbfa4feb3aa25baf82b512aee74/bench/revm_comparison/src/lib.rs#L12-L15 38 | // https://blog.lambdaclass.com/evm-performance-boosts-with-mlir/ 39 | // > We chose 1000 as N 40 | Bench { 41 | name: "fibonacci-calldata", 42 | bytecode: hex!( 43 | "5f355f60015b8215601a578181019150909160019003916005565b9150505f5260205ff3" 44 | ) 45 | .to_vec(), 46 | calldata: U256::from(1000).to_be_bytes_vec(), 47 | ..Default::default() 48 | }, 49 | Bench { 50 | name: "factorial", 51 | bytecode: hex!( 52 | "5f355f60015b8215601b57906001018091029160019003916005565b9150505f5260205ff3" 53 | ) 54 | .to_vec(), 55 | calldata: U256::from(1000).to_be_bytes_vec(), 56 | ..Default::default() 57 | }, 58 | Bench { 59 | name: "counter", 60 | bytecode: include_code_str!("../../../data/counter.rt.hex").unwrap(), 61 | // `increment()` 62 | calldata: hex!("d09de08a").to_vec(), 63 | ..Default::default() 64 | }, 65 | Bench { 66 | name: "counter-eof", 67 | bytecode: include_code_str!("../../../data/counter-eof.rt.hex").unwrap(), 68 | // `increment()` 69 | calldata: hex!("d09de08a").to_vec(), 70 | ..Default::default() 71 | }, 72 | Bench { 73 | name: "snailtracer", 74 | bytecode: include_code_str!("../../../data/snailtracer.rt.hex").unwrap(), 75 | // `Benchmark()` 76 | calldata: hex!("30627b7c").to_vec(), 77 | ..Default::default() 78 | }, 79 | Bench { 80 | name: "snailtracer-eof", 81 | bytecode: include_code_str!("../../../data/snailtracer-eof.rt.hex").unwrap(), 82 | // `Benchmark()` 83 | calldata: hex!("30627b7c").to_vec(), 84 | ..Default::default() 85 | }, 86 | Bench { 87 | name: "weth", 88 | bytecode: include_code_str!("../../../data/weth.rt.hex").unwrap(), 89 | ..Default::default() 90 | }, 91 | Bench { 92 | name: "hash_10k", 93 | bytecode: include_code_str!("../../../data/hash_10k.rt.hex").unwrap(), 94 | // `Benchmark()` 95 | calldata: hex!("30627b7c").to_vec(), 96 | ..Default::default() 97 | }, 98 | Bench { 99 | name: "hash_10k-eof", 100 | bytecode: include_code_str!("../../../data/hash_10k-eof.rt.hex").unwrap(), 101 | // `Benchmark()` 102 | calldata: hex!("30627b7c").to_vec(), 103 | ..Default::default() 104 | }, 105 | Bench { 106 | name: "erc20_transfer", 107 | bytecode: include_code_str!("../../../data/erc20_transfer.rt.hex").unwrap(), 108 | // `Benchmark()` 109 | calldata: hex!("30627b7c").to_vec(), 110 | ..Default::default() 111 | }, 112 | Bench { 113 | name: "push0_proxy", 114 | bytecode: include_code_str!("../../../data/push0_proxy.rt.hex").unwrap(), 115 | ..Default::default() 116 | }, 117 | Bench { 118 | name: "usdc_proxy", 119 | bytecode: include_code_str!("../../../data/usdc_proxy.rt.hex").unwrap(), 120 | ..Default::default() 121 | }, 122 | Bench { 123 | name: "fiat_token", 124 | bytecode: include_code_str!("../../../data/fiat_token.rt.hex").unwrap(), 125 | ..Default::default() 126 | }, 127 | Bench { 128 | name: "uniswap_v2_pair", 129 | bytecode: include_code_str!("../../../data/uniswap_v2_pair.rt.hex").unwrap(), 130 | ..Default::default() 131 | }, 132 | Bench { 133 | name: "seaport", 134 | bytecode: include_code_str!("../../../data/seaport.rt.hex").unwrap(), 135 | ..Default::default() 136 | }, 137 | Bench { 138 | name: "airdrop", 139 | bytecode: include_code_str!("../../../data/airdrop.rt.hex").unwrap(), 140 | // `paused()` 141 | calldata: hex!("5c975abb").to_vec(), 142 | ..Default::default() 143 | }, 144 | Bench { 145 | name: "bswap64", 146 | bytecode: include_code_str!("../../../data/bswap64.rt.hex").unwrap(), 147 | // `to_little_endian_64(uint64 = 0x0102)` returns (bytes) 148 | calldata: hex!( 149 | "ff2f79f10000000000000000000000000000000000000000000000000000000000000102" 150 | ) 151 | .to_vec(), 152 | ..Default::default() 153 | }, 154 | Bench { 155 | name: "bswap64_opt", 156 | bytecode: include_code_str!("../../../data/bswap64_opt.rt.hex").unwrap(), 157 | // `to_little_endian_64(uint64 = 0x0102)` returns (bytes) 158 | calldata: hex!( 159 | "ff2f79f10000000000000000000000000000000000000000000000000000000000000102" 160 | ) 161 | .to_vec(), 162 | ..Default::default() 163 | }, 164 | ] 165 | } 166 | 167 | #[rustfmt::skip] 168 | const FIBONACCI: &[u8] = &[ 169 | // input to the program (which fib number we want) 170 | // op::PUSH2, input[0], input[1], 171 | op::JUMPDEST, op::JUMPDEST, op::JUMPDEST, 172 | 173 | // 1st/2nd fib number 174 | op::PUSH1, 0, 175 | op::PUSH1, 1, 176 | // 7 177 | 178 | // MAINLOOP: 179 | op::JUMPDEST, 180 | op::DUP3, 181 | op::ISZERO, 182 | op::PUSH1, 28, // cleanup 183 | op::JUMPI, 184 | 185 | // fib step 186 | op::DUP2, 187 | op::DUP2, 188 | op::ADD, 189 | op::SWAP2, 190 | op::POP, 191 | op::SWAP1, 192 | // 19 193 | 194 | // decrement fib step counter 195 | op::SWAP2, 196 | op::PUSH1, 1, 197 | op::SWAP1, 198 | op::SUB, 199 | op::SWAP2, 200 | op::PUSH1, 7, // goto MAINLOOP 201 | op::JUMP, 202 | // 28 203 | 204 | // CLEANUP: 205 | op::JUMPDEST, 206 | op::SWAP2, 207 | op::POP, 208 | op::POP, 209 | // done: requested fib number is the only element on the stack! 210 | op::STOP, 211 | ]; 212 | 213 | /// Literal translation of the `FIBONACCI` EVM bytecode to Rust. 214 | pub fn fibonacci_rust(mut i: U256) -> U256 { 215 | let mut a = U256::from(0); 216 | let mut b = U256::from(1); 217 | while i != U256::ZERO { 218 | let tmp = a; 219 | a = b; 220 | b = b.wrapping_add(tmp); 221 | i -= U256::from(1); 222 | } 223 | a 224 | } 225 | 226 | #[test] 227 | fn test_fibonacci_rust() { 228 | revm_primitives::uint! { 229 | assert_eq!(fibonacci_rust(0_U256), 0_U256); 230 | assert_eq!(fibonacci_rust(1_U256), 1_U256); 231 | assert_eq!(fibonacci_rust(2_U256), 1_U256); 232 | assert_eq!(fibonacci_rust(3_U256), 2_U256); 233 | assert_eq!(fibonacci_rust(4_U256), 3_U256); 234 | assert_eq!(fibonacci_rust(5_U256), 5_U256); 235 | assert_eq!(fibonacci_rust(6_U256), 8_U256); 236 | assert_eq!(fibonacci_rust(7_U256), 13_U256); 237 | assert_eq!(fibonacci_rust(8_U256), 21_U256); 238 | assert_eq!(fibonacci_rust(9_U256), 34_U256); 239 | assert_eq!(fibonacci_rust(10_U256), 55_U256); 240 | assert_eq!(fibonacci_rust(100_U256), 354224848179261915075_U256); 241 | assert_eq!(fibonacci_rust(1000_U256), 0x2e3510283c1d60b00930b7e8803c312b4c8e6d5286805fc70b594dc75cc0604b_U256); 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /crates/revmc-cli/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(missing_docs)] 2 | 3 | use revm_interpreter::OpCode; 4 | use revm_primitives::hex; 5 | use revmc::{ 6 | eyre::{bail, eyre, Result, WrapErr}, 7 | U256, 8 | }; 9 | use std::{cmp::Ordering, path::Path, str::FromStr}; 10 | 11 | mod benches; 12 | pub use benches::*; 13 | 14 | pub fn read_code(code: Option<&str>, code_path: Option<&Path>) -> Result> { 15 | if let Some(code) = code { 16 | return read_code_string(code.trim().as_bytes(), None); 17 | } 18 | 19 | if let Some(code_path) = code_path { 20 | let contents = std::fs::read(code_path)?; 21 | let ext = code_path.extension().and_then(|s| s.to_str()); 22 | return read_code_string(&contents, ext); 23 | } 24 | 25 | Err(eyre!("one of --code, --code-path is required when argument is 'custom'")) 26 | } 27 | 28 | pub fn read_code_string(contents: &[u8], ext: Option<&str>) -> Result> { 29 | let has_prefix = contents.starts_with(b"0x") || contents.starts_with(b"0X"); 30 | let is_hex = ext != Some("bin") && (ext == Some("hex") || has_prefix); 31 | let utf8 = 32 | || std::str::from_utf8(contents).wrap_err("given code is not valid UTF-8").map(str::trim); 33 | if is_hex { 34 | let input = utf8()?; 35 | let mut lines = input.lines().map(str::trim); 36 | let first_line = lines.next().unwrap_or_default(); 37 | hex::decode(first_line).wrap_err("given code is not valid hex") 38 | } else if ext == Some("bin") || !contents.is_ascii() { 39 | Ok(contents.to_vec()) 40 | } else if ext == Some("evm") { 41 | parse_evm_dsl(utf8()?) 42 | } else if contents.is_ascii() { 43 | let s = utf8()?; 44 | parse_evm_dsl(s).or_else(|_| hex::decode(s).wrap_err("given code is not valid hex")) 45 | } else { 46 | Err(eyre!("could not determine bytecode type")) 47 | } 48 | } 49 | 50 | /// Parse EVM code from a string. 51 | fn parse_evm_dsl(s: &str) -> Result> { 52 | const COM: char = ';'; 53 | 54 | let mut code = Vec::with_capacity(32); 55 | 56 | let lines = s.lines().map(str::trim).filter(|s| !s.is_empty()); 57 | let words = lines.flat_map(|s| s.split_whitespace().take_while(|s| !s.starts_with(COM))); 58 | let mut words = words.peekable(); 59 | while let Some(word) = words.next() { 60 | if word == "PUSH" { 61 | let next = words.next().ok_or_else(|| eyre!("missing immediate for opcode PUSH"))?; 62 | let imm_bytes = parse_imm(next, None)?; 63 | code.push(OpCode::PUSH0.get() + imm_bytes.len() as u8); 64 | code.extend_from_slice(&imm_bytes); 65 | } else { 66 | let op = OpCode::parse(word).ok_or_else(|| eyre!("invalid opcode: {word:?}"))?; 67 | code.push(op.get()); 68 | let imm_len = op.info().immediate_size(); 69 | if imm_len > 0 { 70 | let imm = words.next().ok_or_else(|| eyre!("missing immediate for opcode {op}"))?; 71 | let imm_bytes = parse_imm(imm, Some(imm_len))?; 72 | code.extend_from_slice(&imm_bytes); 73 | } else if let Some(next) = words.peek() { 74 | if U256::from_str(next).is_ok() { 75 | bail!("unexpected immediate for opcode {op}"); 76 | } 77 | } 78 | } 79 | } 80 | 81 | Ok(code) 82 | } 83 | 84 | fn parse_imm(s: &str, size: Option) -> Result> { 85 | let num: U256 = s.parse().wrap_err("failed to parse immediate")?; 86 | let mut imm_bytes = num.to_be_bytes_trimmed_vec(); 87 | if let Some(size) = size { 88 | debug_assert!(size <= 32); 89 | match imm_bytes.len().cmp(&(size as usize)) { 90 | Ordering::Less => { 91 | let extend = size as usize - imm_bytes.len(); 92 | imm_bytes.splice(0..0, std::iter::repeat(0).take(extend)); 93 | } 94 | Ordering::Equal => {} 95 | Ordering::Greater => { 96 | bail!("expected at most {size} immediate bytes, got {}", imm_bytes.len()) 97 | } 98 | } 99 | } 100 | debug_assert!(imm_bytes.len() <= 32); 101 | Ok(imm_bytes) 102 | } 103 | 104 | #[cfg(test)] 105 | mod tests { 106 | use super::*; 107 | use revm_interpreter::opcode as op; 108 | 109 | #[test] 110 | fn test_evm_dsl() { 111 | let cases: &[(&str, Vec)] = &[ 112 | ("ADD ; ADD\n ADD", vec![op::ADD, op::ADD]), 113 | ("PUSH1 0", vec![op::PUSH1, 0]), 114 | ("PUSH3 0x000069", vec![op::PUSH3, 0, 0, 0x69]), 115 | ("PUSH3 0x69 ; padded", vec![op::PUSH3, 0, 0, 0x69]), 116 | ("PUSH 0", vec![op::PUSH0]), 117 | ("PUSH 1", vec![op::PUSH1, 1]), 118 | ("PUSH 2", vec![op::PUSH1, 2]), 119 | ("PUSH 69", vec![op::PUSH1, 69]), 120 | ("PUSH 0x2222", vec![op::PUSH2, 0x22, 0x22]), 121 | ]; 122 | for (s, expected) in cases.iter() { 123 | let code = match parse_evm_dsl(s) { 124 | Ok(code) => code, 125 | Err(e) => panic!("code: {s:?}\n\n err: {e}"), 126 | }; 127 | assert_eq!(code, *expected, "{s:?}"); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /crates/revmc-cli/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(missing_docs)] 2 | 3 | use clap::{Parser, ValueEnum}; 4 | use color_eyre::{eyre::eyre, Result}; 5 | use revm_interpreter::{opcode::make_instruction_table, SharedMemory}; 6 | use revm_primitives::{address, spec_to_generic, Env, SpecId, TransactTo}; 7 | use revmc::{eyre::ensure, EvmCompiler, EvmContext, EvmLlvmBackend, OptimizationLevel}; 8 | use revmc_cli::{get_benches, read_code, Bench}; 9 | use std::{ 10 | hint::black_box, 11 | path::{Path, PathBuf}, 12 | }; 13 | 14 | #[derive(Parser)] 15 | struct Cli { 16 | /// Benchmark name, "custom", path to a file, or a symbol to load from a shared object. 17 | bench_name: String, 18 | #[arg(default_value = "1")] 19 | n_iters: u64, 20 | 21 | #[arg(long)] 22 | code: Option, 23 | #[arg(long, conflicts_with = "code")] 24 | code_path: Option, 25 | #[arg(long)] 26 | calldata: Option, 27 | 28 | /// Load a shared object file instead of JIT compiling. 29 | /// 30 | /// Use with `--aot` to also run the compiled library. 31 | #[arg(long)] 32 | load: Option>, 33 | 34 | /// Parse the bytecode only. 35 | #[arg(long)] 36 | parse_only: bool, 37 | 38 | /// Compile and link to a shared library. 39 | #[arg(long)] 40 | aot: bool, 41 | 42 | /// Interpret the code instead of compiling. 43 | #[arg(long, conflicts_with = "aot")] 44 | interpret: bool, 45 | 46 | /// Target triple. 47 | #[arg(long, default_value = "native")] 48 | target: String, 49 | /// Target CPU. 50 | #[arg(long)] 51 | target_cpu: Option, 52 | /// Target features. 53 | #[arg(long)] 54 | target_features: Option, 55 | 56 | /// Compile only, do not link. 57 | #[arg(long, requires = "aot")] 58 | no_link: bool, 59 | 60 | #[arg(short = 'o', long)] 61 | out_dir: Option, 62 | #[arg(short = 'O', long, default_value = "3")] 63 | opt_level: OptimizationLevel, 64 | #[arg(long, value_enum, default_value = "osaka")] 65 | spec_id: SpecIdValueEnum, 66 | /// Short-hand for `--spec-id osaka`. 67 | #[arg(long, conflicts_with = "spec_id")] 68 | eof: bool, 69 | /// Skip validating EOF code. 70 | #[arg(long, requires = "eof")] 71 | no_validate: bool, 72 | #[arg(long)] 73 | debug_assertions: bool, 74 | #[arg(long)] 75 | no_gas: bool, 76 | #[arg(long)] 77 | no_len_checks: bool, 78 | #[arg(long, default_value = "1000000000")] 79 | gas_limit: u64, 80 | } 81 | 82 | fn main() -> Result<()> { 83 | if std::env::var_os("RUST_BACKTRACE").is_none() { 84 | std::env::set_var("RUST_BACKTRACE", "1"); 85 | } 86 | let _ = color_eyre::install(); 87 | let _ = init_tracing_subscriber(); 88 | 89 | let cli = Cli::parse(); 90 | 91 | // Build the compiler. 92 | let context = revmc::llvm::inkwell::context::Context::create(); 93 | let target = revmc::Target::new(cli.target, cli.target_cpu, cli.target_features); 94 | let backend = EvmLlvmBackend::new_for_target(&context, cli.aot, cli.opt_level, &target)?; 95 | let mut compiler = EvmCompiler::new(backend); 96 | compiler.set_dump_to(cli.out_dir); 97 | compiler.gas_metering(!cli.no_gas); 98 | unsafe { compiler.stack_bound_checks(!cli.no_len_checks) }; 99 | compiler.frame_pointers(true); 100 | compiler.debug_assertions(cli.debug_assertions); 101 | compiler.validate_eof(!cli.no_validate); 102 | 103 | let Bench { name, bytecode, calldata, stack_input, native: _ } = if cli.bench_name == "custom" { 104 | Bench { 105 | name: "custom", 106 | bytecode: read_code(cli.code.as_deref(), cli.code_path.as_deref())?, 107 | ..Default::default() 108 | } 109 | } else if Path::new(&cli.bench_name).exists() { 110 | let path = Path::new(&cli.bench_name); 111 | ensure!(path.is_file(), "argument must be a file"); 112 | ensure!(cli.code.is_none(), "--code is not allowed with a file argument"); 113 | ensure!(cli.code_path.is_none(), "--code-path is not allowed with a file argument"); 114 | Bench { 115 | name: path.file_stem().unwrap().to_str().unwrap().to_string().leak(), 116 | bytecode: read_code(None, Some(path))?, 117 | ..Default::default() 118 | } 119 | } else { 120 | match get_benches().into_iter().find(|b| b.name == cli.bench_name) { 121 | Some(b) => b, 122 | None => { 123 | if cli.load.is_some() { 124 | Bench { 125 | name: cli.bench_name.clone().leak(), 126 | bytecode: Vec::new(), 127 | ..Default::default() 128 | } 129 | } else { 130 | return Err(eyre!("unknown benchmark: {}", cli.bench_name)); 131 | } 132 | } 133 | } 134 | }; 135 | compiler.set_module_name(name); 136 | 137 | let calldata = if let Some(calldata) = cli.calldata { 138 | revmc::primitives::hex::decode(calldata)?.into() 139 | } else { 140 | calldata.into() 141 | }; 142 | let gas_limit = cli.gas_limit; 143 | 144 | let mut env = Env::default(); 145 | env.tx.caller = address!("0000000000000000000000000000000000000001"); 146 | env.tx.transact_to = TransactTo::Call(address!("0000000000000000000000000000000000000002")); 147 | env.tx.data = calldata; 148 | env.tx.gas_limit = gas_limit; 149 | 150 | let bytecode = revm_interpreter::analysis::to_analysed(revm_primitives::Bytecode::new_raw( 151 | revm_primitives::Bytes::copy_from_slice(&bytecode), 152 | )); 153 | let contract = revm_interpreter::Contract::new_env(&env, bytecode, None); 154 | let mut host = revm_interpreter::DummyHost::new(env); 155 | 156 | let bytecode = contract.bytecode.original_byte_slice(); 157 | 158 | let spec_id = if cli.eof { SpecId::OSAKA } else { cli.spec_id.into() }; 159 | if !stack_input.is_empty() { 160 | compiler.inspect_stack_length(true); 161 | } 162 | 163 | if cli.parse_only { 164 | let _ = compiler.parse(bytecode.into(), spec_id)?; 165 | return Ok(()); 166 | } 167 | 168 | let f_id = compiler.translate(name, bytecode, spec_id)?; 169 | 170 | let mut load = cli.load; 171 | if cli.aot { 172 | let out_dir = if let Some(out_dir) = compiler.out_dir() { 173 | out_dir.join(cli.bench_name) 174 | } else { 175 | let dir = std::env::temp_dir().join("revmc-cli").join(cli.bench_name); 176 | std::fs::create_dir_all(&dir)?; 177 | dir 178 | }; 179 | 180 | // Compile. 181 | let obj = out_dir.join("a.o"); 182 | compiler.write_object_to_file(&obj)?; 183 | ensure!(obj.exists(), "Failed to write object file"); 184 | eprintln!("Compiled object file to {}", obj.display()); 185 | 186 | // Link. 187 | if !cli.no_link { 188 | let so = out_dir.join("a.so"); 189 | let linker = revmc::Linker::new(); 190 | linker.link(&so, [obj.to_str().unwrap()])?; 191 | ensure!(so.exists(), "Failed to link object file"); 192 | eprintln!("Linked shared object file to {}", so.display()); 193 | } 194 | 195 | // Fall through to loading the library below if requested. 196 | if let Some(load @ None) = &mut load { 197 | *load = Some(out_dir.join("a.so")); 198 | } else { 199 | return Ok(()); 200 | } 201 | } 202 | 203 | let lib; 204 | let f = if let Some(load) = load { 205 | if let Some(load) = load { 206 | lib = unsafe { libloading::Library::new(load) }?; 207 | let f: libloading::Symbol<'_, revmc::EvmCompilerFn> = 208 | unsafe { lib.get(name.as_bytes())? }; 209 | *f 210 | } else { 211 | return Err(eyre!("--load with no argument requires --aot")); 212 | } 213 | } else { 214 | unsafe { compiler.jit_function(f_id)? } 215 | }; 216 | 217 | #[allow(unused_parens)] 218 | let table = spec_to_generic!(spec_id, (const { &make_instruction_table::<_, SPEC>() })); 219 | let mut run = |f: revmc::EvmCompilerFn| { 220 | let mut interpreter = 221 | revm_interpreter::Interpreter::new(contract.clone(), gas_limit, false); 222 | host.clear(); 223 | 224 | if cli.interpret { 225 | let action = interpreter.run(SharedMemory::new(), table, &mut host); 226 | (interpreter.instruction_result, action) 227 | } else { 228 | let (mut ecx, stack, stack_len) = 229 | EvmContext::from_interpreter_with_stack(&mut interpreter, &mut host); 230 | 231 | for (i, input) in stack_input.iter().enumerate() { 232 | stack.as_mut_slice()[i] = input.into(); 233 | } 234 | *stack_len = stack_input.len(); 235 | 236 | let r = unsafe { f.call_noinline(Some(stack), Some(stack_len), &mut ecx) }; 237 | (r, interpreter.next_action) 238 | } 239 | }; 240 | 241 | if cli.n_iters == 0 { 242 | return Ok(()); 243 | } 244 | 245 | let (ret, action) = run(f); 246 | println!("InstructionResult::{ret:?}"); 247 | println!("InterpreterAction::{action:#?}"); 248 | 249 | if cli.n_iters > 1 { 250 | bench(cli.n_iters, name, || run(f)); 251 | return Ok(()); 252 | } 253 | 254 | Ok(()) 255 | } 256 | 257 | fn bench(n_iters: u64, name: &str, mut f: impl FnMut() -> T) { 258 | let warmup = (n_iters / 10).max(10); 259 | for _ in 0..warmup { 260 | black_box(f()); 261 | } 262 | 263 | let t = std::time::Instant::now(); 264 | for _ in 0..n_iters { 265 | black_box(f()); 266 | } 267 | let d = t.elapsed(); 268 | eprintln!("{name}: {:>9?} ({d:>12?} / {n_iters})", d / n_iters as u32); 269 | } 270 | 271 | fn init_tracing_subscriber() -> Result<(), tracing_subscriber::util::TryInitError> { 272 | use tracing_subscriber::prelude::*; 273 | let registry = tracing_subscriber::Registry::default() 274 | .with(tracing_subscriber::EnvFilter::from_default_env()); 275 | #[cfg(feature = "tracy")] 276 | let registry = registry.with(tracing_tracy::TracyLayer::default()); 277 | registry.with(tracing_subscriber::fmt::layer()).try_init() 278 | } 279 | 280 | #[derive(Clone, Copy, Debug, ValueEnum)] 281 | #[clap(rename_all = "lowercase")] 282 | #[allow(non_camel_case_types)] 283 | pub enum SpecIdValueEnum { 284 | FRONTIER, 285 | FRONTIER_THAWING, 286 | HOMESTEAD, 287 | DAO_FORK, 288 | TANGERINE, 289 | SPURIOUS_DRAGON, 290 | BYZANTIUM, 291 | CONSTANTINOPLE, 292 | PETERSBURG, 293 | ISTANBUL, 294 | MUIR_GLACIER, 295 | BERLIN, 296 | LONDON, 297 | ARROW_GLACIER, 298 | GRAY_GLACIER, 299 | MERGE, 300 | SHANGHAI, 301 | CANCUN, 302 | PRAGUE, 303 | OSAKA, 304 | LATEST, 305 | } 306 | 307 | impl From for SpecId { 308 | fn from(v: SpecIdValueEnum) -> Self { 309 | match v { 310 | SpecIdValueEnum::FRONTIER => Self::FRONTIER, 311 | SpecIdValueEnum::FRONTIER_THAWING => Self::FRONTIER_THAWING, 312 | SpecIdValueEnum::HOMESTEAD => Self::HOMESTEAD, 313 | SpecIdValueEnum::DAO_FORK => Self::DAO_FORK, 314 | SpecIdValueEnum::TANGERINE => Self::TANGERINE, 315 | SpecIdValueEnum::SPURIOUS_DRAGON => Self::SPURIOUS_DRAGON, 316 | SpecIdValueEnum::BYZANTIUM => Self::BYZANTIUM, 317 | SpecIdValueEnum::CONSTANTINOPLE => Self::CONSTANTINOPLE, 318 | SpecIdValueEnum::PETERSBURG => Self::PETERSBURG, 319 | SpecIdValueEnum::ISTANBUL => Self::ISTANBUL, 320 | SpecIdValueEnum::MUIR_GLACIER => Self::MUIR_GLACIER, 321 | SpecIdValueEnum::BERLIN => Self::BERLIN, 322 | SpecIdValueEnum::LONDON => Self::LONDON, 323 | SpecIdValueEnum::ARROW_GLACIER => Self::ARROW_GLACIER, 324 | SpecIdValueEnum::GRAY_GLACIER => Self::GRAY_GLACIER, 325 | SpecIdValueEnum::MERGE => Self::MERGE, 326 | SpecIdValueEnum::SHANGHAI => Self::SHANGHAI, 327 | SpecIdValueEnum::CANCUN => Self::CANCUN, 328 | SpecIdValueEnum::PRAGUE => Self::PRAGUE, 329 | SpecIdValueEnum::OSAKA => Self::OSAKA, 330 | SpecIdValueEnum::LATEST => Self::LATEST, 331 | } 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /crates/revmc-cli/tests.rs: -------------------------------------------------------------------------------- 1 | #![allow(missing_docs, unused_crate_dependencies)] 2 | 3 | const CMD: &str = env!("CARGO_BIN_EXE_revmc-cli"); 4 | 5 | fn main() { 6 | let code = revmc_cli_tests::run_tests(CMD.as_ref()); 7 | std::process::exit(code); 8 | } 9 | -------------------------------------------------------------------------------- /crates/revmc-context/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.1.0](https://github.com/paradigmxyz/revmc/releases/tag/v0.1.0) - 2024-06-27 9 | 10 | ### Documentation 11 | 12 | - Add changelogs 13 | 14 | ### Features 15 | 16 | - Improve DX around statically linked bytecodes, add example ([#37](https://github.com/paradigmxyz/revmc/issues/37)) 17 | 18 | ### Miscellaneous Tasks 19 | 20 | - Rebrand to `revmc` ([#33](https://github.com/paradigmxyz/revmc/issues/33)) 21 | 22 | ### Styling 23 | 24 | - Clippy, add more lints ([#34](https://github.com/paradigmxyz/revmc/issues/34)) 25 | 26 | 27 | -------------------------------------------------------------------------------- /crates/revmc-context/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "revmc-context" 3 | description = "EVM bytecode compiler runtime context" 4 | homepage = "https://github.com/danipopes/revmc/tree/main/crates/revmc-context" 5 | 6 | version.workspace = true 7 | authors.workspace = true 8 | edition.workspace = true 9 | rust-version.workspace = true 10 | license.workspace = true 11 | categories.workspace = true 12 | keywords.workspace = true 13 | repository.workspace = true 14 | exclude.workspace = true 15 | 16 | [lints] 17 | workspace = true 18 | 19 | [dependencies] 20 | revm-interpreter.workspace = true 21 | revm-primitives.workspace = true 22 | 23 | [features] 24 | default = ["std"] 25 | std = ["revm-interpreter/std", "revm-primitives/std"] 26 | host-ext-any = [] 27 | -------------------------------------------------------------------------------- /crates/revmc-context/README.md: -------------------------------------------------------------------------------- 1 | # revmc-context 2 | 3 | EVM bytecode compiler runtime context. 4 | -------------------------------------------------------------------------------- /crates/revmc-cranelift/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.1.0](https://github.com/paradigmxyz/revmc/releases/tag/v0.1.0) - 2024-06-27 9 | 10 | ### Documentation 11 | 12 | - Add changelogs 13 | 14 | ### Features 15 | 16 | - Implement IR builtins ([#36](https://github.com/paradigmxyz/revmc/issues/36)) 17 | 18 | ### Miscellaneous Tasks 19 | 20 | - Rebrand to `revmc` ([#33](https://github.com/paradigmxyz/revmc/issues/33)) 21 | 22 | ### Styling 23 | 24 | - Clippy, add more lints ([#34](https://github.com/paradigmxyz/revmc/issues/34)) 25 | 26 | 27 | -------------------------------------------------------------------------------- /crates/revmc-cranelift/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "revmc-cranelift" 3 | description = "EVM bytecode compiler Cranelift backend" 4 | homepage = "https://github.com/danipopes/revmc/tree/main/crates/revmc-cranelift" 5 | 6 | version.workspace = true 7 | authors.workspace = true 8 | edition.workspace = true 9 | rust-version.workspace = true 10 | license.workspace = true 11 | categories.workspace = true 12 | keywords.workspace = true 13 | repository.workspace = true 14 | exclude.workspace = true 15 | 16 | [lints] 17 | workspace = true 18 | 19 | [dependencies] 20 | revmc-backend.workspace = true 21 | 22 | cranelift = "0.116" 23 | cranelift-jit = "0.116" 24 | cranelift-module = "0.116" 25 | cranelift-native = "0.116" 26 | cranelift-object = "0.116" 27 | -------------------------------------------------------------------------------- /crates/revmc-cranelift/README.md: -------------------------------------------------------------------------------- 1 | # revmc-cranelift 2 | 3 | EVM bytecode compiler [Cranelift] backend. 4 | 5 | This crate is currently not functional due to missing `i256` support in Cranelift. 6 | 7 | [Cranelift]: https://cranelift.dev/ 8 | -------------------------------------------------------------------------------- /crates/revmc-cranelift/src/pretty_clif.rs: -------------------------------------------------------------------------------- 1 | //! This module provides the [`CommentWriter`] which makes it possible to add comments to the 2 | //! written cranelift IR. 3 | //! 4 | //! Modified from [`rustc_codegen_cranelift`](https://github.com/rust-lang/rustc_codegen_cranelift/blob/07633821ed63360d4d7464998c29f4f588930a03/src/pretty_clif.rs). 5 | 6 | #![allow(dead_code)] 7 | 8 | use cranelift::codegen::{ 9 | entity::SecondaryMap, 10 | ir::{entities::AnyEntity, Block, Fact, Function, Inst, Value}, 11 | isa::TargetIsa, 12 | write::{FuncWriter, PlainWriter}, 13 | }; 14 | use std::{ 15 | collections::{hash_map::Entry, HashMap}, 16 | fmt, 17 | io::Write, 18 | path::Path, 19 | }; 20 | 21 | #[derive(Clone, Debug)] 22 | pub(crate) struct CommentWriter { 23 | enabled: bool, 24 | global_comments: Vec, 25 | entity_comments: HashMap, 26 | } 27 | 28 | impl Default for CommentWriter { 29 | fn default() -> Self { 30 | Self::new() 31 | } 32 | } 33 | 34 | impl CommentWriter { 35 | pub(crate) fn new() -> Self { 36 | Self { 37 | enabled: should_write_ir(), 38 | global_comments: Vec::new(), 39 | entity_comments: HashMap::new(), 40 | } 41 | } 42 | 43 | pub(crate) fn enabled(&self) -> bool { 44 | self.enabled 45 | } 46 | 47 | pub(crate) fn clear(&mut self) { 48 | self.global_comments.clear(); 49 | self.entity_comments.clear(); 50 | } 51 | 52 | pub(crate) fn add_global_comment>(&mut self, comment: S) { 53 | debug_assert!(self.enabled); 54 | self.global_comments.push(comment.into()); 55 | } 56 | 57 | pub(crate) fn add_comment + AsRef, E: Into>( 58 | &mut self, 59 | entity: E, 60 | comment: S, 61 | ) { 62 | debug_assert!(self.enabled); 63 | 64 | match self.entity_comments.entry(entity.into()) { 65 | Entry::Occupied(mut occ) => { 66 | occ.get_mut().push('\n'); 67 | occ.get_mut().push_str(comment.as_ref()); 68 | } 69 | Entry::Vacant(vac) => { 70 | vac.insert(comment.into()); 71 | } 72 | } 73 | } 74 | } 75 | 76 | impl FuncWriter for &'_ CommentWriter { 77 | fn write_preamble( 78 | &mut self, 79 | w: &mut dyn fmt::Write, 80 | func: &Function, 81 | ) -> Result { 82 | for comment in &self.global_comments { 83 | if !comment.is_empty() { 84 | writeln!(w, "; {comment}")?; 85 | } else { 86 | writeln!(w)?; 87 | } 88 | } 89 | if !self.global_comments.is_empty() { 90 | writeln!(w)?; 91 | } 92 | 93 | self.super_preamble(w, func) 94 | } 95 | 96 | fn write_entity_definition( 97 | &mut self, 98 | w: &mut dyn fmt::Write, 99 | _func: &Function, 100 | entity: AnyEntity, 101 | value: &dyn fmt::Display, 102 | maybe_fact: Option<&Fact>, 103 | ) -> fmt::Result { 104 | if let Some(fact) = maybe_fact { 105 | write!(w, " {entity} ! {fact} = {value}")?; 106 | } else { 107 | write!(w, " {entity} = {value}")?; 108 | } 109 | 110 | if let Some(comment) = self.entity_comments.get(&entity) { 111 | writeln!(w, " ; {}", comment.replace('\n', "\n; ")) 112 | } else { 113 | writeln!(w) 114 | } 115 | } 116 | 117 | fn write_block_header( 118 | &mut self, 119 | w: &mut dyn fmt::Write, 120 | func: &Function, 121 | block: Block, 122 | indent: usize, 123 | ) -> fmt::Result { 124 | PlainWriter.write_block_header(w, func, block, indent) 125 | } 126 | 127 | fn write_instruction( 128 | &mut self, 129 | w: &mut dyn fmt::Write, 130 | func: &Function, 131 | aliases: &SecondaryMap>, 132 | inst: Inst, 133 | indent: usize, 134 | ) -> fmt::Result { 135 | PlainWriter.write_instruction(w, func, aliases, inst, indent)?; 136 | if let Some(comment) = self.entity_comments.get(&inst.into()) { 137 | writeln!(w, "; {}", comment.replace('\n', "\n; "))?; 138 | } 139 | Ok(()) 140 | } 141 | } 142 | 143 | pub(crate) fn should_write_ir() -> bool { 144 | cfg!(debug_assertions) 145 | } 146 | 147 | pub(crate) fn write_ir_file( 148 | path: &Path, 149 | write: impl FnOnce(&mut std::fs::File) -> std::io::Result<()>, 150 | ) { 151 | let res = std::fs::File::create(path).and_then(|mut file| write(&mut file)); 152 | if let Err(err) = res { 153 | panic!("{err}") 154 | } 155 | } 156 | 157 | pub(crate) fn write_clif_file( 158 | path: &Path, 159 | isa: &dyn TargetIsa, 160 | func: &Function, 161 | mut clif_comments: &CommentWriter, 162 | ) { 163 | write_ir_file(path, |file| { 164 | let mut clif = String::new(); 165 | cranelift::codegen::write::decorate_function(&mut clif_comments, &mut clif, func).unwrap(); 166 | 167 | for flag in isa.flags().iter() { 168 | writeln!(file, "set {flag}")?; 169 | } 170 | write!(file, "target {}", isa.triple().architecture)?; 171 | for isa_flag in isa.isa_flags().iter() { 172 | write!(file, " {isa_flag}")?; 173 | } 174 | writeln!(file, "\n")?; 175 | writeln!(file)?; 176 | file.write_all(clif.as_bytes())?; 177 | Ok(()) 178 | }); 179 | } 180 | -------------------------------------------------------------------------------- /crates/revmc-llvm/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.1.0](https://github.com/paradigmxyz/revmc/releases/tag/v0.1.0) - 2024-06-27 9 | 10 | ### Documentation 11 | 12 | - Add changelogs 13 | 14 | ### Features 15 | 16 | - Add target configuration ([#39](https://github.com/paradigmxyz/revmc/issues/39)) 17 | - Implement IR builtins ([#36](https://github.com/paradigmxyz/revmc/issues/36)) 18 | 19 | ### Miscellaneous Tasks 20 | 21 | - Replace timing macros with tracy ([#40](https://github.com/paradigmxyz/revmc/issues/40)) 22 | - Rebrand to `revmc` ([#33](https://github.com/paradigmxyz/revmc/issues/33)) 23 | 24 | ### Styling 25 | 26 | - Clippy, add more lints ([#34](https://github.com/paradigmxyz/revmc/issues/34)) 27 | 28 | 29 | -------------------------------------------------------------------------------- /crates/revmc-llvm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "revmc-llvm" 3 | description = "EVM bytecode compiler LLVM backend" 4 | homepage = "https://github.com/danipopes/revmc/tree/main/crates/revmc-llvm" 5 | 6 | version.workspace = true 7 | authors.workspace = true 8 | edition.workspace = true 9 | rust-version.workspace = true 10 | license.workspace = true 11 | categories.workspace = true 12 | keywords.workspace = true 13 | repository.workspace = true 14 | exclude.workspace = true 15 | 16 | [lints] 17 | workspace = true 18 | 19 | [dependencies] 20 | revmc-backend.workspace = true 21 | 22 | inkwell = { version = "0.5", features = [ "llvm18-0" ] } 23 | rustc-hash.workspace = true 24 | tracing.workspace = true 25 | 26 | [features] 27 | prefer-static = ["inkwell/llvm18-0-prefer-static"] 28 | prefer-dynamic = ["inkwell/llvm18-0-prefer-dynamic"] 29 | -------------------------------------------------------------------------------- /crates/revmc-llvm/README.md: -------------------------------------------------------------------------------- 1 | # revmc-llvm 2 | 3 | EVM bytecode compiler [LLVM] backend. 4 | 5 | [LLVM]: https://llvm.org/ 6 | -------------------------------------------------------------------------------- /crates/revmc-llvm/src/dh.rs: -------------------------------------------------------------------------------- 1 | use inkwell::{ 2 | context::{AsContextRef, Context}, 3 | llvm_sys::{core::*, prelude::*, LLVMDiagnosticHandler, LLVMDiagnosticSeverity::*}, 4 | }; 5 | use std::{ffi::c_void, fmt, ptr}; 6 | 7 | /// LLVM diagnostic handler guard. 8 | pub(crate) struct DiagnosticHandlerGuard<'ctx> { 9 | cx: &'ctx Context, 10 | prev_dh: LLVMDiagnosticHandler, 11 | prev_dhc: *mut c_void, 12 | } 13 | 14 | impl fmt::Debug for DiagnosticHandlerGuard<'_> { 15 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 16 | f.debug_struct("DiagnosticHandlerGuard").finish_non_exhaustive() 17 | } 18 | } 19 | 20 | impl<'ctx> DiagnosticHandlerGuard<'ctx> { 21 | pub(crate) fn new(cx: &'ctx Context) -> Self { 22 | unsafe { 23 | let c = cx.as_ctx_ref(); 24 | let prev_dh = LLVMContextGetDiagnosticHandler(c); 25 | let prev_dhc = LLVMContextGetDiagnosticContext(c); 26 | LLVMContextSetDiagnosticHandler(c, Some(Self::diagnostic_handler), ptr::null_mut()); 27 | Self { cx, prev_dh, prev_dhc } 28 | } 29 | } 30 | 31 | extern "C" fn diagnostic_handler(di: LLVMDiagnosticInfoRef, _context: *mut c_void) { 32 | unsafe { 33 | // `LLVMGetDiagInfoDescription` returns an LLVM `Message`. 34 | let msg_cstr = crate::llvm_string(LLVMGetDiagInfoDescription(di)); 35 | let msg = msg_cstr.to_string_lossy(); 36 | match LLVMGetDiagInfoSeverity(di) { 37 | LLVMDSError => error!(target: "llvm", "{msg}"), 38 | LLVMDSWarning => warn!(target: "llvm", "{msg}"), 39 | LLVMDSRemark => trace!(target: "llvm", "{msg}"), 40 | LLVMDSNote => debug!(target: "llvm", "{msg}"), 41 | } 42 | } 43 | } 44 | } 45 | 46 | impl Drop for DiagnosticHandlerGuard<'_> { 47 | fn drop(&mut self) { 48 | unsafe { 49 | LLVMContextSetDiagnosticHandler(self.cx.as_ctx_ref(), self.prev_dh, self.prev_dhc); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /crates/revmc-llvm/src/utils.rs: -------------------------------------------------------------------------------- 1 | use inkwell::support::LLVMString; 2 | use std::ffi::c_char; 3 | 4 | pub(crate) unsafe fn llvm_string(ptr: *const c_char) -> LLVMString { 5 | // `LLVMString::new` is private 6 | std::mem::transmute(ptr) 7 | } 8 | -------------------------------------------------------------------------------- /crates/revmc/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.1.0](https://github.com/paradigmxyz/revmc/releases/tag/v0.1.0) - 2024-06-27 9 | 10 | ### Dependencies 11 | 12 | - Bump revm 13 | 14 | ### Documentation 15 | 16 | - Add changelogs 17 | 18 | ### Features 19 | 20 | - Add target configuration ([#39](https://github.com/paradigmxyz/revmc/issues/39)) 21 | - Improve DX around statically linked bytecodes, add example ([#37](https://github.com/paradigmxyz/revmc/issues/37)) 22 | - Implement IR builtins ([#36](https://github.com/paradigmxyz/revmc/issues/36)) 23 | 24 | ### Miscellaneous Tasks 25 | 26 | - Replace timing macros with tracy ([#40](https://github.com/paradigmxyz/revmc/issues/40)) 27 | - Add release configuration 28 | - Update some comments 29 | - Rebrand to `revmc` ([#33](https://github.com/paradigmxyz/revmc/issues/33)) 30 | 31 | 32 | -------------------------------------------------------------------------------- /crates/revmc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "revmc" 3 | description = "EVM bytecode compiler" 4 | homepage = "https://github.com/danipopes/revmc/tree/main/crates/revmc" 5 | 6 | version.workspace = true 7 | authors.workspace = true 8 | edition.workspace = true 9 | rust-version.workspace = true 10 | license.workspace = true 11 | categories.workspace = true 12 | keywords.workspace = true 13 | repository.workspace = true 14 | exclude.workspace = true 15 | 16 | [package.metadata.docs.rs] 17 | no-default-features = true 18 | rustdoc-args = ["--cfg", "docsrs"] 19 | 20 | [lints] 21 | workspace = true 22 | 23 | [dependencies] 24 | revmc-backend.workspace = true 25 | revmc-builtins = { workspace = true, features = ["ir"] } 26 | revmc-context.workspace = true 27 | revmc-cranelift = { workspace = true, optional = true } 28 | revmc-llvm = { workspace = true, optional = true } 29 | 30 | alloy-primitives = { workspace = true, features = ["std"] } 31 | revm-interpreter.workspace = true 32 | revm-primitives.workspace = true 33 | 34 | bitflags = "2.5" 35 | bitvec = "1.0" 36 | either = "1.13" 37 | rustc-hash.workspace = true 38 | tracing.workspace = true 39 | 40 | arbitrary = { version = "1.3", optional = true } 41 | paste = { workspace = true, optional = true } 42 | similar-asserts = { version = "1.5", optional = true } 43 | 44 | [dev-dependencies] 45 | revmc-context = { workspace = true, features = ["host-ext-any"] } 46 | paste.workspace = true 47 | similar-asserts = "1.5" 48 | tempfile = "3.10" 49 | 50 | [features] 51 | default = ["llvm"] 52 | llvm = ["dep:revmc-llvm"] 53 | llvm-prefer-static = ["llvm", "revmc-llvm?/prefer-static"] 54 | llvm-prefer-dynamic = ["llvm", "revmc-llvm?/prefer-dynamic"] 55 | cranelift = ["dep:revmc-cranelift"] 56 | 57 | asm-keccak = ["alloy-primitives/asm-keccak"] 58 | 59 | # I don't think this is supported, but it's necessary for --all-features to work in workspaces which 60 | # also have this feature. 61 | optimism = ["revm-primitives/optimism", "revm-interpreter/optimism"] 62 | 63 | # Internal features. 64 | __fuzzing = ["dep:arbitrary", "dep:paste", "dep:similar-asserts"] 65 | -------------------------------------------------------------------------------- /crates/revmc/README.md: -------------------------------------------------------------------------------- 1 | ../../README.md -------------------------------------------------------------------------------- /crates/revmc/src/bytecode/info.rs: -------------------------------------------------------------------------------- 1 | use revm_interpreter::{gas, opcode as op}; 2 | use revm_primitives::{spec_to_generic, SpecId}; 3 | 4 | /// Opcode information. 5 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] 6 | pub struct OpcodeInfo(u16); 7 | 8 | impl OpcodeInfo { 9 | /// The unknown flag. 10 | pub const UNKNOWN: u16 = 0b1000_0000_0000_0000; 11 | /// The dynamic flag. 12 | pub const DYNAMIC: u16 = 0b0100_0000_0000_0000; 13 | /// The disabled flag. 14 | pub const DISABLED: u16 = 0b0010_0000_0000_0000; 15 | /// The EOF flag. 16 | pub const EOF: u16 = 0b0001_0000_0000_0000; 17 | /// The mask for the gas cost. 18 | pub const MASK: u16 = 0b0000_1111_1111_1111; 19 | 20 | /// Creates a new gas info with the given gas cost. 21 | #[inline] 22 | pub const fn new(gas: u16) -> Self { 23 | Self(gas) 24 | } 25 | 26 | /// Returns `true` if the opcode is unknown. 27 | #[inline] 28 | pub const fn is_unknown(self) -> bool { 29 | self.0 == Self::UNKNOWN 30 | } 31 | 32 | /// Returns `true` if the gas cost is dynamic. 33 | #[inline] 34 | pub const fn is_dynamic(self) -> bool { 35 | self.0 & Self::DYNAMIC != 0 36 | } 37 | 38 | /// Returns `true` if the opcode is known, but disabled in the current EVM 39 | /// version. 40 | #[inline] 41 | pub const fn is_disabled(self) -> bool { 42 | self.0 & Self::DISABLED != 0 43 | } 44 | 45 | /// Returns `true` if the opcode is an EOF opcode, meaning it is disallowed in 46 | /// legacy bytecode. 47 | #[inline] 48 | pub const fn is_eof_only(self) -> bool { 49 | self.0 & Self::EOF != 0 50 | } 51 | 52 | /// Returns the base gas cost of the opcode. 53 | /// 54 | /// This may not be the final/full gas cost of the opcode as it may also have a dynamic cost. 55 | #[inline] 56 | pub const fn base_gas(self) -> u16 { 57 | self.0 & Self::MASK 58 | } 59 | 60 | /// Sets the unknown flag. 61 | #[inline] 62 | pub fn set_unknown(&mut self) { 63 | self.0 = Self::UNKNOWN; 64 | } 65 | 66 | /// Sets the dynamic flag. 67 | #[inline] 68 | pub fn set_dynamic(&mut self) { 69 | self.0 |= Self::DYNAMIC; 70 | } 71 | 72 | /// Sets the disabled flag. 73 | #[inline] 74 | pub fn set_disabled(&mut self) { 75 | self.0 |= Self::DISABLED; 76 | } 77 | 78 | /// Sets the EOF flag. 79 | #[inline] 80 | pub fn set_eof(&mut self) { 81 | self.0 |= Self::EOF; 82 | } 83 | 84 | /// Sets the gas cost. 85 | /// 86 | /// # Panics 87 | /// 88 | /// Panics if the gas cost is greater than [`Self::MASK`]. 89 | #[inline] 90 | #[track_caller] 91 | pub fn set_gas(&mut self, gas: u16) { 92 | assert!(gas <= Self::MASK); 93 | self.0 = (self.0 & !Self::MASK) | (gas & Self::MASK); 94 | } 95 | } 96 | 97 | /// Returns the static info map for the given `SpecId`. 98 | #[allow(unused_parens)] 99 | pub const fn op_info_map(spec_id: SpecId) -> &'static [OpcodeInfo; 256] { 100 | spec_to_generic!(spec_id, (const { &make_map(::SPEC_ID) })) 101 | } 102 | 103 | #[allow(unused_mut)] 104 | const fn make_map(spec_id: SpecId) -> [OpcodeInfo; 256] { 105 | const DYNAMIC: u16 = OpcodeInfo::DYNAMIC; 106 | const EOF: u16 = OpcodeInfo::EOF; 107 | 108 | let mut map = [OpcodeInfo(OpcodeInfo::UNKNOWN); 256]; 109 | macro_rules! set { 110 | ($($op:ident = $gas:expr $(, if $spec_id:ident)? ;)*) => { 111 | $( 112 | let mut g = $gas; 113 | $( 114 | if (spec_id as u8) < SpecId::$spec_id as u8 { 115 | g |= OpcodeInfo::DISABLED; 116 | } 117 | )? 118 | map[op::$op as usize] = OpcodeInfo::new(g); 119 | )* 120 | }; 121 | } 122 | // [1]: Not dynamic in all `SpecId`s, but gas is calculated dynamically in a builtin. 123 | // TODO: Could be converted into [2] with a base cost. 124 | // [2]: Dynamic with a base cost. Only the dynamic part is paid in builtins. 125 | set! { 126 | STOP = 0; 127 | 128 | ADD = 3; 129 | MUL = 5; 130 | SUB = 3; 131 | DIV = 5; 132 | SDIV = 5; 133 | MOD = 5; 134 | SMOD = 5; 135 | ADDMOD = 8; 136 | MULMOD = 8; 137 | EXP = 10 | DYNAMIC; // [2] 138 | SIGNEXTEND = 5; 139 | // 0x0C 140 | // 0x0D 141 | // 0x0E 142 | // 0x0F 143 | LT = 3; 144 | GT = 3; 145 | SLT = 3; 146 | SGT = 3; 147 | EQ = 3; 148 | ISZERO = 3; 149 | AND = 3; 150 | OR = 3; 151 | XOR = 3; 152 | NOT = 3; 153 | BYTE = 3; 154 | SHL = 3, if CONSTANTINOPLE; 155 | SHR = 3, if CONSTANTINOPLE; 156 | SAR = 3, if CONSTANTINOPLE; 157 | // 0x1E 158 | // 0x1F 159 | KECCAK256 = 30 | DYNAMIC; // [2] 160 | // 0x21 161 | // 0x22 162 | // 0x23 163 | // 0x24 164 | // 0x25 165 | // 0x26 166 | // 0x27 167 | // 0x28 168 | // 0x29 169 | // 0x2A 170 | // 0x2B 171 | // 0x2C 172 | // 0x2D 173 | // 0x2E 174 | // 0x2F 175 | ADDRESS = 2; 176 | BALANCE = DYNAMIC; // [1] 177 | ORIGIN = 2; 178 | CALLER = 2; 179 | CALLVALUE = 2; 180 | CALLDATALOAD = 3; 181 | CALLDATASIZE = 2; 182 | CALLDATACOPY = 3 | DYNAMIC; // [2] 183 | CODESIZE = 2; 184 | CODECOPY = 3 | DYNAMIC; // [2] 185 | 186 | GASPRICE = 2; 187 | EXTCODESIZE = DYNAMIC; // [1] 188 | EXTCODECOPY = DYNAMIC; 189 | RETURNDATASIZE = 2, if BYZANTIUM; 190 | RETURNDATACOPY = 3 | DYNAMIC, if BYZANTIUM; // [2] 191 | EXTCODEHASH = DYNAMIC, if CONSTANTINOPLE; // [1] 192 | BLOCKHASH = 20; 193 | COINBASE = 2; 194 | TIMESTAMP = 2; 195 | NUMBER = 2; 196 | DIFFICULTY = 2; 197 | GASLIMIT = 2; 198 | CHAINID = 2, if ISTANBUL; 199 | SELFBALANCE = 5, if ISTANBUL; 200 | BASEFEE = 2, if LONDON; 201 | BLOBHASH = 3, if CANCUN; 202 | BLOBBASEFEE = 2, if CANCUN; 203 | // 0x4B 204 | // 0x4C 205 | // 0x4D 206 | // 0x4E 207 | // 0x4F 208 | POP = 2; 209 | MLOAD = 3 | DYNAMIC; // [2] 210 | MSTORE = 3 | DYNAMIC; // [2] 211 | MSTORE8 = 3 | DYNAMIC; // [2] 212 | SLOAD = DYNAMIC; // [1] 213 | SSTORE = DYNAMIC; 214 | JUMP = 8; 215 | JUMPI = 10; 216 | PC = 2; 217 | MSIZE = 2; 218 | GAS = 2; 219 | JUMPDEST = 1; 220 | TLOAD = 100, if CANCUN; 221 | TSTORE = 100, if CANCUN; 222 | MCOPY = 3 | DYNAMIC, if CANCUN; // [2] 223 | 224 | PUSH0 = 2, if SHANGHAI; 225 | PUSH1 = 3; 226 | PUSH2 = 3; 227 | PUSH3 = 3; 228 | PUSH4 = 3; 229 | PUSH5 = 3; 230 | PUSH6 = 3; 231 | PUSH7 = 3; 232 | PUSH8 = 3; 233 | PUSH9 = 3; 234 | PUSH10 = 3; 235 | PUSH11 = 3; 236 | PUSH12 = 3; 237 | PUSH13 = 3; 238 | PUSH14 = 3; 239 | PUSH15 = 3; 240 | PUSH16 = 3; 241 | PUSH17 = 3; 242 | PUSH18 = 3; 243 | PUSH19 = 3; 244 | PUSH20 = 3; 245 | PUSH21 = 3; 246 | PUSH22 = 3; 247 | PUSH23 = 3; 248 | PUSH24 = 3; 249 | PUSH25 = 3; 250 | PUSH26 = 3; 251 | PUSH27 = 3; 252 | PUSH28 = 3; 253 | PUSH29 = 3; 254 | PUSH30 = 3; 255 | PUSH31 = 3; 256 | PUSH32 = 3; 257 | 258 | DUP1 = 3; 259 | DUP2 = 3; 260 | DUP3 = 3; 261 | DUP4 = 3; 262 | DUP5 = 3; 263 | DUP6 = 3; 264 | DUP7 = 3; 265 | DUP8 = 3; 266 | DUP9 = 3; 267 | DUP10 = 3; 268 | DUP11 = 3; 269 | DUP12 = 3; 270 | DUP13 = 3; 271 | DUP14 = 3; 272 | DUP15 = 3; 273 | DUP16 = 3; 274 | 275 | SWAP1 = 3; 276 | SWAP2 = 3; 277 | SWAP3 = 3; 278 | SWAP4 = 3; 279 | SWAP5 = 3; 280 | SWAP6 = 3; 281 | SWAP7 = 3; 282 | SWAP8 = 3; 283 | SWAP9 = 3; 284 | SWAP10 = 3; 285 | SWAP11 = 3; 286 | SWAP12 = 3; 287 | SWAP13 = 3; 288 | SWAP14 = 3; 289 | SWAP15 = 3; 290 | SWAP16 = 3; 291 | 292 | LOG0 = log_cost(0) | DYNAMIC; // [2] 293 | LOG1 = log_cost(1) | DYNAMIC; // [2] 294 | LOG2 = log_cost(2) | DYNAMIC; // [2] 295 | LOG3 = log_cost(3) | DYNAMIC; // [2] 296 | LOG4 = log_cost(4) | DYNAMIC; // [2] 297 | // 0xA5 298 | // 0xA6 299 | // 0xA7 300 | // 0xA8 301 | // 0xA9 302 | // 0xAA 303 | // 0xAB 304 | // 0xAC 305 | // 0xAD 306 | // 0xAE 307 | // 0xAF 308 | // 0xB0 309 | // 0xB1 310 | // 0xB2 311 | // 0xB3 312 | // 0xB4 313 | // 0xB5 314 | // 0xB6 315 | // 0xB7 316 | // 0xB8 317 | // 0xB9 318 | // 0xBA 319 | // 0xBB 320 | // 0xBC 321 | // 0xBD 322 | // 0xBE 323 | // 0xBF 324 | // 0xC0 325 | // 0xC1 326 | // 0xC2 327 | // 0xC3 328 | // 0xC4 329 | // 0xC5 330 | // 0xC6 331 | // 0xC7 332 | // 0xC8 333 | // 0xC9 334 | // 0xCA 335 | // 0xCB 336 | // 0xCC 337 | // 0xCD 338 | // 0xCE 339 | // 0xCF 340 | DATALOAD = 4 | EOF, if OSAKA; 341 | DATALOADN = 3 | EOF, if OSAKA; 342 | DATASIZE = 2 | EOF, if OSAKA; 343 | DATACOPY = 3 | DYNAMIC | EOF, if OSAKA; // [2] 344 | // 0xD4 345 | // 0xD5 346 | // 0xD6 347 | // 0xD7 348 | // 0xD8 349 | // 0xD9 350 | // 0xDA 351 | // 0xDB 352 | // 0xDC 353 | // 0xDD 354 | // 0xDE 355 | // 0xDF 356 | RJUMP = 2 | EOF, if OSAKA; 357 | RJUMPI = 4 | EOF, if OSAKA; 358 | RJUMPV = 4 | EOF, if OSAKA; 359 | CALLF = 5 | EOF, if OSAKA; 360 | RETF = 3 | EOF, if OSAKA; 361 | JUMPF = 5 | EOF, if OSAKA; 362 | DUPN = 3 | EOF, if OSAKA; 363 | SWAPN = 3 | EOF, if OSAKA; 364 | EXCHANGE = 3 | EOF, if OSAKA; 365 | // 0xE9 366 | // 0xEA 367 | // 0xEB 368 | EOFCREATE = DYNAMIC | EOF, if OSAKA; // TODO: EOF_CREATE_GAS | DYNAMIC is too big 369 | // 0xED 370 | RETURNCONTRACT = DYNAMIC | EOF, if OSAKA; 371 | // 0xEF 372 | CREATE = DYNAMIC; 373 | CALL = DYNAMIC; 374 | CALLCODE = DYNAMIC; 375 | RETURN = DYNAMIC; 376 | DELEGATECALL = DYNAMIC, if HOMESTEAD; 377 | CREATE2 = DYNAMIC, if PETERSBURG; 378 | // 0xF6 379 | RETURNDATALOAD = 3 | EOF, if OSAKA; 380 | EXTCALL = DYNAMIC | EOF, if OSAKA; 381 | EXTDELEGATECALL = DYNAMIC | EOF, if OSAKA; 382 | STATICCALL = DYNAMIC, if BYZANTIUM; 383 | EXTSTATICCALL = DYNAMIC | EOF, if OSAKA; 384 | // 0xFC 385 | REVERT = DYNAMIC, if BYZANTIUM; 386 | INVALID = 0; 387 | SELFDESTRUCT = DYNAMIC; 388 | } 389 | map 390 | } 391 | 392 | const fn log_cost(n: u8) -> u16 { 393 | match gas::log_cost(n, 0) { 394 | Some(gas) => { 395 | assert!(gas <= u16::MAX as u64); 396 | gas as u16 397 | } 398 | None => unreachable!(), 399 | } 400 | } 401 | -------------------------------------------------------------------------------- /crates/revmc/src/bytecode/opcode.rs: -------------------------------------------------------------------------------- 1 | use crate::{op_info_map, OpcodeInfo}; 2 | use revm_interpreter::{opcode as op, OPCODE_INFO_JUMPTABLE}; 3 | use revm_primitives::{Bytes, Eof, SpecId, EOF_MAGIC_BYTES}; 4 | use std::{fmt, slice}; 5 | 6 | /// A bytecode iterator that yields opcodes and their immediate data, alongside the program counter. 7 | /// 8 | /// Created by calling [`OpcodesIter::with_pc`]. 9 | #[derive(Debug)] 10 | pub struct OpcodesIterWithPc<'a> { 11 | iter: OpcodesIter<'a>, 12 | pc: usize, 13 | } 14 | 15 | impl<'a> Iterator for OpcodesIterWithPc<'a> { 16 | type Item = (usize, Opcode<'a>); 17 | 18 | #[inline] 19 | fn next(&mut self) -> Option { 20 | self.iter.next().map(|opcode| { 21 | let pc = self.pc; 22 | self.pc += 1; 23 | if let Some(imm) = opcode.immediate { 24 | self.pc += imm.len(); 25 | } 26 | (pc, opcode) 27 | }) 28 | } 29 | 30 | #[inline] 31 | fn size_hint(&self) -> (usize, Option) { 32 | self.iter.size_hint() 33 | } 34 | } 35 | 36 | impl std::iter::FusedIterator for OpcodesIterWithPc<'_> {} 37 | 38 | /// An iterator that yields opcodes and their immediate data. 39 | /// 40 | /// If the bytecode is not well-formed, the iterator will still yield opcodes, but the immediate 41 | /// data may be incorrect. For example, if the bytecode is `PUSH2 0x69`, the iterator will yield 42 | /// `PUSH2, None`. 43 | #[derive(Clone, Debug)] 44 | pub struct OpcodesIter<'a> { 45 | iter: slice::Iter<'a, u8>, 46 | info: &'static [OpcodeInfo; 256], 47 | } 48 | 49 | impl fmt::Display for OpcodesIter<'_> { 50 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 51 | for (i, op) in self.clone().enumerate() { 52 | if i > 0 { 53 | f.write_str(" ")?; 54 | } 55 | write!(f, "{op}")?; 56 | } 57 | Ok(()) 58 | } 59 | } 60 | 61 | impl<'a> OpcodesIter<'a> { 62 | /// Create a new iterator over the given bytecode slice. 63 | #[inline] 64 | pub fn new(slice: &'a [u8], spec_id: SpecId) -> Self { 65 | Self { iter: slice.iter(), info: op_info_map(spec_id) } 66 | } 67 | 68 | /// Returns a new iterator that also yields the program counter alongside the opcode and 69 | /// immediate data. 70 | #[inline] 71 | pub fn with_pc(self) -> OpcodesIterWithPc<'a> { 72 | OpcodesIterWithPc { iter: self, pc: 0 } 73 | } 74 | 75 | /// Returns the inner iterator. 76 | #[inline] 77 | pub fn inner(&self) -> &slice::Iter<'a, u8> { 78 | &self.iter 79 | } 80 | 81 | /// Returns the inner iterator. 82 | #[inline] 83 | pub fn inner_mut(&mut self) -> &mut slice::Iter<'a, u8> { 84 | &mut self.iter 85 | } 86 | 87 | /// Returns the inner iterator. 88 | #[inline] 89 | pub fn into_inner(self) -> slice::Iter<'a, u8> { 90 | self.iter 91 | } 92 | } 93 | 94 | impl<'a> Iterator for OpcodesIter<'a> { 95 | type Item = Opcode<'a>; 96 | 97 | #[inline] 98 | fn next(&mut self) -> Option { 99 | self.iter.next().map(|&opcode| { 100 | let info = self.info[opcode as usize]; 101 | if info.is_unknown() || info.is_disabled() { 102 | return Opcode { opcode, immediate: None }; 103 | } 104 | 105 | let mut len = min_imm_len(opcode) as usize; 106 | if opcode == op::RJUMPV { 107 | if let Some(&max_case) = self.iter.as_slice().first() { 108 | len += (max_case as usize + 1) * 2; 109 | } 110 | } 111 | let immediate = if len > 0 { 112 | let r = self.iter.as_slice().get(..len); 113 | // TODO: Use `advance_by` when stable. 114 | self.iter.by_ref().take(len).for_each(drop); 115 | r 116 | } else { 117 | None 118 | }; 119 | Opcode { opcode, immediate } 120 | }) 121 | } 122 | 123 | #[inline] 124 | fn size_hint(&self) -> (usize, Option) { 125 | let len = self.iter.len(); 126 | ((len != 0) as usize, Some(len)) 127 | } 128 | } 129 | 130 | impl std::iter::FusedIterator for OpcodesIter<'_> {} 131 | 132 | /// An opcode and its immediate data. Returned by [`OpcodesIter`]. 133 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 134 | pub struct Opcode<'a> { 135 | /// The opcode. 136 | pub opcode: u8, 137 | /// The immediate data, if any. 138 | pub immediate: Option<&'a [u8]>, 139 | } 140 | 141 | impl fmt::Debug for Opcode<'_> { 142 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 143 | fmt::Display::fmt(self, f) 144 | } 145 | } 146 | 147 | impl fmt::Display for Opcode<'_> { 148 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 149 | match OPCODE_INFO_JUMPTABLE[self.opcode as usize] { 150 | Some(s) => f.write_str(s.name()), 151 | None => write!(f, "UNKNOWN(0x{:02x})", self.opcode), 152 | }?; 153 | match self.immediate { 154 | Some(imm) => write!(f, " {}", revm_primitives::hex::encode_prefixed(imm)), 155 | None => Ok(()), 156 | } 157 | } 158 | } 159 | 160 | /// Returns the length of the immediate data for the given opcode, or `0` if none. 161 | /// 162 | /// This is the full length for all opcodes that have an immediate, except for `RJUMPV`, which 163 | /// currently is the only opcode which has a variable length immediate. 164 | #[inline] 165 | pub const fn min_imm_len(op: u8) -> u8 { 166 | if let Some(info) = &OPCODE_INFO_JUMPTABLE[op as usize] { 167 | info.immediate_size() 168 | } else { 169 | 0 170 | } 171 | } 172 | 173 | /// Returns the number of input and output stack elements of the given opcode. 174 | #[inline] 175 | pub const fn stack_io(op: u8) -> (u8, u8) { 176 | if let Some(info) = &OPCODE_INFO_JUMPTABLE[op as usize] { 177 | (info.inputs(), info.outputs()) 178 | } else { 179 | (0, 0) 180 | } 181 | } 182 | 183 | /// Returns a string representation of the given bytecode. 184 | pub fn format_bytecode(bytecode: &[u8], spec_id: SpecId) -> String { 185 | let mut w = String::new(); 186 | format_bytecode_to(bytecode, spec_id, &mut w).unwrap(); 187 | w 188 | } 189 | 190 | /// Formats an EVM bytecode to the given writer. 191 | pub fn format_bytecode_to( 192 | bytecode: &[u8], 193 | spec_id: SpecId, 194 | w: &mut W, 195 | ) -> fmt::Result { 196 | if spec_id.is_enabled_in(SpecId::OSAKA) && bytecode.starts_with(&EOF_MAGIC_BYTES) { 197 | write!(w, "{:#?}", Eof::decode(Bytes::copy_from_slice(bytecode))) 198 | } else { 199 | write!(w, "{}", OpcodesIter::new(bytecode, spec_id)) 200 | } 201 | } 202 | 203 | #[cfg(test)] 204 | mod tests { 205 | use super::*; 206 | use revm_interpreter::opcode as op; 207 | 208 | const DEF_SPEC: SpecId = SpecId::ARROW_GLACIER; 209 | 210 | #[test] 211 | fn iter_basic() { 212 | let bytecode = [0x01, 0x02, 0x03, 0x04, 0x05]; 213 | let mut iter = OpcodesIter::new(&bytecode, DEF_SPEC); 214 | 215 | assert_eq!(iter.next(), Some(Opcode { opcode: 0x01, immediate: None })); 216 | assert_eq!(iter.next(), Some(Opcode { opcode: 0x02, immediate: None })); 217 | assert_eq!(iter.next(), Some(Opcode { opcode: 0x03, immediate: None })); 218 | assert_eq!(iter.next(), Some(Opcode { opcode: 0x04, immediate: None })); 219 | assert_eq!(iter.next(), Some(Opcode { opcode: 0x05, immediate: None })); 220 | assert_eq!(iter.next(), None); 221 | } 222 | 223 | #[test] 224 | fn iter_with_imm() { 225 | let bytecode = [op::PUSH0, op::PUSH1, 0x69, op::PUSH2, 0x01, 0x02]; 226 | let mut iter = OpcodesIter::new(&bytecode, DEF_SPEC); 227 | 228 | assert_eq!(iter.next(), Some(Opcode { opcode: op::PUSH0, immediate: None })); 229 | assert_eq!(iter.next(), Some(Opcode { opcode: op::PUSH1, immediate: Some(&[0x69]) })); 230 | assert_eq!(iter.next(), Some(Opcode { opcode: op::PUSH2, immediate: Some(&[0x01, 0x02]) })); 231 | assert_eq!(iter.next(), None); 232 | } 233 | 234 | #[test] 235 | fn iter_with_imm_too_short() { 236 | let bytecode = [op::PUSH2, 0x69]; 237 | let mut iter = OpcodesIter::new(&bytecode, DEF_SPEC); 238 | 239 | assert_eq!(iter.next(), Some(Opcode { opcode: op::PUSH2, immediate: None })); 240 | assert_eq!(iter.next(), None); 241 | } 242 | 243 | #[test] 244 | fn display() { 245 | let bytecode = [op::PUSH0, op::PUSH1, 0x69, op::PUSH2, 0x01, 0x02]; 246 | let s = format_bytecode(&bytecode, DEF_SPEC); 247 | assert_eq!(s, "PUSH0 PUSH1 0x69 PUSH2 0x0102"); 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /crates/revmc/src/bytecode/sections.rs: -------------------------------------------------------------------------------- 1 | use super::Bytecode; 2 | use core::fmt; 3 | 4 | // TODO: Separate gas sections from stack length sections. 5 | // E.g. `GAS` should stop only a gas section because it requires `gasleft`, and execution will 6 | // continue with the next instruction. 7 | 8 | /// A section is a sequence of instructions that are executed sequentially without any jumps or 9 | /// branches. 10 | /// 11 | /// This would be better named "block" but it's already used in the context of the basic block 12 | /// analysis. 13 | #[derive(Clone, Copy, Default, PartialEq, Eq)] 14 | pub(crate) struct Section { 15 | /// The total base gas cost of all instructions in the section. 16 | pub(crate) gas_cost: u32, 17 | /// The stack height required to execute the section. 18 | pub(crate) inputs: u16, 19 | /// The maximum stack height growth relative to the stack height at section start. 20 | pub(crate) max_growth: i16, 21 | } 22 | 23 | impl fmt::Debug for Section { 24 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 25 | if self.is_empty() { 26 | f.write_str("Section::EMPTY") 27 | } else { 28 | f.debug_struct("Section") 29 | .field("gas_cost", &self.gas_cost) 30 | .field("stack_req", &self.inputs) 31 | .field("stack_max_growth", &self.max_growth) 32 | .finish() 33 | } 34 | } 35 | } 36 | 37 | impl Section { 38 | /// Returns `true` if the section is empty. 39 | #[inline] 40 | pub(crate) fn is_empty(self) -> bool { 41 | self == Self::default() 42 | } 43 | } 44 | 45 | /// Instruction section analysis. 46 | #[derive(Default)] 47 | pub(crate) struct SectionAnalysis { 48 | inputs: i32, 49 | diff: i32, 50 | max_growth: i32, 51 | 52 | gas_cost: u64, 53 | start_inst: usize, 54 | } 55 | 56 | impl SectionAnalysis { 57 | /// Process a single instruction. 58 | pub(crate) fn process(&mut self, bytecode: &mut Bytecode<'_>, inst: usize) { 59 | let is_eof = bytecode.is_eof(); 60 | 61 | // JUMPDEST starts a section. 62 | if bytecode.inst(inst).is_reachable_jumpdest(is_eof, bytecode.has_dynamic_jumps()) { 63 | self.save_to(bytecode, inst); 64 | self.reset(inst); 65 | } 66 | 67 | let data = bytecode.inst(inst); 68 | let (inp, out) = data.stack_io(); 69 | let stack_diff = out as i32 - inp as i32; 70 | self.inputs = self.inputs.max(inp as i32 - self.diff); 71 | self.diff += stack_diff; 72 | self.max_growth = self.max_growth.max(self.diff); 73 | 74 | self.gas_cost += data.base_gas as u64; 75 | 76 | // Instructions that require `gasleft` and branching instructions end a section, starting a 77 | // new one on the next instruction, if any. 78 | if (!is_eof && data.requires_gasleft(bytecode.spec_id)) 79 | || data.may_suspend(is_eof) 80 | || data.is_branching(is_eof) 81 | { 82 | let next = inst + 1; 83 | self.save_to(bytecode, next); 84 | self.reset(next); 85 | } 86 | } 87 | 88 | /// Finishes the analysis. 89 | pub(crate) fn finish(self, bytecode: &mut Bytecode<'_>) { 90 | self.save_to(bytecode, bytecode.insts.len() - 1); 91 | if enabled!(tracing::Level::DEBUG) { 92 | let mut max_len = 0; 93 | let mut current = 0; 94 | let mut count = 0usize; 95 | for (inst, data) in bytecode.iter_insts() { 96 | if data.section.is_empty() { 97 | continue; 98 | } 99 | let len = inst - current; 100 | max_len = max_len.max(len); 101 | current = inst; 102 | count += 1; 103 | } 104 | debug!(count, max_len, "sections"); 105 | } 106 | } 107 | 108 | /// Saves the current section to the bytecode. 109 | fn save_to(&self, bytecode: &mut Bytecode<'_>, next_section_inst: usize) { 110 | if self.start_inst >= bytecode.insts.len() { 111 | return; 112 | } 113 | let section = self.section(); 114 | if !section.is_empty() { 115 | trace!( 116 | inst = self.start_inst, 117 | len = next_section_inst - self.start_inst, 118 | ?section, 119 | "saving" 120 | ); 121 | let mut insts = bytecode.insts[self.start_inst..].iter_mut(); 122 | if let Some(inst) = insts.find(|inst| !inst.is_dead_code()) { 123 | inst.section = section; 124 | } 125 | } 126 | } 127 | 128 | /// Starts a new section. 129 | fn reset(&mut self, inst: usize) { 130 | *self = Self { start_inst: inst, ..Default::default() }; 131 | } 132 | 133 | /// Returns the current section. 134 | fn section(&self) -> Section { 135 | Section { 136 | gas_cost: self.gas_cost.try_into().unwrap_or(u32::MAX), 137 | inputs: self.inputs.try_into().unwrap_or(u16::MAX), 138 | max_growth: self.max_growth.try_into().unwrap_or(i16::MAX), 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /crates/revmc/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::needless_doctest_main)] 2 | #![doc = include_str!("../README.md")] 3 | #![cfg_attr(not(test), warn(unused_extern_crates))] 4 | #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] 5 | 6 | #[macro_use] 7 | extern crate tracing; 8 | 9 | // For features. 10 | use alloy_primitives as _; 11 | 12 | mod bytecode; 13 | pub use bytecode::*; 14 | 15 | mod compiler; 16 | pub use compiler::{EvmCompiler, EvmCompilerInput}; 17 | 18 | mod linker; 19 | pub use linker::Linker; 20 | 21 | /// Internal tests and testing utilities. Not public API. 22 | #[cfg(any(test, feature = "__fuzzing"))] 23 | pub mod tests; 24 | 25 | #[allow(ambiguous_glob_reexports)] 26 | #[doc(inline)] 27 | pub use revmc_backend::*; 28 | #[allow(ambiguous_glob_reexports)] 29 | #[doc(inline)] 30 | pub use revmc_context::*; 31 | 32 | #[cfg(feature = "llvm")] 33 | #[doc(no_inline)] 34 | pub use llvm::EvmLlvmBackend; 35 | #[cfg(feature = "llvm")] 36 | #[doc(inline)] 37 | pub use revmc_llvm as llvm; 38 | 39 | #[cfg(feature = "cranelift")] 40 | #[doc(no_inline)] 41 | pub use cranelift::EvmCraneliftBackend; 42 | #[cfg(feature = "cranelift")] 43 | #[doc(inline)] 44 | pub use revmc_cranelift as cranelift; 45 | 46 | #[doc(no_inline)] 47 | pub use revm_interpreter::{self as interpreter, primitives}; 48 | 49 | const I256_MIN: U256 = U256::from_limbs([ 50 | 0x0000000000000000, 51 | 0x0000000000000000, 52 | 0x0000000000000000, 53 | 0x8000000000000000, 54 | ]); 55 | 56 | /// Enable for `cargo asm -p revmc --lib`. 57 | #[cfg(any())] 58 | pub fn generate_all_assembly() -> EvmCompiler> { 59 | let cx = Box::leak(Box::new(llvm::inkwell::context::Context::create())); 60 | let mut compiler = 61 | EvmCompiler::new(EvmLlvmBackend::new(cx, OptimizationLevel::Aggressive).unwrap()); 62 | let _ = compiler.jit(None, &[], primitives::SpecId::ARROW_GLACIER).unwrap(); 63 | unsafe { compiler.clear().unwrap() }; 64 | compiler 65 | } 66 | -------------------------------------------------------------------------------- /crates/revmc/src/linker.rs: -------------------------------------------------------------------------------- 1 | use std::path::{Path, PathBuf}; 2 | 3 | /// EVM bytecode compiler linker. 4 | #[derive(Debug)] 5 | pub struct Linker { 6 | cc: Option, 7 | linker: Option, 8 | cflags: Vec, 9 | } 10 | 11 | impl Default for Linker { 12 | fn default() -> Self { 13 | Self::new() 14 | } 15 | } 16 | 17 | impl Linker { 18 | /// Creates a new linker. 19 | pub fn new() -> Self { 20 | Self { cc: None, linker: None, cflags: vec![] } 21 | } 22 | 23 | /// Sets the C compiler to use for linking. Default: "cc". 24 | pub fn cc(&mut self, cc: Option) { 25 | self.cc = cc; 26 | } 27 | 28 | /// Sets the linker to use for linking. Default: `lld`. 29 | pub fn linker(&mut self, linker: Option) { 30 | self.linker = linker; 31 | } 32 | 33 | /// Sets the C compiler flags to use for linking. 34 | pub fn cflags(&mut self, cflags: impl IntoIterator>) { 35 | self.cflags.extend(cflags.into_iter().map(Into::into)); 36 | } 37 | 38 | /// Links the given object files into a shared library at the given path. 39 | #[instrument(level = "debug", skip_all)] 40 | pub fn link( 41 | &self, 42 | out: &Path, 43 | objects: impl IntoIterator>, 44 | ) -> std::io::Result<()> { 45 | let storage; 46 | let cc = match &self.cc { 47 | Some(cc) => cc, 48 | None => { 49 | let str = match std::env::var_os("CC") { 50 | Some(cc) => { 51 | storage = cc; 52 | storage.as_os_str() 53 | } 54 | None => "cc".as_ref(), 55 | }; 56 | Path::new(str) 57 | } 58 | }; 59 | 60 | let mut cmd = std::process::Command::new(cc); 61 | cmd.arg("-o").arg(out); 62 | cmd.arg("-shared"); 63 | cmd.arg("-O3"); 64 | if let Some(linker) = &self.linker { 65 | cmd.arg(format!("-fuse-ld={}", linker.display())); 66 | } else { 67 | cmd.arg("-fuse-ld=lld"); 68 | } 69 | if cfg!(target_vendor = "apple") { 70 | cmd.arg("-Wl,-dead_strip,-undefined,dynamic_lookup"); 71 | } else { 72 | cmd.arg("-Wl,--gc-sections,--strip-debug"); 73 | } 74 | cmd.args(&self.cflags); 75 | cmd.args(objects); 76 | debug!(cmd=?cmd.get_program(), "linking"); 77 | trace!(?cmd, "full linking command"); 78 | let output = cmd.output()?; 79 | if !output.status.success() { 80 | return Err(std::io::Error::new( 81 | std::io::ErrorKind::Other, 82 | format!("cc failed with {output:#?}"), 83 | )); 84 | } 85 | Ok(()) 86 | } 87 | } 88 | 89 | #[cfg(test)] 90 | mod tests { 91 | use super::*; 92 | use revm_primitives::SpecId; 93 | 94 | #[test] 95 | fn basic() { 96 | let tmp = tempfile::tempdir().expect("could not create temp dir"); 97 | let obj = tmp.path().join("out.o"); 98 | let so = tmp.path().join("out.so"); 99 | 100 | // Compile and build object file. 101 | let cx = crate::llvm::inkwell::context::Context::create(); 102 | let opt_level = revmc_backend::OptimizationLevel::Aggressive; 103 | let backend = crate::EvmLlvmBackend::new(&cx, true, opt_level).unwrap(); 104 | let mut compiler = crate::EvmCompiler::new(backend); 105 | if let Err(e) = compiler.translate("link_test_basic", &[][..], SpecId::CANCUN) { 106 | panic!("failed to compile: {e}"); 107 | } 108 | 109 | if let Err(e) = compiler.write_object_to_file(&obj) { 110 | panic!("failed to write object: {e}"); 111 | } 112 | assert!(obj.exists()); 113 | 114 | // Link object to shared library. 115 | let mut linker = Linker::new(); 116 | let mut n = 0; 117 | for driver in ["cc", "gcc", "clang"] { 118 | if !command_v(driver) { 119 | continue; 120 | } 121 | n += 1; 122 | 123 | let _ = std::fs::remove_file(&so); 124 | linker.cc = Some(driver.into()); 125 | if let Err(e) = linker.link(&so, [&obj]) { 126 | panic!("failed to link with {driver}: {e}"); 127 | } 128 | assert!(so.exists()); 129 | } 130 | assert!(n > 0, "no C compiler found"); 131 | } 132 | 133 | fn command_v(cmd: &str) -> bool { 134 | let Ok(output) = std::process::Command::new(cmd).arg("--version").output() else { 135 | return false; 136 | }; 137 | if !output.status.success() { 138 | eprintln!("command {cmd} failed: {output:#?}"); 139 | return false; 140 | } 141 | true 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /crates/revmc/src/tests/fibonacci.rs: -------------------------------------------------------------------------------- 1 | use super::{with_evm_context, DEF_SPEC}; 2 | use crate::{Backend, EvmCompiler}; 3 | use paste::paste; 4 | use revm_interpreter::{opcode as op, InstructionResult}; 5 | use revm_primitives::U256; 6 | 7 | macro_rules! fibonacci_tests { 8 | ($($i:expr),* $(,)?) => {paste! { 9 | $( 10 | matrix_tests!([] = |jit| run_fibonacci_test(jit, $i, false)); 11 | matrix_tests!([] = |jit| run_fibonacci_test(jit, $i, true)); 12 | )* 13 | }}; 14 | } 15 | 16 | fibonacci_tests!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 100, 1000); 17 | 18 | fn run_fibonacci_test(compiler: &mut EvmCompiler, input: u16, dynamic: bool) { 19 | let code = mk_fibonacci_code(input, dynamic); 20 | 21 | unsafe { compiler.clear() }.unwrap(); 22 | compiler.inspect_stack_length(true); 23 | let f = unsafe { compiler.jit("fib", &code, DEF_SPEC) }.unwrap(); 24 | 25 | with_evm_context(&code, |ecx, stack, stack_len| { 26 | if dynamic { 27 | stack.as_mut_slice()[0] = U256::from(input).into(); 28 | *stack_len = 1; 29 | } 30 | let r = unsafe { f.call(Some(stack), Some(stack_len), ecx) }; 31 | assert_eq!(r, InstructionResult::Stop); 32 | // Apparently the code does `fibonacci(input + 1)`. 33 | assert_eq!(*stack_len, 1); 34 | assert_eq!(stack.as_slice()[0].to_u256(), fibonacci_rust(input + 1)); 35 | }); 36 | } 37 | 38 | fn mk_fibonacci_code(input: u16, dynamic: bool) -> Vec { 39 | if dynamic { 40 | [&[op::JUMPDEST; 3][..], FIBONACCI_CODE].concat() 41 | } else { 42 | let input = input.to_be_bytes(); 43 | [[op::PUSH2].as_slice(), input.as_slice(), FIBONACCI_CODE].concat() 44 | } 45 | } 46 | 47 | // Modified from jitevm: https://github.com/paradigmxyz/jitevm/blob/f82261fc8a1a6c1a3d40025a910ba0ce3fcaed71/src/test_data.rs#L3 48 | #[rustfmt::skip] 49 | const FIBONACCI_CODE: &[u8] = &[ 50 | // Expects the code to be offset 3 bytes. 51 | // JUMPDEST, JUMPDEST, JUMPDEST, 52 | 53 | // 1st/2nd fib number 54 | op::PUSH1, 0, 55 | op::PUSH1, 1, 56 | // 7 57 | 58 | // MAINLOOP: 59 | op::JUMPDEST, 60 | op::DUP3, 61 | op::ISZERO, 62 | op::PUSH1, 28, // cleanup 63 | op::JUMPI, 64 | 65 | // fib step 66 | op::DUP2, 67 | op::DUP2, 68 | op::ADD, 69 | op::SWAP2, 70 | op::POP, 71 | op::SWAP1, 72 | // 19 73 | 74 | // decrement fib step counter 75 | op::SWAP2, 76 | op::PUSH1, 1, 77 | op::SWAP1, 78 | op::SUB, 79 | op::SWAP2, 80 | op::PUSH1, 7, // goto MAINLOOP 81 | op::JUMP, 82 | // 28 83 | 84 | // CLEANUP: 85 | op::JUMPDEST, 86 | op::SWAP2, 87 | op::POP, 88 | op::POP, 89 | // done: requested fib number is the only element on the stack! 90 | op::STOP, 91 | ]; 92 | 93 | fn fibonacci_rust(n: u16) -> U256 { 94 | let mut a = U256::from(0); 95 | let mut b = U256::from(1); 96 | for _ in 0..n { 97 | let tmp = a; 98 | a = b; 99 | b = b.wrapping_add(tmp); 100 | } 101 | a 102 | } 103 | 104 | #[test] 105 | fn test_fibonacci_rust() { 106 | revm_primitives::uint! { 107 | assert_eq!(fibonacci_rust(0), 0_U256); 108 | assert_eq!(fibonacci_rust(1), 1_U256); 109 | assert_eq!(fibonacci_rust(2), 1_U256); 110 | assert_eq!(fibonacci_rust(3), 2_U256); 111 | assert_eq!(fibonacci_rust(4), 3_U256); 112 | assert_eq!(fibonacci_rust(5), 5_U256); 113 | assert_eq!(fibonacci_rust(6), 8_U256); 114 | assert_eq!(fibonacci_rust(7), 13_U256); 115 | assert_eq!(fibonacci_rust(8), 21_U256); 116 | assert_eq!(fibonacci_rust(9), 34_U256); 117 | assert_eq!(fibonacci_rust(10), 55_U256); 118 | assert_eq!(fibonacci_rust(100), 354224848179261915075_U256); 119 | assert_eq!(fibonacci_rust(1000), 0x2e3510283c1d60b00930b7e8803c312b4c8e6d5286805fc70b594dc75cc0604b_U256); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /crates/revmc/src/tests/macros.rs: -------------------------------------------------------------------------------- 1 | macro_rules! matrix_tests { 2 | ($run:ident) => { 3 | #[cfg(feature = "llvm")] 4 | mod llvm { 5 | use super::*; 6 | #[allow(unused_imports)] 7 | use similar_asserts::assert_eq; 8 | 9 | fn run_llvm(compiler: &mut EvmCompiler>) { 10 | crate::tests::set_test_dump(compiler, module_path!()); 11 | $run(compiler); 12 | } 13 | 14 | #[test] 15 | fn unopt() { 16 | crate::tests::with_llvm_backend_jit(crate::OptimizationLevel::None, run_llvm); 17 | } 18 | 19 | #[test] 20 | fn opt() { 21 | crate::tests::with_llvm_backend_jit(crate::OptimizationLevel::Aggressive, run_llvm); 22 | } 23 | } 24 | }; 25 | 26 | ($name:ident = | $compiler:ident | $e:expr) => { 27 | mod $name { 28 | use super::*; 29 | #[allow(unused_imports)] 30 | use similar_asserts::assert_eq; 31 | 32 | fn run_generic($compiler: &mut EvmCompiler) { 33 | $e; 34 | } 35 | 36 | matrix_tests!(run_generic); 37 | } 38 | }; 39 | ($name:ident = $run:ident) => { 40 | mod $name { 41 | use super::*; 42 | #[allow(unused_imports)] 43 | use similar_asserts::assert_eq; 44 | 45 | matrix_tests!($run); 46 | } 47 | }; 48 | } 49 | 50 | macro_rules! build_push32 { 51 | ($code:ident[$i:ident], $x:expr) => {{ 52 | $code[$i] = op::PUSH32; 53 | $i += 1; 54 | $code[$i..$i + 32].copy_from_slice(&$x.to_be_bytes::<32>()); 55 | $i += 32; 56 | }}; 57 | } 58 | 59 | macro_rules! tests { 60 | ($($group:ident { $($t:tt)* })*) => { uint! { 61 | $( 62 | mod $group { 63 | use super::*; 64 | #[allow(unused_imports)] 65 | use similar_asserts::assert_eq; 66 | 67 | tests!(@cases $($t)*); 68 | } 69 | )* 70 | }}; 71 | 72 | (@cases $( $name:ident($($t:tt)*) ),* $(,)?) => { 73 | $( 74 | matrix_tests!($name = |jit| run_test_case(tests!(@case $($t)*), jit)); 75 | )* 76 | }; 77 | 78 | (@case @raw { $($fields:tt)* }) => { &TestCase { $($fields)* ..Default::default() } }; 79 | 80 | (@case $op:expr $(, $args:expr)* $(,)? => $($ret:expr),* $(,)? $(; op_gas($op_gas:expr))?) => { 81 | &TestCase { 82 | bytecode: &tests!(@bytecode $op, $($args),*), 83 | expected_stack: &[$($ret),*], 84 | expected_gas: tests!(@gas $op $(, $op_gas)?; $($args),*), 85 | ..Default::default() 86 | } 87 | }; 88 | 89 | (@bytecode $op:expr, $a:expr) => { bytecode_unop($op, $a) }; 90 | (@bytecode $op:expr, $a:expr, $b:expr) => { bytecode_binop($op, $a, $b) }; 91 | (@bytecode $op:expr, $a:expr, $b:expr, $c:expr) => { bytecode_ternop($op, $a, $b, $c) }; 92 | 93 | (@gas $op:expr; $($args:expr),+) => { 94 | tests!(@gas 95 | $op, 96 | DEF_OPINFOS[$op as usize].base_gas() as u64; 97 | $($args),+ 98 | ) 99 | }; 100 | (@gas $op:expr, $op_gas:expr; $($args:expr),+) => { 101 | $op_gas + tests!(@gas_base $($args),+) 102 | }; 103 | (@gas_base $a:expr) => { 3 }; 104 | (@gas_base $a:expr, $b:expr) => { 6 }; 105 | (@gas_base $a:expr, $b:expr, $c:expr) => { 9 }; 106 | } 107 | -------------------------------------------------------------------------------- /crates/revmc/src/tests/meta.rs: -------------------------------------------------------------------------------- 1 | use super::with_evm_context; 2 | use crate::{Backend, EvmCompiler}; 3 | use revm_interpreter::InstructionResult; 4 | use revm_primitives::SpecId; 5 | 6 | matrix_tests!(translate_then_compile); 7 | 8 | // Also tests multiple functions in the same module. 9 | fn translate_then_compile(compiler: &mut EvmCompiler) { 10 | let bytecode: &[u8] = &[]; 11 | let spec_id = SpecId::CANCUN; 12 | compiler.gas_metering(false); 13 | let gas_id = compiler.translate("test1", bytecode, spec_id).unwrap(); 14 | compiler.gas_metering(true); 15 | let no_gas_id = compiler.translate("test2", bytecode, spec_id).unwrap(); 16 | let gas_fn = unsafe { compiler.jit_function(gas_id) }.unwrap(); 17 | let no_gas_fn = unsafe { compiler.jit_function(no_gas_id) }.unwrap(); 18 | with_evm_context(bytecode, |ecx, stack, stack_len| { 19 | let r = unsafe { gas_fn.call(Some(stack), Some(stack_len), ecx) }; 20 | assert_eq!(r, InstructionResult::Stop); 21 | let r = unsafe { no_gas_fn.call(Some(stack), Some(stack_len), ecx) }; 22 | assert_eq!(r, InstructionResult::Stop); 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /crates/revmc/src/tests/resume.rs: -------------------------------------------------------------------------------- 1 | use super::{eof, eof_sections_unchecked, with_evm_context, DEF_SPEC}; 2 | use crate::{Backend, EvmCompiler, TEST_SUSPEND}; 3 | use revm_interpreter::{opcode as op, InstructionResult}; 4 | use revm_primitives::{SpecId, U256}; 5 | 6 | matrix_tests!(legacy = |compiler| run(compiler, TEST, DEF_SPEC)); 7 | matrix_tests!(eof_one_section = |compiler| run(compiler, &eof(TEST), SpecId::OSAKA)); 8 | matrix_tests!( 9 | eof_two_sections = |compiler| run( 10 | compiler, 11 | &eof_sections_unchecked(&[&[op::JUMPF, 0x00, 0x01], TEST]).raw, 12 | SpecId::OSAKA 13 | ) 14 | ); 15 | 16 | #[rustfmt::skip] 17 | const TEST: &[u8] = &[ 18 | // 0 19 | op::PUSH1, 0x42, 20 | TEST_SUSPEND, 21 | 22 | // 1 23 | op::PUSH1, 0x69, 24 | TEST_SUSPEND, 25 | 26 | // 2 27 | op::ADD, 28 | TEST_SUSPEND, 29 | 30 | // 3 31 | op::STOP, 32 | ]; 33 | 34 | fn run(compiler: &mut EvmCompiler, code: &[u8], spec_id: SpecId) { 35 | // Done manually in `fn eof` and friends. 36 | compiler.validate_eof(false); 37 | let f = unsafe { compiler.jit("resume", code, spec_id) }.unwrap(); 38 | 39 | with_evm_context(code, |ecx, stack, stack_len| { 40 | let is_eof = ecx.contract.bytecode.is_eof(); 41 | assert_eq!(ecx.resume_at, 0); 42 | 43 | // op::PUSH1, 0x42, 44 | let r = unsafe { f.call(Some(stack), Some(stack_len), ecx) }; 45 | assert_eq!(r, InstructionResult::CallOrCreate); 46 | assert_eq!(*stack_len, 1); 47 | assert_eq!(stack.as_slice()[0].to_u256(), U256::from(0x42)); 48 | let resume_1 = ecx.resume_at; 49 | if resume_1 < 100 { 50 | assert_eq!(resume_1, 1); 51 | } 52 | 53 | // op::PUSH1, 0x69, 54 | let r = unsafe { f.call(Some(stack), Some(stack_len), ecx) }; 55 | assert_eq!(r, InstructionResult::CallOrCreate); 56 | assert_eq!(*stack_len, 2); 57 | assert_eq!(stack.as_slice()[0].to_u256(), U256::from(0x42)); 58 | assert_eq!(stack.as_slice()[1].to_u256(), U256::from(0x69)); 59 | let resume_2 = ecx.resume_at; 60 | if resume_2 < 100 { 61 | assert_eq!(resume_2, 2); 62 | } 63 | 64 | // op::ADD, 65 | let r = unsafe { f.call(Some(stack), Some(stack_len), ecx) }; 66 | assert_eq!(r, InstructionResult::CallOrCreate); 67 | assert_eq!(*stack_len, 1); 68 | assert_eq!(stack.as_slice()[0].to_u256(), U256::from(0x42 + 0x69)); 69 | let resume_3 = ecx.resume_at; 70 | if resume_3 < 100 { 71 | assert_eq!(resume_3, 3); 72 | } 73 | 74 | // stop 75 | let r = unsafe { f.call(Some(stack), Some(stack_len), ecx) }; 76 | assert_eq!(r, InstructionResult::Stop); 77 | assert_eq!(*stack_len, 1); 78 | assert_eq!(stack.as_slice()[0].to_u256(), U256::from(0x42 + 0x69)); 79 | assert_eq!(ecx.resume_at, resume_3); 80 | 81 | // Does not stack overflow EOF because of removed checks. This cannot happen in practice. 82 | if !is_eof { 83 | // op::ADD, 84 | ecx.resume_at = resume_2; 85 | let r = unsafe { f.call(Some(stack), Some(stack_len), ecx) }; 86 | assert_eq!(r, InstructionResult::StackUnderflow); 87 | assert_eq!(*stack_len, 1); 88 | assert_eq!(stack.as_slice()[0].to_u256(), U256::from(0x42 + 0x69)); 89 | assert_eq!(ecx.resume_at, resume_2); 90 | } 91 | 92 | stack.as_mut_slice()[*stack_len] = U256::from(2).into(); 93 | *stack_len += 1; 94 | 95 | // op::ADD, 96 | ecx.resume_at = resume_2; 97 | let r = unsafe { f.call(Some(stack), Some(stack_len), ecx) }; 98 | assert_eq!(r, InstructionResult::CallOrCreate); 99 | assert_eq!(*stack_len, 1); 100 | assert_eq!(stack.as_slice()[0].to_u256(), U256::from(0x42 + 0x69 + 2)); 101 | assert_eq!(ecx.resume_at, resume_3); 102 | 103 | // op::PUSH1, 0x69, 104 | ecx.resume_at = resume_1; 105 | let r = unsafe { f.call(Some(stack), Some(stack_len), ecx) }; 106 | assert_eq!(r, InstructionResult::CallOrCreate); 107 | assert_eq!(*stack_len, 2); 108 | assert_eq!(stack.as_slice()[0].to_u256(), U256::from(0x42 + 0x69 + 2)); 109 | assert_eq!(stack.as_slice()[1].to_u256(), U256::from(0x69)); 110 | assert_eq!(ecx.resume_at, resume_2); 111 | 112 | // op::ADD, 113 | let r = unsafe { f.call(Some(stack), Some(stack_len), ecx) }; 114 | assert_eq!(r, InstructionResult::CallOrCreate); 115 | assert_eq!(*stack_len, 1); 116 | assert_eq!(stack.as_slice()[0].to_u256(), U256::from(0x42 + 0x69 + 2 + 0x69)); 117 | assert_eq!(ecx.resume_at, resume_3); 118 | 119 | // stop 120 | let r = unsafe { f.call(Some(stack), Some(stack_len), ecx) }; 121 | assert_eq!(r, InstructionResult::Stop); 122 | assert_eq!(*stack_len, 1); 123 | assert_eq!(stack.as_slice()[0].to_u256(), U256::from(0x42 + 0x69 + 2 + 0x69)); 124 | assert_eq!(ecx.resume_at, resume_3); 125 | 126 | // stop 127 | let r = unsafe { f.call(Some(stack), Some(stack_len), ecx) }; 128 | assert_eq!(r, InstructionResult::Stop); 129 | assert_eq!(*stack_len, 1); 130 | assert_eq!(stack.as_slice()[0].to_u256(), U256::from(0x42 + 0x69 + 2 + 0x69)); 131 | assert_eq!(ecx.resume_at, resume_3); 132 | }); 133 | } 134 | -------------------------------------------------------------------------------- /data/airdrop.rt.hex: -------------------------------------------------------------------------------- 1 | 0x608060405234801561001057600080fd5b50600436106101585760003560e01c80637cb64759116100c3578063ccb98ffc1161007c578063ccb98ffc146102d1578063e5962195146102e4578063ea80119e14610307578063f2fde38b1461031a578063fbfa77cf1461032d578063fc0c546a1461034057600080fd5b80637cb64759146102295780638456cb591461023c5780638da5cb5b14610244578063b2f8b88314610288578063c1ce767d1461029b578063cc3c0f06146102ae57600080fd5b80633f4ba83a116101155780633f4ba83a146101c65780635c975abb146101ce578063639924ce146101f25780636817031b14610205578063715018a61461021857806378e979251461022057600080fd5b80631a7c948e1461015d5780632eb4a7ab14610172578063304440171461018e578063304c2c03146101975780633197cbb6146101aa5780633e0a322d146101b3575b600080fd5b61017061016b366004611128565b610353565b005b61017b60025481565b6040519081526020015b60405180910390f35b61017b60035481565b6101706101a53660046111dc565b6104b5565b61017b60055481565b6101706101c1366004611253565b6105f9565b61017061064d565b6000805160206114108339815191525460ff165b6040519015158152602001610185565b6101e261020036600461126c565b61065f565b6101706102133660046112b8565b6106b7565b6101706106e1565b61017b60045481565b610170610237366004611253565b6106f3565b610170610700565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b03165b6040516001600160a01b039091168152602001610185565b610170610296366004611253565b610710565b6101706102a93660046112e1565b61071d565b6101e26102bc366004611253565b60066020526000908152604090205460ff1681565b6101706102df366004611253565b610792565b6101e26102f23660046112b8565b60076020526000908152604090205460ff1681565b610170610315366004611338565b6107e5565b6101706103283660046112b8565b6108fc565b600054610270906001600160a01b031681565b600154610270906001600160a01b031681565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff16159067ffffffffffffffff166000811580156103995750825b905060008267ffffffffffffffff1660011480156103b65750303b155b9050811580156103c4575080155b156103e25760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561040c57845460ff60401b1916600160401b1785555b61041461093a565b61041d8c61094a565b600080546001600160a01b03808e166001600160a01b03199283161790925560018054928d1692909116919091179055600289905560038890556004879055600586905583156104a757845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050505050565b6104bd61095b565b4260045411156105025760405162461bcd60e51b815260206004820152600b60248201526a139bdd081cdd185c9d195960aa1b60448201526064015b60405180910390fd5b426005541161053b5760405162461bcd60e51b8152602060048201526005602482015264115b99195960da1b60448201526064016104f9565b3360008181526007602052604090205460ff16156105855760405162461bcd60e51b8152602060048201526007602482015266109b1bd8dad95960ca1b60448201526064016104f9565b806001600160a01b031661059d60035486868661098c565b6001600160a01b0316146105e55760405162461bcd60e51b815260206004820152600f60248201526e0a6cadcc8cae440dad2e6dac2e8c6d608b1b60448201526064016104f9565b6105f08686896109ba565b50505050505050565b610601610b1a565b60055481106106485760405162461bcd60e51b815260206004820152601360248201527253746172742074696d6520746f6f206c61746560681b60448201526064016104f9565b600455565b610655610b1a565b61065d610b75565b565b6002546040516bffffffffffffffffffffffff193360601b166020820152603481018590526000916106ad918591859160540160405160208183030381529060405280519060200120610bd5565b90505b9392505050565b6106bf610b1a565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b6106e9610b1a565b61065d6000610bed565b6106fb610b1a565b600255565b610708610b1a565b61065d610c5e565b610718610b1a565b600355565b610725610b1a565b60005b8281101561078c57816007600086868581811061074757610747611397565b905060200201602081019061075c91906112b8565b6001600160a01b031681526020810191909152604001600020805460ff1916911515919091179055600101610728565b50505050565b61079a610b1a565b80600454106107e05760405162461bcd60e51b8152602060048201526012602482015271456e642074696d6520746f6f206561726c7960701b60448201526064016104f9565b600555565b6107ed61095b565b42600454111561082d5760405162461bcd60e51b815260206004820152600b60248201526a139bdd081cdd185c9d195960aa1b60448201526064016104f9565b42600554116108665760405162461bcd60e51b8152602060048201526005602482015264115b99195960da1b60448201526064016104f9565b3360009081526007602052604090205460ff16156108b05760405162461bcd60e51b8152602060048201526007602482015266109b1bd8dad95960ca1b60448201526064016104f9565b806108f15760405162461bcd60e51b81526020600482015260116024820152702a1321903737ba1030b1b1b2b83a32b21760791b60448201526064016104f9565b61078c8383866109ba565b610904610b1a565b6001600160a01b03811661092e57604051631e4fbdf760e01b8152600060048201526024016104f9565b61093781610bed565b50565b610942610ca7565b61065d610cf0565b610952610ca7565b61093781610d11565b6000805160206114108339815191525460ff161561065d5760405163d93c066560e01b815260040160405180910390fd5b60008060008061099e88888888610d19565b9250925092506109ae8282610de8565b50909695505050505050565b60405133606081901b6bffffffffffffffffffffffff19166020830152603482018390529060009060540160408051601f1981840301815291815281516020928301206000818152600690935291205490915060ff1615610a4f5760405162461bcd60e51b815260206004820152600f60248201526e105b1c9958591e4818db185a5b5959608a1b60448201526064016104f9565b610a5d858560025484610bd5565b610a995760405162461bcd60e51b815260206004820152600d60248201526c24b73b30b634b210383937b7b360991b60448201526064016104f9565b600081815260066020908152604091829020805460ff1916600117905581516001600160a01b03851681529081018590527fd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a910160405180910390a1600054600154610b13916001600160a01b0391821691168486610ea5565b5050505050565b33610b4c7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b6001600160a01b03161461065d5760405163118cdaa760e01b81523360048201526024016104f9565b610b7d610eff565b600080516020611410833981519152805460ff191681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a150565b600082610be3868685610f2f565b1495945050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b610c6661095b565b600080516020611410833981519152805460ff191660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25833610bb7565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661065d57604051631afcd79f60e31b815260040160405180910390fd5b610cf8610ca7565b600080516020611410833981519152805460ff19169055565b610904610ca7565b600080807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0841115610d545750600091506003905082610dde565b604080516000808252602082018084528a905260ff891692820192909252606081018790526080810186905260019060a0016020604051602081039080840390855afa158015610da8573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116610dd457506000925060019150829050610dde565b9250600091508190505b9450945094915050565b6000826003811115610dfc57610dfc6113ad565b03610e05575050565b6001826003811115610e1957610e196113ad565b03610e375760405163f645eedf60e01b815260040160405180910390fd5b6002826003811115610e4b57610e4b6113ad565b03610e6c5760405163fce698f760e01b8152600481018290526024016104f9565b6003826003811115610e8057610e806113ad565b03610ea1576040516335e2f38360e21b8152600481018290526024016104f9565b5050565b604080516001600160a01b0385811660248301528416604482015260648082018490528251808303909101815260849091019091526020810180516001600160e01b03166323b872dd60e01b17905261078c908590610f71565b6000805160206114108339815191525460ff1661065d57604051638dfc202b60e01b815260040160405180910390fd5b600081815b84811015610f6857610f5e82878784818110610f5257610f52611397565b90506020020135610fd9565b9150600101610f34565b50949350505050565b6000610f866001600160a01b03841683611005565b90508051600014158015610fab575080806020019051810190610fa991906113c3565b155b15610fd457604051635274afe760e01b81526001600160a01b03841660048201526024016104f9565b505050565b6000818310610ff55760008281526020849052604090206106b0565b5060009182526020526040902090565b60606106b08383600084600080856001600160a01b0316848660405161102b91906113e0565b60006040518083038185875af1925050503d8060008114611068576040519150601f19603f3d011682016040523d82523d6000602084013e61106d565b606091505b509150915061107d868383611087565b9695505050505050565b60608261109c57611097826110e3565b6106b0565b81511580156110b357506001600160a01b0384163b155b156110dc57604051639996b31560e01b81526001600160a01b03851660048201526024016104f9565b50806106b0565b8051156110f35780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b80356001600160a01b038116811461112357600080fd5b919050565b600080600080600080600060e0888a03121561114357600080fd5b61114c8861110c565b965061115a6020890161110c565b95506111686040890161110c565b969995985095966060810135965060808101359560a0820135955060c0909101359350915050565b60008083601f8401126111a257600080fd5b50813567ffffffffffffffff8111156111ba57600080fd5b6020830191508360208260051b85010111156111d557600080fd5b9250929050565b60008060008060008060a087890312156111f557600080fd5b86359550602087013567ffffffffffffffff81111561121357600080fd5b61121f89828a01611190565b909650945050604087013560ff8116811461123957600080fd5b959894975092956060810135946080909101359350915050565b60006020828403121561126557600080fd5b5035919050565b60008060006040848603121561128157600080fd5b83359250602084013567ffffffffffffffff81111561129f57600080fd5b6112ab86828701611190565b9497909650939450505050565b6000602082840312156112ca57600080fd5b6106b08261110c565b801515811461093757600080fd5b6000806000604084860312156112f657600080fd5b833567ffffffffffffffff81111561130d57600080fd5b61131986828701611190565b909450925050602084013561132d816112d3565b809150509250925092565b6000806000806060858703121561134e57600080fd5b84359350602085013567ffffffffffffffff81111561136c57600080fd5b61137887828801611190565b909450925050604085013561138c816112d3565b939692955090935050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052602160045260246000fd5b6000602082840312156113d557600080fd5b81516106b0816112d3565b6000825160005b8181101561140157602081860181015185830152016113e7565b50600092019182525091905056fecd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300a264697066735822122031fda5b1878e1e20e6d71eaf807640281d410002a9df7658467343791e9e910464736f6c63430008170033 -------------------------------------------------------------------------------- /data/bswap64.rt.hex: -------------------------------------------------------------------------------- 1 | 0x608060048036101561000f575f80fd5b5f3560e01c63ff2f79f114610022575f80fd5b346101de5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101de5780359167ffffffffffffffff9182841684036101de57604092828401908111838210176101b2578352600882527fffffffffffffffff0000000000000000000000000000000000000000000000006020830194602036873760c01b1690825115610186578160071a8553825160011015610186578160061a6021840153825160021015610186578160051a60228401538251600310156101865781811a60238401538251811015610186578160031a6024840153825160051015610186578160021a6025840153825160061015610186578160011a602684015382516007101561018657505f1a60278201537fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8351948593602085525180918160208701528686015e5f85828601015201168101030190f35b6032907f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b6041827f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b5f80fdfea26469706673582212208f1d6d698d50560a1947599f8855b4bc10e155c6e665bd6833a8d1687852a17064736f6c63430008190033 2 | 3 | contract C { 4 | function to_little_endian_64(uint64 value) public pure returns (bytes memory ret) { 5 | ret = new bytes(8); 6 | bytes8 bytesValue = bytes8(value); 7 | // Byteswapping during copying to bytes. 8 | ret[0] = bytesValue[7]; 9 | ret[1] = bytesValue[6]; 10 | ret[2] = bytesValue[5]; 11 | ret[3] = bytesValue[4]; 12 | ret[4] = bytesValue[3]; 13 | ret[5] = bytesValue[2]; 14 | ret[6] = bytesValue[1]; 15 | ret[7] = bytesValue[0]; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /data/bswap64_opt.rt.hex: -------------------------------------------------------------------------------- 1 | 0x60806040526004361015610011575f80fd5b5f3560e01c63ff2f79f114610024575f80fd5b346100ed5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100ed5760043567ffffffffffffffff811681036100ed576100e990610073610139565b9067ff000000000000008160381b169066ff0000000000008160281b169065ff00000000008160181b169064ff000000008160081b169063ff0000008160081c169062ff00008160181c169060ff61ff008260281c169160381c161717171717171760c01b6020820152604051918291826100f1565b0390f35b5f80fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602060409481855280519182918282880152018686015e5f8582860101520116010190565b604051906040820182811067ffffffffffffffff821117610164576040526008825260203681840137565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffdfea2646970667358221220b70b4e488cd362e10f6925a4da52a953b746633ef939bf96e967b5c6b5e600d364736f6c63430008190033 2 | 3 | contract C { 4 | function to_little_endian_64(uint64 x) public pure returns (bytes memory ret) { 5 | uint64 swapped = 6 | (((x & 0xff00000000000000) >> 56) 7 | | ((x & 0x00ff000000000000) >> 40) 8 | | ((x & 0x0000ff0000000000) >> 24) 9 | | ((x & 0x000000ff00000000) >> 8) 10 | | ((x & 0x00000000ff000000) << 8) 11 | | ((x & 0x0000000000ff0000) << 24) 12 | | ((x & 0x000000000000ff00) << 40) 13 | | ((x & 0x00000000000000ff) << 56)); 14 | 15 | ret = new bytes(8); 16 | assembly { 17 | mstore(add(ret, 0x20), shl(192, swapped)) 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /data/counter-eof.rt.hex: -------------------------------------------------------------------------------- 1 | ef0001010004020001013c04006d00008000056080806040526004361015e100035f80fd5f3560e01c9081633fb5c1cb14e100e081638381f58a14e1009c5063d09de08a14e100045fe0ffd534e100875f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112e1005c5f547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114e100086001015f555f80f37f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5f80fd5f80fd34e100335f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112e100086020905f548152f35f80fd5f80fd34e1003460207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112e100086004355f555f80f35f80fd5f80fda3646970667358221220cc6570f0f9fb641c08c7d9d1c36810bd19987855bcd8f9ccde7cfcd8670b41fa6c6578706572696d656e74616cf564736f6c63782c302e382e32372d646576656c6f702e323032342e372e32342b636f6d6d69742e64353139363430342e6d6f64006b -------------------------------------------------------------------------------- /data/counter.rt.hex: -------------------------------------------------------------------------------- 1 | 6080806040526004361015610012575f80fd5b5f3560e01c9081633fb5c1cb146101035781638381f58a146100cc575063d09de08a1461003d575f80fd5b346100c8575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100c8575f547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811461009b576001015f55005b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f80fd5b346100c8575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100c8576020905f548152f35b346100c85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100c8576004355f5500fea264697066735822122019105920c33dab502c8d24693bd24f1b4b0fbb562ad3a475173a15b7aa3574f064736f6c63430008190033 -------------------------------------------------------------------------------- /data/erc20_transfer.rt.hex: -------------------------------------------------------------------------------- 1 | 608060405234801561000f575f80fd5b50600436106100cf575f3560e01c8063395093511161007d578063a457c2d711610058578063a457c2d7146101a2578063a9059cbb146101b5578063dd62ed3e146101c8575f80fd5b8063395093511461015257806370a082311461016557806395d89b411461019a575f80fd5b806323b872dd116100ad57806323b872dd1461012657806330627b7c14610139578063313ce56714610143575f80fd5b806306fdde03146100d3578063095ea7b3146100f157806318160ddd14610114575b5f80fd5b6100db61020d565b6040516100e89190610a3f565b60405180910390f35b6101046100ff366004610aba565b61029d565b60405190151581526020016100e8565b6002545b6040519081526020016100e8565b610104610134366004610ae2565b6102b6565b6101416102d9565b005b604051601281526020016100e8565b610104610160366004610aba565b61031d565b610118610173366004610b1b565b73ffffffffffffffffffffffffffffffffffffffff165f9081526020819052604090205490565b6100db610368565b6101046101b0366004610aba565b610377565b6101046101c3366004610aba565b61044c565b6101186101d6366004610b3b565b73ffffffffffffffffffffffffffffffffffffffff9182165f90815260016020908152604080832093909416825291909152205490565b60606003805461021c90610b6c565b80601f016020809104026020016040519081016040528092919081815260200182805461024890610b6c565b80156102935780601f1061026a57610100808354040283529160200191610293565b820191905f5260205f20905b81548152906001019060200180831161027657829003601f168201915b5050505050905090565b5f336102aa818585610459565b60019150505b92915050565b5f336102c385828561060b565b6102ce8585856106e1565b506001949350505050565b6102fa336102e96012600a610d08565b6102f590612710610d16565b61094e565b5f5b61138881101561031a5761031160018261044c565b506001016102fc565b50565b335f81815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff871684529091528120549091906102aa9082908690610363908790610d2d565b610459565b60606004805461021c90610b6c565b335f81815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff871684529091528120549091908381101561043f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f00000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b6102ce8286868403610459565b5f336102aa8185856106e1565b73ffffffffffffffffffffffffffffffffffffffff83166104fb576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610436565b73ffffffffffffffffffffffffffffffffffffffff821661059e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152608401610436565b73ffffffffffffffffffffffffffffffffffffffff8381165f8181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff8381165f908152600160209081526040808320938616835292905220547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146106db57818110156106ce576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610436565b6106db8484848403610459565b50505050565b73ffffffffffffffffffffffffffffffffffffffff8316610784576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152608401610436565b73ffffffffffffffffffffffffffffffffffffffff8216610827576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152608401610436565b73ffffffffffffffffffffffffffffffffffffffff83165f90815260208190526040902054818110156108dc576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152608401610436565b73ffffffffffffffffffffffffffffffffffffffff8481165f81815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a36106db565b73ffffffffffffffffffffffffffffffffffffffff82166109cb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610436565b8060025f8282546109dc9190610d2d565b909155505073ffffffffffffffffffffffffffffffffffffffff82165f81815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610ab5575f80fd5b919050565b5f8060408385031215610acb575f80fd5b610ad483610a92565b946020939093013593505050565b5f805f60608486031215610af4575f80fd5b610afd84610a92565b9250610b0b60208501610a92565b9150604084013590509250925092565b5f60208284031215610b2b575f80fd5b610b3482610a92565b9392505050565b5f8060408385031215610b4c575f80fd5b610b5583610a92565b9150610b6360208401610a92565b90509250929050565b600181811c90821680610b8057607f821691505b602082108103610bb7577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b600181815b80851115610c4357817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115610c2957610c29610bbd565b80851615610c3657918102915b93841c9390800290610bef565b509250929050565b5f82610c59575060016102b0565b81610c6557505f6102b0565b8160018114610c7b5760028114610c8557610ca1565b60019150506102b0565b60ff841115610c9657610c96610bbd565b50506001821b6102b0565b5060208310610133831016604e8410600b8410161715610cc4575081810a6102b0565b610cce8383610bea565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04821115610d0057610d00610bbd565b029392505050565b5f610b3460ff841683610c4b565b80820281158282048414176102b0576102b0610bbd565b808201808211156102b0576102b0610bbd56fea26469706673582212202876b78b10607faa6f80d7286e1d3c837a871525c86393c77cfefde3b06bbefa64736f6c63430008190033 -------------------------------------------------------------------------------- /data/hash_10k-eof.rt.hex: -------------------------------------------------------------------------------- 1 | ef000101000402000100c604006d000080000760806040526004361015e100035f80fd5f3560e01c6330627b7c1415e1ffee34e100a05f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112e100755f806127108210e1000a602090604051908152f3604051906020820190815260208252604082019180831067ffffffffffffffff841117e1000f600192604052519020910190e0ffb97f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5f80fd5f80fda3646970667358221220cd27a637eeed27ea6b9fcdffcb8a7ffd83e1bf9bc5cfc0df27c8617a28d6a8c06c6578706572696d656e74616cf564736f6c63782c302e382e32372d646576656c6f702e323032342e372e32342b636f6d6d69742e64353139363430342e6d6f64006b -------------------------------------------------------------------------------- /data/hash_10k.rt.hex: -------------------------------------------------------------------------------- 1 | 6080604052348015600e575f80fd5b50600436106026575f3560e01c806330627b7c14602a575b5f80fd5b60306042565b60405190815260200160405180910390f35b5f805b612710811015609957604080516020810184905201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152919052805160209091012091506001016045565b509056fea26469706673582212205619b6f35e6b36e84f322068af3e58f103c6c5317af1da5cbb6f136edc8e3be564736f6c63430008190033 -------------------------------------------------------------------------------- /data/push0_proxy.rt.hex: -------------------------------------------------------------------------------- 1 | 365f5f375f5f365f73bebebebebebebebebebebebebebebebebebebebe5af43d5f5f3e5f3d91602a57fd5bf3 -------------------------------------------------------------------------------- /data/usdc_proxy.rt.hex: -------------------------------------------------------------------------------- 1 | 0x60806040526004361061006d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633659cfe6146100775780634f1ef286146100ba5780635c60da1b146101085780638f2839701461015f578063f851a440146101a2575b6100756101f9565b005b34801561008357600080fd5b506100b8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610213565b005b610106600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001908201803590602001919091929391929390505050610268565b005b34801561011457600080fd5b5061011d610308565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561016b57600080fd5b506101a0600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610360565b005b3480156101ae57600080fd5b506101b761051e565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610201610576565b61021161020c610651565b610682565b565b61021b6106a8565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561025c57610257816106d9565b610265565b6102646101f9565b5b50565b6102706106a8565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102fa576102ac836106d9565b3073ffffffffffffffffffffffffffffffffffffffff163483836040518083838082843782019150509250505060006040518083038185875af19250505015156102f557600080fd5b610303565b6103026101f9565b5b505050565b60006103126106a8565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156103545761034d610651565b905061035d565b61035c6101f9565b5b90565b6103686106a8565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561051257600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614151515610466576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260368152602001807f43616e6e6f74206368616e6765207468652061646d696e206f6620612070726f81526020017f787920746f20746865207a65726f20616464726573730000000000000000000081525060400191505060405180910390fd5b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f61048f6106a8565b82604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a161050d81610748565b61051b565b61051a6101f9565b5b50565b60006105286106a8565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561056a576105636106a8565b9050610573565b6105726101f9565b5b90565b61057e6106a8565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151515610647576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260328152602001807f43616e6e6f742063616c6c2066616c6c6261636b2066756e6374696f6e20667281526020017f6f6d207468652070726f78792061646d696e000000000000000000000000000081525060400191505060405180910390fd5b61064f610777565b565b6000807f7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c36001029050805491505090565b3660008037600080366000845af43d6000803e80600081146106a3573d6000f35b3d6000fd5b6000807f10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b6001029050805491505090565b6106e281610779565b7fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b81604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a150565b60007f10d6a54a4754c8869d6886b5f5d7fbfa5b4522237ea5c60d11bc4e7a1ff9390b60010290508181555050565b565b60006107848261084b565b151561081e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252603b8152602001807f43616e6e6f742073657420612070726f787920696d706c656d656e746174696f81526020017f6e20746f2061206e6f6e2d636f6e74726163742061646472657373000000000081525060400191505060405180910390fd5b7f7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c360010290508181555050565b600080823b9050600081119150509190505600a165627a7a72305820a4a547cfc7202c5acaaae74d428e988bc62ad5024eb0165532d3a8f91db4ed240029 -------------------------------------------------------------------------------- /data/weth.rt.hex: -------------------------------------------------------------------------------- 1 | 0x6060604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b9578063095ea7b31461014757806318160ddd146101a157806323b872dd146101ca5780632e1a7d4d14610243578063313ce5671461026657806370a082311461029557806395d89b41146102e2578063a9059cbb14610370578063d0e30db0146103ca578063dd62ed3e146103d4575b6100b7610440565b005b34156100c457600080fd5b6100cc6104dd565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561010c5780820151818401526020810190506100f1565b50505050905090810190601f1680156101395780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561015257600080fd5b610187600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061057b565b604051808215151515815260200191505060405180910390f35b34156101ac57600080fd5b6101b461066d565b6040518082815260200191505060405180910390f35b34156101d557600080fd5b610229600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061068c565b604051808215151515815260200191505060405180910390f35b341561024e57600080fd5b61026460048080359060200190919050506109d9565b005b341561027157600080fd5b610279610b05565b604051808260ff1660ff16815260200191505060405180910390f35b34156102a057600080fd5b6102cc600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610b18565b6040518082815260200191505060405180910390f35b34156102ed57600080fd5b6102f5610b30565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561033557808201518184015260208101905061031a565b50505050905090810190601f1680156103625780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561037b57600080fd5b6103b0600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610bce565b604051808215151515815260200191505060405180910390f35b6103d2610440565b005b34156103df57600080fd5b61042a600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610be3565b6040518082815260200191505060405180910390f35b34600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055503373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a2565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105735780601f1061054857610100808354040283529160200191610573565b820191906000526020600020905b81548152906001019060200180831161055657829003601f168201915b505050505081565b600081600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60003073ffffffffffffffffffffffffffffffffffffffff1631905090565b600081600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101515156106dc57600080fd5b3373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16141580156107b457507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414155b156108cf5781600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015151561084457600080fd5b81600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b81600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610a2757600080fd5b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055503373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501515610ab457600080fd5b3373ffffffffffffffffffffffffffffffffffffffff167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65826040518082815260200191505060405180910390a250565b600260009054906101000a900460ff1681565b60036020528060005260406000206000915090505481565b60018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610bc65780601f10610b9b57610100808354040283529160200191610bc6565b820191906000526020600020905b815481529060010190602001808311610ba957829003601f168201915b505050505081565b6000610bdb33848461068c565b905092915050565b60046020528160005260406000206020528060005260406000206000915091505054815600a165627a7a72305820deb4c2ccab3c2fdca32ab3f46728389c2fe2c165d5fafa07661e4e004f6c344a0029 -------------------------------------------------------------------------------- /examples/compiler/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "revmc-examples-compiler" 3 | publish = false 4 | 5 | version.workspace = true 6 | authors.workspace = true 7 | edition.workspace = true 8 | rust-version.workspace = true 9 | license.workspace = true 10 | categories.workspace = true 11 | keywords.workspace = true 12 | repository.workspace = true 13 | exclude.workspace = true 14 | 15 | [build-dependencies] 16 | revmc-build.workspace = true 17 | 18 | [dependencies] 19 | revmc = { workspace = true, features = ["llvm-prefer-dynamic"] } 20 | 21 | clap = { version = "4", features = ["derive"] } 22 | eyre.workspace = true 23 | -------------------------------------------------------------------------------- /examples/compiler/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Emit the configuration to run compiled bytecodes. 3 | revmc_build::emit(); 4 | } 5 | -------------------------------------------------------------------------------- /examples/compiler/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Simple JIT compiler example. 2 | //! 3 | //! For a more complete example, see the `revmc-cli` crate. 4 | 5 | use clap::Parser; 6 | use eyre::Context; 7 | use revmc::{ 8 | interpreter::{Contract, DummyHost, Interpreter}, 9 | primitives::SpecId, 10 | EvmCompiler, EvmLlvmBackend, OptimizationLevel, 11 | }; 12 | use std::path::PathBuf; 13 | 14 | #[derive(Parser)] 15 | struct Cli { 16 | #[arg(long, required_unless_present = "code_path")] 17 | code: Option, 18 | #[arg(long, conflicts_with = "code")] 19 | code_path: Option, 20 | } 21 | 22 | fn main() -> eyre::Result<()> { 23 | // Parse CLI arguments. 24 | let cli = Cli::parse(); 25 | let code = match (cli.code, cli.code_path) { 26 | (Some(code), None) => code, 27 | (None, Some(path)) => std::fs::read_to_string(&path) 28 | .wrap_err_with(|| format!("Failed to read code from file: {path:?}"))?, 29 | _ => unreachable!(), 30 | }; 31 | let bytecode = revmc::primitives::hex::decode(code.trim()) 32 | .wrap_err("Failed to decode hex-encoded code")?; 33 | 34 | // Compile the code. 35 | let context = revmc::llvm::inkwell::context::Context::create(); 36 | let backend = EvmLlvmBackend::new(&context, false, OptimizationLevel::Aggressive)?; 37 | let mut compiler = EvmCompiler::new(backend); 38 | let f = unsafe { compiler.jit("test", &bytecode[..], SpecId::CANCUN) } 39 | .wrap_err("Failed to JIT-compile code")?; 40 | 41 | // Set up runtime context and run the function. 42 | let mut interpreter = Interpreter::new(Contract::default(), 1_000_000, false); 43 | let mut host = DummyHost::default(); 44 | let result = unsafe { f.call_with_interpreter(&mut interpreter, &mut host) }; 45 | eprintln!("{result:#?}"); 46 | 47 | Ok(()) 48 | } 49 | -------------------------------------------------------------------------------- /examples/runner/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "revmc-examples-runner" 3 | publish = false 4 | 5 | version.workspace = true 6 | authors.workspace = true 7 | edition.workspace = true 8 | rust-version.workspace = true 9 | license.workspace = true 10 | categories.workspace = true 11 | keywords.workspace = true 12 | repository.workspace = true 13 | exclude.workspace = true 14 | 15 | [build-dependencies] 16 | revmc = { workspace = true, features = ["llvm-prefer-dynamic"] } 17 | revmc-build.workspace = true 18 | cc = "1.0" 19 | 20 | [dependencies] 21 | revmc-builtins = { workspace = true, default-features = false } 22 | revmc-context = { workspace = true, default-features = false } 23 | 24 | revm = { workspace = true, default-features = false } 25 | -------------------------------------------------------------------------------- /examples/runner/build.rs: -------------------------------------------------------------------------------- 1 | use revmc::{ 2 | primitives::{hex, SpecId}, 3 | EvmCompiler, EvmLlvmBackend, OptimizationLevel, Result, 4 | }; 5 | use std::path::PathBuf; 6 | 7 | include!("./src/common.rs"); 8 | 9 | fn main() -> Result<()> { 10 | // Emit the configuration to run compiled bytecodes. 11 | // This not used if we are only using statically linked bytecodes. 12 | revmc_build::emit(); 13 | 14 | // Compile and statically link a bytecode. 15 | let name = "fibonacci"; 16 | let bytecode = FIBONACCI_CODE; 17 | 18 | let out_dir = PathBuf::from(std::env::var("OUT_DIR")?); 19 | let context = revmc::llvm::inkwell::context::Context::create(); 20 | let backend = EvmLlvmBackend::new(&context, true, OptimizationLevel::Aggressive)?; 21 | let mut compiler = EvmCompiler::new(backend); 22 | compiler.translate(name, bytecode, SpecId::CANCUN)?; 23 | let object = out_dir.join(name).with_extension("o"); 24 | compiler.write_object_to_file(&object)?; 25 | 26 | cc::Build::new().object(&object).static_flag(true).compile(name); 27 | 28 | Ok(()) 29 | } 30 | -------------------------------------------------------------------------------- /examples/runner/src/common.rs: -------------------------------------------------------------------------------- 1 | #[allow(dead_code)] 2 | const FIBONACCI_CODE: &[u8] = 3 | &hex!("5f355f60015b8215601a578181019150909160019003916005565b9150505f5260205ff3"); 4 | #[allow(dead_code)] 5 | const FIBONACCI_HASH: [u8; 32] = 6 | hex!("ab1ad1211002e1ddb8d9a4ef58a902224851f6a0273ee3e87276a8d21e649ce8"); 7 | -------------------------------------------------------------------------------- /examples/runner/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Minimal, `no_std` runner. 2 | 3 | #![no_std] 4 | 5 | extern crate alloc; 6 | 7 | // This dependency is needed to define the necessary symbols used by the compiled bytecodes, 8 | // but we don't use it directly, so silence the unused crate dependency warning. 9 | use revmc_builtins as _; 10 | 11 | use alloc::sync::Arc; 12 | use revm::{ 13 | handler::register::EvmHandler, 14 | primitives::{hex, B256}, 15 | Database, 16 | }; 17 | use revmc_context::EvmCompilerFn; 18 | 19 | include!("./common.rs"); 20 | 21 | // The bytecode we statically linked. 22 | revmc_context::extern_revmc! { 23 | fn fibonacci; 24 | } 25 | 26 | /// Build a [`revm::Evm`] with a custom handler that can call compiled functions. 27 | pub fn build_evm<'a, DB: Database + 'static>(db: DB) -> revm::Evm<'a, ExternalContext, DB> { 28 | revm::Evm::builder() 29 | .with_db(db) 30 | .with_external_context(ExternalContext::new()) 31 | .append_handler_register(register_handler) 32 | .build() 33 | } 34 | 35 | pub struct ExternalContext; 36 | 37 | impl ExternalContext { 38 | fn new() -> Self { 39 | Self 40 | } 41 | 42 | fn get_function(&self, bytecode_hash: B256) -> Option { 43 | // Can use any mapping between bytecode hash and function. 44 | if bytecode_hash == FIBONACCI_HASH { 45 | return Some(EvmCompilerFn::new(fibonacci)); 46 | } 47 | 48 | None 49 | } 50 | } 51 | 52 | // This `+ 'static` bound is only necessary here because of an internal cfg feature. 53 | fn register_handler(handler: &mut EvmHandler<'_, ExternalContext, DB>) { 54 | let prev = handler.execution.execute_frame.clone(); 55 | handler.execution.execute_frame = Arc::new(move |frame, memory, tables, context| { 56 | let interpreter = frame.interpreter_mut(); 57 | let bytecode_hash = interpreter.contract.hash.unwrap_or_default(); 58 | if let Some(f) = context.external.get_function(bytecode_hash) { 59 | Ok(unsafe { f.call_with_interpreter_and_memory(interpreter, memory, context) }) 60 | } else { 61 | prev(frame, memory, tables, context) 62 | } 63 | }); 64 | } 65 | -------------------------------------------------------------------------------- /examples/runner/src/main.rs: -------------------------------------------------------------------------------- 1 | use revm::{ 2 | db::{CacheDB, EmptyDB}, 3 | primitives::{address, hex, AccountInfo, Bytecode, TransactTo, U256}, 4 | }; 5 | use revmc_examples_runner::build_evm; 6 | 7 | include!("./common.rs"); 8 | 9 | fn main() { 10 | let num = 11 | std::env::args().nth(1).map(|s| s.parse().unwrap()).unwrap_or_else(|| U256::from(100)); 12 | // The bytecode runs fib(input + 1), so we need to subtract 1. 13 | let actual_num = num.saturating_sub(U256::from(1)); 14 | 15 | let db = CacheDB::new(EmptyDB::new()); 16 | let mut evm = build_evm(db); 17 | let fibonacci_address = address!("0000000000000000000000000000000000001234"); 18 | evm.db_mut().insert_account_info( 19 | fibonacci_address, 20 | AccountInfo { 21 | code_hash: FIBONACCI_HASH.into(), 22 | code: Some(Bytecode::new_raw(FIBONACCI_CODE.into())), 23 | ..Default::default() 24 | }, 25 | ); 26 | evm.context.evm.env.tx.transact_to = TransactTo::Call(fibonacci_address); 27 | evm.context.evm.env.tx.data = actual_num.to_be_bytes_vec().into(); 28 | let result = evm.transact().unwrap(); 29 | // eprintln!("{:#?}", result.result); 30 | 31 | println!("fib({num}) = {}", U256::from_be_slice(result.result.output().unwrap())); 32 | } 33 | -------------------------------------------------------------------------------- /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | corpus 3 | artifacts 4 | coverage 5 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "revmc-fuzz" 3 | version = "0.0.0" 4 | publish = false 5 | edition = "2021" 6 | 7 | [package.metadata] 8 | cargo-fuzz = true 9 | 10 | [dependencies] 11 | libfuzzer-sys = "0.4" 12 | revmc = { workspace = true, features = ["llvm-prefer-dynamic", "__fuzzing"] } 13 | eyre.workspace = true 14 | 15 | [build-dependencies] 16 | revmc-build.workspace = true 17 | 18 | [[bin]] 19 | name = "vs_interpreter" 20 | path = "fuzz_targets/vs_interpreter.rs" 21 | test = false 22 | doc = false 23 | bench = false 24 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/vs_interpreter.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use libfuzzer_sys::fuzz_target; 4 | use revmc::{ 5 | interpreter::OPCODE_INFO_JUMPTABLE, 6 | primitives::SpecId, 7 | tests::{run_test_case, TestCase}, 8 | EvmCompiler, EvmLlvmBackend, OpcodesIter, OptimizationLevel, 9 | }; 10 | use std::path::PathBuf; 11 | 12 | fuzz_target!(|test_case: TestCase<'_>| { 13 | if should_skip(test_case.bytecode, test_case.spec_id) { 14 | return; 15 | } 16 | 17 | let context = revmc::llvm::inkwell::context::Context::create(); 18 | let backend = EvmLlvmBackend::new(&context, false, OptimizationLevel::None).unwrap(); 19 | let mut compiler = EvmCompiler::new(backend); 20 | if let Ok(dump_location) = std::env::var("COMPILER_DUMP") { 21 | compiler.set_dump_to(Some(PathBuf::from(dump_location))); 22 | } 23 | run_test_case(&test_case, &mut compiler); 24 | }); 25 | 26 | fn should_skip(bytecode: &[u8], spec_id: SpecId) -> bool { 27 | OpcodesIter::new(bytecode, spec_id).any(|op| { 28 | let Some(info) = OPCODE_INFO_JUMPTABLE[op.opcode as usize] else { return true }; 29 | // Skip if the immediate is incomplete. 30 | // TODO: What is the expected behavior here? 31 | if info.immediate_size() > 0 && op.immediate.is_none() { 32 | return true; 33 | } 34 | false 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /release.toml: -------------------------------------------------------------------------------- 1 | # Configuration file for [`cargo-release`](https://github.com/crate-ci/cargo-release) 2 | # See: https://github.com/crate-ci/cargo-release/blob/master/docs/reference.md 3 | 4 | allow-branch = ["main"] 5 | sign-commit = true 6 | sign-tag = true 7 | shared-version = true 8 | pre-release-commit-message = "chore: release {{version}}" 9 | tag-prefix = "" # tag only once instead of per every crate 10 | pre-release-hook = [ 11 | "bash", 12 | "-c", 13 | "$WORKSPACE_ROOT/scripts/changelog.sh --tag {{version}}", 14 | ] 15 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | reorder_imports = true 2 | use_field_init_shorthand = true 3 | use_small_heuristics = "Max" 4 | 5 | # Nightly 6 | max_width = 100 7 | comment_width = 100 8 | imports_granularity = "Crate" 9 | wrap_comments = true 10 | format_code_in_doc_comments = true 11 | doc_comment_code_block_width = 100 12 | format_macro_matchers = true 13 | -------------------------------------------------------------------------------- /scripts/changelog.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | run_unless_dry_run() { 5 | if [ "$DRY_RUN" = "true" ]; then 6 | echo "skipping due to dry run: $*" >&2 7 | else 8 | "$@" 9 | fi 10 | } 11 | 12 | root=$WORKSPACE_ROOT 13 | crate=$CRATE_ROOT 14 | crate_glob="${crate#"$root/"}/**" 15 | 16 | if [[ "$crate" = */tests/* || "$crate" = */examples/* || "$crate" = *test-utils* || "$crate" = "revmc-cli" ]]; then 17 | exit 0 18 | fi 19 | 20 | command=(git cliff --workdir "$root" --config "$root/cliff.toml" "${@}") 21 | run_unless_dry_run "${command[@]}" --output "$root/CHANGELOG.md" 22 | if [ -n "$crate" ] && [ "$root" != "$crate" ]; then 23 | run_unless_dry_run "${command[@]}" --include-path "$crate_glob" --output "$crate/CHANGELOG.md" 24 | fi 25 | -------------------------------------------------------------------------------- /tests/codegen/address_mask.evm: -------------------------------------------------------------------------------- 1 | ADDRESS 2 | PUSH20 0xffffffffffffffffffffffffffffffffffffffff 3 | AND 4 | 5 | ; CHECK: load i160 6 | ; CHECK: [[ADDR:%.*]] = zext i160 {{.*}} to i256 7 | ; CHECK-NOT: and 8 | ; CHECK: store i256 [[ADDR]] 9 | ; CHECK: ret i8 10 | -------------------------------------------------------------------------------- /tests/codegen/non_zero.evm: -------------------------------------------------------------------------------- 1 | CALLVALUE 2 | ISZERO 3 | ISZERO 4 | 5 | ; CHECK: [[VALUE:%.*value.*]] = load i256 6 | ; CHECK-NEXT: [[NON_ZERO:%.*]] = icmp ne i256 [[VALUE]], 0 7 | ; CHECK-NEXT: [[EXTENDED:%.*]] = zext i1 [[NON_ZERO]] to i256 8 | ; CHECK-NEXT: store i256 [[EXTENDED]] 9 | ; CHECK-NEXT: br label %return 10 | --------------------------------------------------------------------------------