├── .cargo └── config.toml ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── pull_request_template.md └── workflows │ ├── ci.yaml │ ├── dependencies.yaml │ ├── dependencies_check.yaml │ └── secrets-scanner.yaml ├── .gitignore ├── .gitmodules ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── deny.toml ├── rust-toolchain.toml └── src ├── const.rs ├── context ├── attribute │ ├── memory.rs │ └── mod.rs ├── function │ ├── block │ │ ├── evmla_data.rs │ │ ├── key.rs │ │ └── mod.rs │ ├── declaration.rs │ ├── evmla_data.rs │ ├── mod.rs │ └── return.rs ├── loop.rs ├── mod.rs ├── pointer.rs ├── traits │ ├── address_space.rs │ ├── evmla_data.rs │ ├── evmla_function.rs │ ├── mod.rs │ ├── solidity_data.rs │ └── yul_data.rs └── value.rs ├── debug_config ├── ir_type.rs └── mod.rs ├── debug_info.rs ├── eravm ├── build.rs ├── const.rs ├── context │ ├── address_space.rs │ ├── evmla_data.rs │ ├── function │ │ ├── intrinsics.rs │ │ ├── llvm_runtime.rs │ │ ├── mod.rs │ │ ├── runtime │ │ │ ├── default_call.rs │ │ │ ├── deploy_code.rs │ │ │ ├── deployer_call.rs │ │ │ ├── entry.rs │ │ │ ├── mod.rs │ │ │ └── runtime_code.rs │ │ ├── vyper_data.rs │ │ └── yul_data.rs │ ├── global.rs │ ├── mod.rs │ ├── solidity_data.rs │ ├── tests.rs │ ├── vyper_data.rs │ └── yul_data.rs ├── evm │ ├── arithmetic.rs │ ├── bitwise.rs │ ├── call.rs │ ├── calldata.rs │ ├── comparison.rs │ ├── context.rs │ ├── create.rs │ ├── crypto.rs │ ├── ether_gas.rs │ ├── event.rs │ ├── ext_code.rs │ ├── immutable.rs │ ├── math.rs │ ├── memory.rs │ ├── mod.rs │ ├── return.rs │ ├── return_data.rs │ └── storage.rs ├── extensions │ ├── abi.rs │ ├── call.rs │ ├── const_array.rs │ ├── general.rs │ ├── math.rs │ └── mod.rs ├── mod.rs └── utils.rs ├── evm ├── build.rs ├── const.rs ├── context │ ├── address_space.rs │ ├── evmla_data.rs │ ├── function │ │ ├── intrinsics.rs │ │ ├── mod.rs │ │ ├── runtime │ │ │ ├── entry.rs │ │ │ └── mod.rs │ │ └── vyper_data.rs │ ├── mod.rs │ ├── solidity_data.rs │ └── yul_data.rs ├── instructions │ ├── arithmetic.rs │ ├── bitwise.rs │ ├── call.rs │ ├── calldata.rs │ ├── code.rs │ ├── comparison.rs │ ├── context.rs │ ├── create.rs │ ├── ether_gas.rs │ ├── event.rs │ ├── immutable.rs │ ├── math.rs │ ├── memory.rs │ ├── mod.rs │ ├── return.rs │ ├── return_data.rs │ └── storage.rs ├── mod.rs └── warning.rs ├── lib.rs ├── optimizer ├── mod.rs └── settings │ ├── mod.rs │ └── size_level.rs └── target_machine.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | rustflags = [ 3 | "-W", "missing_docs", 4 | ] 5 | incremental = true 6 | 7 | [tools.clippy] 8 | warn = [ 9 | "missing_docs_in_private_items", 10 | ] 11 | 12 | [target.x86_64-apple-darwin] 13 | rustflags = [ 14 | "-W", "missing_docs", 15 | "-C", "link-arg=-mmacosx-version-min=11.0", 16 | ] 17 | 18 | [target.aarch64-apple-darwin] 19 | rustflags = [ 20 | "-W", "missing_docs", 21 | "-C", "link-arg=-mmacosx-version-min=11.0", 22 | ] 23 | 24 | [env] 25 | LLVM_SYS_191_PREFIX = { value = "./target-llvm/target-final/", relative = true, force = false } 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Use this template for reporting issues 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | ### 🐛 Bug Report 10 | 11 | #### 📝 Description 12 | 13 | Provide a clear and concise description of the bug. 14 | 15 | #### 🔄 Reproduction Steps 16 | 17 | Steps to reproduce the behaviour 18 | 19 | #### 🤔 Expected Behavior 20 | 21 | Describe what you expected to happen. 22 | 23 | #### 😯 Current Behavior 24 | 25 | Describe what actually happened. 26 | 27 | #### 🖥️ Environment 28 | 29 | Any relevant environment details. 30 | 31 | #### 📋 Additional Context 32 | 33 | Add any other context about the problem here. If applicable, add screenshots to help explain. 34 | 35 | #### 📎 Log Output 36 | 37 | ``` 38 | Paste any relevant log output here. 39 | ``` 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Use this template for requesting features 4 | title: '' 5 | labels: feat 6 | assignees: '' 7 | --- 8 | 9 | ### 🌟 Feature Request 10 | 11 | #### 📝 Description 12 | 13 | Provide a clear and concise description of the feature you'd like to see. 14 | 15 | #### 🤔 Rationale 16 | 17 | Explain why this feature is important and how it benefits the project. 18 | 19 | #### 📋 Additional Context 20 | 21 | Add any other context or information about the feature request here. 22 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # What ❔ 2 | 3 | 4 | 5 | 6 | 7 | ## Why ❔ 8 | 9 | 10 | 11 | 12 | ## Checklist 13 | 14 | 15 | 16 | 17 | - [ ] PR title corresponds to the body of PR. 18 | - [ ] Tests for the changes have been added / updated. 19 | - [ ] Documentation comments have been added / updated. 20 | - [ ] Code has been formatted via `cargo fmt` and checked with `cargo clippy`. 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: Build and test 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | 15 | # Cargo checks 16 | cargo-check: 17 | runs-on: matterlabs-ci-runner-high-performance 18 | container: 19 | image: ghcr.io/matter-labs/zksync-llvm-runner:latest 20 | steps: 21 | - uses: actions/checkout@v4 22 | with: 23 | submodules: 'recursive' 24 | 25 | # This step is required to checkout submodules 26 | # that are disabled in .gitmodules config 27 | - name: Checkout submodules 28 | run: | 29 | git config --global --add safe.directory '*' 30 | git submodule update --force --depth=1 --recursive --checkout 31 | 32 | - name: Build LLVM 33 | uses: matter-labs/era-compiler-ci/.github/actions/build-llvm@v1 34 | with: 35 | clone-llvm: 'false' 36 | build-type: 'RelWithDebInfo' 37 | enable-assertions: true 38 | 39 | - name: Cargo checks 40 | uses: matter-labs/era-compiler-ci/.github/actions/cargo-check@v1 41 | with: 42 | github_token: ${{ secrets.GITHUB_TOKEN }} 43 | 44 | # Build and run regression tests 45 | build-and-test: 46 | runs-on: matterlabs-ci-runner-high-performance 47 | container: 48 | image: ghcr.io/matter-labs/zksync-llvm-runner:latest 49 | env: 50 | TARGET: x86_64-unknown-linux-gnu 51 | steps: 52 | - name: Checkout source 53 | uses: actions/checkout@v4 54 | with: 55 | submodules: 'recursive' 56 | 57 | # This step is required to checkout submodules 58 | # that are disabled in .gitmodules config 59 | - name: Checkout submodules 60 | run: | 61 | git config --global --add safe.directory '*' 62 | git submodule update --force --depth=1 --recursive --checkout 63 | 64 | - name: Build LLVM 65 | uses: matter-labs/era-compiler-ci/.github/actions/build-llvm@v1 66 | with: 67 | clone-llvm: 'false' 68 | build-type: 'RelWithDebInfo' 69 | enable-assertions: true 70 | 71 | - name: Run tests 72 | uses: matter-labs/era-compiler-ci/.github/actions/rust-unit-tests@v1 73 | with: 74 | target: ${{ env.TARGET }} 75 | enable-coverage: ${{ github.event_name == 'push' }} 76 | coverage-token: ${{ secrets.CODECOV_TOKEN }} 77 | 78 | # Special job that allows some of the jobs to be skipped or failed 79 | # requiring others to be successful 80 | pr-checks: 81 | runs-on: ubuntu-latest 82 | if: always() 83 | needs: 84 | - cargo-check 85 | - build-and-test 86 | steps: 87 | - name: Decide on PR checks 88 | uses: re-actors/alls-green@release/v1 89 | with: 90 | jobs: ${{ toJSON(needs) }} 91 | -------------------------------------------------------------------------------- /.github/workflows/dependencies.yaml: -------------------------------------------------------------------------------- 1 | name: Dependencies test 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | ref: 7 | description: 'Revision to use for the build.' 8 | required: true 9 | type: string 10 | dependencies_list: 11 | description: 'Space-separated list of dependencies to update. Example: "era-compiler-common era-compiler-llvm-context"' 12 | required: true 13 | default: '' 14 | type: string 15 | dependencies_branch: 16 | description: 'Branch of testing dependencies' 17 | required: true 18 | default: '' 19 | type: string 20 | 21 | jobs: 22 | dependencies-check: 23 | runs-on: matterlabs-ci-runner-high-performance 24 | container: 25 | image: ghcr.io/matter-labs/zksync-llvm-runner:latest 26 | env: 27 | TARGET: x86_64-unknown-linux-gnu 28 | steps: 29 | # Ref to checkout will be defined automatically from the event that triggered the workflow 30 | - name: Checkout source 31 | uses: actions/checkout@v4 32 | with: 33 | ref: ${{ inputs.ref }} 34 | repository: matter-labs/era-compiler-llvm-context 35 | submodules: 'recursive' 36 | 37 | # This step is required to checkout submodules 38 | # that are disabled in .gitmodules config 39 | - name: Checkout submodules 40 | run: | 41 | git config --global --add safe.directory '*' 42 | git submodule update --force --depth=1 --recursive --checkout 43 | 44 | - name: Patch dependencies 45 | shell: bash -ex {0} 46 | env: 47 | CARGO_TOML: Cargo.toml 48 | GH_ML_URL: https://github.com 49 | # `www.github.com` is a workaround for Cargo issue with `patch` section from `git` source 50 | # https://github.com/rust-lang/cargo/issues/5478 51 | GH_ML_PATCHED_URL: https://www.github.com 52 | run: | 53 | for DEP in ${{ inputs.dependencies_list }}; do 54 | CRATE=$(echo "${DEP}" | cut -d'/' -f2) 55 | echo "[patch.\"${GH_ML_URL}/${DEP}\"]" >> "${CARGO_TOML}" 56 | echo "${CRATE} = { git = '${GH_ML_PATCHED_URL}/${DEP}', branch = '${{ inputs.dependencies_branch }}' }" >> "${CARGO_TOML}" 57 | done 58 | cat "${CARGO_TOML}" 59 | 60 | - name: Build LLVM 61 | uses: matter-labs/era-compiler-ci/.github/actions/build-llvm@v1 62 | with: 63 | build-type: 'RelWithDebInfo' 64 | enable-assertions: true 65 | clone-llvm: false 66 | 67 | - name: Run tests 68 | uses: matter-labs/era-compiler-ci/.github/actions/rust-unit-tests@v1 69 | with: 70 | target: ${{ env.TARGET }} 71 | -------------------------------------------------------------------------------- /.github/workflows/dependencies_check.yaml: -------------------------------------------------------------------------------- 1 | name: Dependency check 2 | 3 | on: 4 | pull_request: 5 | 6 | concurrency: 7 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 8 | cancel-in-progress: true 9 | 10 | jobs: 11 | 12 | define-dependencies: 13 | runs-on: ubuntu-latest 14 | env: 15 | GH_TOKEN: ${{ secrets.ZKSYNC_DISPATCH_TOKEN }} 16 | outputs: 17 | dependencies_list: ${{ steps.define-dependencies.outputs.dependencies_list }} 18 | zksolc_branch: ${{ steps.target-branches.outputs.zksolc_branch }} 19 | zkvyper_branch: ${{ steps.target-branches.outputs.zkvyper_branch }} 20 | steps: 21 | - name: Define dependencies 22 | id: define-dependencies 23 | env: 24 | POSSIBLE_DEPS: "matter-labs/era-compiler-common matter-labs/era-compiler-llvm-context matter-labs-forks/inkwell matter-labs-forks/llvm-sys.rs" 25 | shell: bash -x {0} 26 | run: | 27 | DEPENDENCIES_LIST="" 28 | for DEP in ${POSSIBLE_DEPS}; do 29 | # Check if the branch of this repository dependency exists in the other repositories 30 | # and if so, add it to the list of the dependencies that should be updated on the target repository 31 | if [ $(gh api "/repos/${DEP}/branches/${{ github.head_ref }}" > /dev/null 2>&1; echo $?) -eq 0 ]; then 32 | DEPENDENCIES_LIST="${DEPENDENCIES_LIST} ${DEP}" 33 | fi 34 | done 35 | echo "dependencies_list=$(echo ${DEPENDENCIES_LIST} | sed 's/^[[:space:]]*//g')" | tee -a "${GITHUB_OUTPUT}" 36 | 37 | - name: Define target branch 38 | id: target-branches 39 | env: 40 | ERA_SOLIDITY_REPO: matter-labs/era-compiler-solidity 41 | ERA_VYPER_REPO: matter-labs/era-compiler-vyper 42 | shell: bash -x {0} 43 | run: | 44 | if [ $(gh api "/repos/${ERA_SOLIDITY_REPO}/branches/${{ github.head_ref }}" > /dev/null 2>&1; echo $?) -eq 0 ]; then 45 | echo "zksolc_branch=${{ github.head_ref }}" | tee -a "${GITHUB_OUTPUT}" 46 | fi 47 | if [ $(gh api "/repos/${ERA_VYPER_REPO}/branches/${{ github.head_ref }}" > /dev/null 2>&1; echo $?) -eq 0 ]; then 48 | echo "zkvyper_branch=${{ github.head_ref }}" | tee -a "${GITHUB_OUTPUT}" 49 | fi 50 | 51 | check-zksolc: 52 | needs: define-dependencies 53 | uses: matter-labs/era-compiler-solidity/.github/workflows/dependencies.yaml@main 54 | secrets: inherit 55 | with: 56 | ref: ${{ needs.define-dependencies.outputs.zksolc_branch || 'main' }} 57 | dependencies_list: ${{ needs.define-dependencies.outputs.dependencies_list }} 58 | dependencies_branch: ${{ github.head_ref }} 59 | 60 | check-zkvyper: 61 | needs: define-dependencies 62 | uses: matter-labs/era-compiler-vyper/.github/workflows/dependencies.yaml@main 63 | secrets: inherit 64 | with: 65 | ref: ${{ needs.define-dependencies.outputs.zkvyper_branch || 'main' }} 66 | dependencies_list: ${{ needs.define-dependencies.outputs.dependencies_list }} 67 | dependencies_branch: ${{ github.head_ref }} 68 | -------------------------------------------------------------------------------- /.github/workflows/secrets-scanner.yaml: -------------------------------------------------------------------------------- 1 | name: Leaked Secrets Scan 2 | on: 3 | pull_request: 4 | merge_group: 5 | jobs: 6 | TruffleHog: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout code 10 | uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 11 | with: 12 | fetch-depth: 0 13 | - name: TruffleHog OSS 14 | uses: trufflesecurity/trufflehog@7e78ca385fb82c19568c7a4b341c97d57d9aa5e1 # v3.82.2 15 | with: 16 | path: ./ 17 | base: ${{ github.event.repository.default_branch }} 18 | head: HEAD 19 | extra_args: --debug --only-verified 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | # IDE project data 13 | /.idea/ 14 | /.vscode/ 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "llvm"] 2 | path = llvm 3 | url = https://github.com/matter-labs/era-compiler-llvm 4 | branch = main 5 | update = none 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | Hello! Thanks for your interest in joining the mission to accelerate the mass adoption of crypto for personal 4 | sovereignty! We welcome contributions from anyone on the internet, and are grateful for even the smallest of fixes! 5 | 6 | ## Ways to contribute 7 | 8 | There are many ways to contribute: 9 | 10 | 1. Open issues: if you find a bug, have something you believe needs to be fixed, or have an idea for a feature, please 11 | open an issue. 12 | 2. Add color to existing issues: provide screenshots, code snippets, and whatever you think would be helpful to resolve 13 | issues. 14 | 3. Resolve issues: either by showing an issue isn't a problem and the current state is ok as is or by fixing the problem 15 | and opening a PR. 16 | 17 | ## Fixing issues 18 | 19 | To contribute code fixing issues, please fork the repo, fix an issue, commit, add documentation as per the PR template, 20 | and the repo's maintainers will review the PR. 21 | [here](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) 22 | for guidance how to work with PRs created from a fork. 23 | 24 | ## Licenses 25 | 26 | If you contribute to this project, your contributions will be made to the project under both Apache 2.0 and the MIT licenses. 27 | 28 | ## Code of Conduct 29 | 30 | Be polite and respectful. 31 | 32 | ### Thank you 33 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "era-compiler-llvm-context" 3 | version = "2.0.0" 4 | authors = [ 5 | "Oleksandr Zarudnyi ", 6 | ] 7 | license = "MIT OR Apache-2.0" 8 | edition = "2021" 9 | description = "Shared front-end code of LLVM-based compilers" 10 | 11 | [lib] 12 | doctest = false 13 | 14 | [dependencies] 15 | anyhow = "1.0" 16 | thiserror = "2.0" 17 | semver = "1.0" 18 | serde = { version = "1.0", "features" = [ "derive" ] } 19 | num = "0.4" 20 | itertools = "0.14" 21 | 22 | zkevm_opcode_defs = "=0.150.6" 23 | 24 | era-compiler-common = { git = "https://github.com/matter-labs/era-compiler-common", branch = "main" } 25 | 26 | [dependencies.inkwell] 27 | git = "https://github.com/matter-labs-forks/inkwell" 28 | branch = "llvm-19" 29 | default-features = false 30 | features = ["llvm19-1", "serde", "no-libffi-linking", "target-eravm", "target-evm"] 31 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Matter Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Shared Front-End Code of LLVM-based Compilers 2 | 3 | This repository contains shared front-end code of LLVM-based compilers developed by Matter Labs. 4 | 5 | ## License 6 | 7 | This library is distributed under the terms of either 8 | 9 | - Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or ) 10 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 11 | 12 | at your option. 13 | 14 | ## Contact Us 15 | 16 | Email us at [solx@matterlabs.dev](mailto:solx@matterlabs.dev). 17 | -------------------------------------------------------------------------------- /deny.toml: -------------------------------------------------------------------------------- 1 | [advisories] 2 | yanked = "warn" 3 | ignore = [ 4 | #"RUSTSEC-0000-0000", 5 | ] 6 | 7 | [licenses] 8 | allow = [ 9 | #"Apache-2.0 WITH LLVM-exception", 10 | "MIT", 11 | "Apache-2.0", 12 | "ISC", 13 | "Unlicense", 14 | "MPL-2.0", 15 | "Unicode-DFS-2016", 16 | "Unicode-3.0", 17 | "CC0-1.0", 18 | "BSD-2-Clause", 19 | "BSD-3-Clause", 20 | "Zlib", 21 | ] 22 | confidence-threshold = 0.8 23 | exceptions = [ 24 | # Each entry is the crate and version constraint, and its specific allow 25 | # list 26 | #{ allow = ["Zlib"], name = "adler32", version = "*" }, 27 | ] 28 | 29 | unused-allowed-license = "allow" 30 | 31 | [licenses.private] 32 | ignore = false 33 | registries = [ 34 | #"https://sekretz.com/registry 35 | ] 36 | 37 | [bans] 38 | multiple-versions = "warn" 39 | wildcards = "allow" 40 | highlight = "all" 41 | workspace-default-features = "allow" 42 | external-default-features = "allow" 43 | allow = [ 44 | #{ name = "ansi_term", version = "=0.11.0" }, 45 | ] 46 | # List of crates to deny 47 | deny = [ 48 | # Each entry the name of a crate and a version range. If version is 49 | # not specified, all versions will be matched. 50 | #{ name = "ansi_term", version = "=0.11.0" }, 51 | ] 52 | 53 | skip = [ 54 | #{ name = "ansi_term", version = "=0.11.0" }, 55 | ] 56 | skip-tree = [ 57 | #{ name = "ansi_term", version = "=0.11.0", depth = 20 }, 58 | ] 59 | 60 | [sources] 61 | unknown-registry = "deny" 62 | unknown-git = "allow" 63 | allow-registry = ["https://github.com/rust-lang/crates.io-index"] 64 | allow-git = [] 65 | 66 | [sources.allow-org] 67 | #github = ["matter-labs"] 68 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | profile = "default" 3 | channel = "1.86.0" 4 | -------------------------------------------------------------------------------- /src/const.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM context constants. 3 | //! 4 | 5 | /// The LLVM framework version. 6 | pub const LLVM_VERSION: semver::Version = semver::Version::new(17, 0, 4); 7 | 8 | /// The entry function name. 9 | pub const ENTRY_FUNCTION_NAME: &str = "__entry"; 10 | 11 | /// The deployed Yul object identifier suffix. 12 | pub static YUL_OBJECT_DEPLOYED_SUFFIX: &str = "_deployed"; 13 | 14 | /// Library deploy address Yul identifier. 15 | pub static LIBRARY_DEPLOY_ADDRESS_TAG: &str = "library_deploy_address"; 16 | -------------------------------------------------------------------------------- /src/context/attribute/memory.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM memory attribute. 3 | //! 4 | 5 | /// 6 | /// The LLVM memory attribute. 7 | /// 8 | #[derive( 9 | Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, 10 | )] 11 | pub enum Memory { 12 | /// The corresponding value. 13 | None = 0, 14 | /// The corresponding value. 15 | Read = 1, 16 | /// The corresponding value. 17 | Write = 2, 18 | /// The corresponding value. 19 | ArgMemOnly = 3, 20 | /// The corresponding value. 21 | ArgMemReadOnly = 4, 22 | /// The corresponding value. 23 | ArgMemWriteOnly = 5, 24 | /// The corresponding value. 25 | InaccessibleMemOnly = 6, 26 | /// The corresponding value. 27 | InaccessibleMemReadOnly = 7, 28 | /// The corresponding value. 29 | InaccessibleMemWriteOnly = 8, 30 | /// The corresponding value. 31 | InaccessibleOrArgMemOnly = 9, 32 | /// The corresponding value. 33 | InaccessibleOrArgMemReadOnly = 10, 34 | /// The corresponding value. 35 | InaccessibleOrArgMemWriteOnly = 11, 36 | } 37 | -------------------------------------------------------------------------------- /src/context/attribute/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM attribute. 3 | //! 4 | 5 | pub mod memory; 6 | 7 | /// 8 | /// The LLVM attribute. 9 | /// 10 | /// In order to check the real order in a new major version of LLVM, find the `Attributes.inc` file 11 | /// inside of the LLVM build directory. This order is actually generated during the building. 12 | /// 13 | #[derive( 14 | Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize, 15 | )] 16 | pub enum Attribute { 17 | /// Unused. 18 | Unused = 0, 19 | /// The eponymous LLVM attribute. 20 | AllocAlign, 21 | /// The eponymous LLVM attribute. 22 | AllocatedPointer, 23 | /// The eponymous LLVM attribute. 24 | AlwaysInline, 25 | /// The eponymous LLVM attribute. 26 | Builtin, 27 | /// The eponymous LLVM attribute. 28 | Cold = 5, 29 | /// The eponymous LLVM attribute. 30 | Convergent, 31 | /// The eponymous LLVM attribute. 32 | CoroDestroyOnlyWhenComplete, 33 | /// The eponymous LLVM attribute. 34 | DeadOnUnwind, 35 | /// The eponymous LLVM attribute. 36 | DisableSanitizerInstrumentation, 37 | /// The eponymous LLVM attribute. 38 | FnRetThunkExtern = 10, 39 | /// The eponymous LLVM attribute. 40 | Hot, 41 | /// The eponymous LLVM attribute. 42 | HybridPatchable, 43 | /// The eponymous LLVM attribute. 44 | ImmArg, 45 | /// The eponymous LLVM attribute. 46 | InReg, 47 | /// The eponymous LLVM attribute. 48 | InlineHint = 15, 49 | /// The eponymous LLVM attribute. 50 | JumpTable, 51 | /// The eponymous LLVM attribute. 52 | MinSize, 53 | /// The eponymous LLVM attribute. 54 | MustProgress, 55 | /// The eponymous LLVM attribute. 56 | Naked, 57 | /// The eponymous LLVM attribute. 58 | Nest = 20, 59 | /// The eponymous LLVM attribute. 60 | NoAlias, 61 | /// The eponymous LLVM attribute. 62 | NoBuiltin, 63 | /// The eponymous LLVM attribute. 64 | NoCallback, 65 | /// The eponymous LLVM attribute. 66 | NoCapture, 67 | /// The eponymous LLVM attribute. 68 | NoCfCheck = 25, 69 | /// The eponymous LLVM attribute. 70 | NoDuplicate, 71 | /// The eponymous LLVM attribute. 72 | NoFree, 73 | /// The eponymous LLVM attribute. 74 | NoImplicitFloat, 75 | /// The eponymous LLVM attribute. 76 | NoInline, 77 | /// The eponymous LLVM attribute. 78 | NoMerge = 30, 79 | /// The eponymous LLVM attribute. 80 | NoProfile, 81 | /// The eponymous LLVM attribute. 82 | NoRecurse, 83 | /// The eponymous LLVM attribute. 84 | NoRedZone, 85 | /// The eponymous LLVM attribute. 86 | NoReturn, 87 | /// The eponymous LLVM attribute. 88 | NoSanitizeBounds = 35, 89 | /// The eponymous LLVM attribute. 90 | NoSanitizeCoverage, 91 | /// The eponymous LLVM attribute. 92 | NoSync, 93 | /// The eponymous LLVM attribute. 94 | NoUndef, 95 | /// The eponymous LLVM attribute. 96 | NoUnwind, 97 | /// The eponymous LLVM attribute. 98 | NonLazyBind = 40, 99 | /// The eponymous LLVM attribute. 100 | NonNull, 101 | /// The eponymous LLVM attribute. 102 | NullPointerIsValid, 103 | /// The eponymous LLVM attribute. 104 | OptForFuzzing, 105 | /// The eponymous LLVM attribute. 106 | OptimizeForDebugging, 107 | /// The eponymous LLVM attribute. 108 | OptimizeForSize = 45, 109 | /// The eponymous LLVM attribute. 110 | OptimizeNone, 111 | /// The eponymous LLVM attribute. 112 | PresplitCoroutine, 113 | /// The eponymous LLVM attribute. 114 | ReadNone, 115 | /// The eponymous LLVM attribute. 116 | ReadOnly, 117 | /// The eponymous LLVM attribute. 118 | Returned = 50, 119 | /// The eponymous LLVM attribute. 120 | ReturnsTwice, 121 | /// The eponymous LLVM attribute. 122 | SExt, 123 | /// The eponymous LLVM attribute. 124 | SafeStack, 125 | /// The eponymous LLVM attribute. 126 | SanitizeAddress, 127 | /// The eponymous LLVM attribute. 128 | SanitizeHWAddress = 55, 129 | /// The eponymous LLVM attribute. 130 | SanitizeMemTag, 131 | /// The eponymous LLVM attribute. 132 | SanitizeMemory, 133 | /// The eponymous LLVM attribute. 134 | SanitizeNumericalStability, 135 | /// The eponymous LLVM attribute. 136 | SanitizeThread, 137 | /// The eponymous LLVM attribute. 138 | ShadowCallStack = 60, 139 | /// The eponymous LLVM attribute. 140 | SkipProfile, 141 | /// The eponymous LLVM attribute. 142 | Speculatable, 143 | /// The eponymous LLVM attribute. 144 | SpeculativeLoadHardening, 145 | /// The eponymous LLVM attribute. 146 | StackProtect, 147 | /// The eponymous LLVM attribute. 148 | StackProtectReq = 65, 149 | /// The eponymous LLVM attribute. 150 | StackProtectStrong, 151 | /// The eponymous LLVM attribute. 152 | StrictFP, 153 | /// The eponymous LLVM attribute. 154 | SwiftAsync, 155 | /// The eponymous LLVM attribute. 156 | SwiftError, 157 | /// The eponymous LLVM attribute. 158 | SwiftSelf = 70, 159 | /// The eponymous LLVM attribute. 160 | WillReturn, 161 | /// The eponymous LLVM attribute. 162 | Writable, 163 | /// The eponymous LLVM attribute. 164 | WriteOnly, 165 | /// The eponymous LLVM attribute. 166 | ZExt, 167 | /// The eponymous LLVM attribute. 168 | ByRef = 75, 169 | /// The eponymous LLVM attribute. 170 | ByVal, 171 | /// The eponymous LLVM attribute. 172 | ElementType, 173 | /// The eponymous LLVM attribute. 174 | InAlloca, 175 | /// The eponymous LLVM attribute. 176 | Preallocated, 177 | /// The eponymous LLVM attribute. 178 | StructRet = 80, 179 | /// The eponymous LLVM attribute. 180 | Alignment, 181 | /// The eponymous LLVM attribute. 182 | AllocKind, 183 | /// The eponymous LLVM attribute. 184 | AllocSize, 185 | /// The eponymous LLVM attribute. 186 | Dereferenceable, 187 | /// The eponymous LLVM attribute. 188 | DereferenceableOrNull = 85, 189 | /// The eponymous LLVM attribute. 190 | Memory, 191 | /// The eponymous LLVM attribute. 192 | NoFPClass, 193 | /// The eponymous LLVM attribute. 194 | StackAlignment, 195 | /// The eponymous LLVM attribute. 196 | UWTable, 197 | /// The eponymous LLVM attribute. 198 | VScaleRange = 90, 199 | /// The eponymous LLVM attribute. 200 | Range, 201 | /// The eponymous LLVM attribute. 202 | Initializes, 203 | } 204 | 205 | impl TryFrom<&str> for Attribute { 206 | type Error = String; 207 | 208 | fn try_from(value: &str) -> Result { 209 | match value { 210 | "AlwaysInline" => Ok(Attribute::AlwaysInline), 211 | "Cold" => Ok(Attribute::Cold), 212 | "Hot" => Ok(Attribute::Hot), 213 | "MinSize" => Ok(Attribute::MinSize), 214 | "OptimizeForSize" => Ok(Attribute::OptimizeForSize), 215 | "NoInline" => Ok(Attribute::NoInline), 216 | "WillReturn" => Ok(Attribute::WillReturn), 217 | "NoReturn" => Ok(Attribute::NoReturn), 218 | "MustProgress" => Ok(Attribute::MustProgress), 219 | _ => Err(value.to_owned()), 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/context/function/block/evmla_data.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM function block EVM legacy assembly data. 3 | //! 4 | 5 | /// 6 | /// The LLVM function block EVM legacy assembly data. 7 | /// 8 | /// Describes some data that is only relevant to the EVM legacy assembly. 9 | /// 10 | #[derive(Debug, Clone)] 11 | pub struct EVMLAData { 12 | /// The initial hashes of the allowed stack states. 13 | pub stack_hashes: Vec<[u8; era_compiler_common::BYTE_LENGTH_FIELD]>, 14 | } 15 | 16 | impl EVMLAData { 17 | /// 18 | /// A shortcut constructor. 19 | /// 20 | pub fn new(stack_hashes: Vec<[u8; era_compiler_common::BYTE_LENGTH_FIELD]>) -> Self { 21 | Self { stack_hashes } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/context/function/block/key.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM IR generator function block key. 3 | //! 4 | 5 | /// 6 | /// The LLVM IR generator function block key. 7 | /// 8 | /// Is only relevant to the EVM legacy assembly. 9 | /// 10 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 11 | pub struct Key { 12 | /// The block code type. 13 | pub code_segment: era_compiler_common::CodeSegment, 14 | /// The block tag. 15 | pub tag: num::BigUint, 16 | } 17 | 18 | impl Key { 19 | /// 20 | /// A shortcut constructor. 21 | /// 22 | pub fn new(code_segment: era_compiler_common::CodeSegment, tag: num::BigUint) -> Self { 23 | Self { code_segment, tag } 24 | } 25 | } 26 | 27 | impl std::fmt::Display for Key { 28 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 29 | write!( 30 | f, 31 | "{}_{}", 32 | match self.code_segment { 33 | era_compiler_common::CodeSegment::Deploy => "dt", 34 | era_compiler_common::CodeSegment::Runtime => "rt", 35 | }, 36 | self.tag 37 | ) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/context/function/block/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM IR generator function block. 3 | //! 4 | 5 | pub mod evmla_data; 6 | pub mod key; 7 | 8 | use self::evmla_data::EVMLAData; 9 | 10 | /// 11 | /// The LLVM IR generator function block. 12 | /// 13 | #[derive(Debug, Clone)] 14 | pub struct Block<'ctx> { 15 | /// The inner block. 16 | inner: inkwell::basic_block::BasicBlock<'ctx>, 17 | /// The EVM legacy assembly compiler data. 18 | evmla_data: Option, 19 | } 20 | 21 | impl<'ctx> Block<'ctx> { 22 | /// 23 | /// A shortcut constructor. 24 | /// 25 | pub fn new(inner: inkwell::basic_block::BasicBlock<'ctx>) -> Self { 26 | Self { 27 | inner, 28 | evmla_data: None, 29 | } 30 | } 31 | 32 | /// 33 | /// Sets the EVM legacy assembly data. 34 | /// 35 | pub fn set_evmla_data(&mut self, data: EVMLAData) { 36 | self.evmla_data = Some(data); 37 | } 38 | 39 | /// 40 | /// The LLVM object reference. 41 | /// 42 | pub fn inner(&self) -> inkwell::basic_block::BasicBlock<'ctx> { 43 | self.inner 44 | } 45 | 46 | /// 47 | /// Returns the EVM data reference. 48 | /// 49 | /// # Panics 50 | /// If the EVM data has not been initialized. 51 | /// 52 | pub fn evm(&self) -> &EVMLAData { 53 | self.evmla_data 54 | .as_ref() 55 | .expect("The EVM data must have been initialized") 56 | } 57 | 58 | /// 59 | /// Returns the EVM data mutable reference. 60 | /// 61 | /// # Panics 62 | /// If the EVM data has not been initialized. 63 | /// 64 | pub fn evm_mut(&mut self) -> &mut EVMLAData { 65 | self.evmla_data 66 | .as_mut() 67 | .expect("The EVM data must have been initialized") 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/context/function/declaration.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM function declaration. 3 | //! 4 | 5 | /// 6 | /// The LLVM function declaration. 7 | /// 8 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 9 | pub struct Declaration<'ctx> { 10 | /// The function type. 11 | pub r#type: inkwell::types::FunctionType<'ctx>, 12 | /// The function value. 13 | pub value: inkwell::values::FunctionValue<'ctx>, 14 | } 15 | 16 | impl<'ctx> Declaration<'ctx> { 17 | /// 18 | /// A shortcut constructor. 19 | /// 20 | pub fn new( 21 | r#type: inkwell::types::FunctionType<'ctx>, 22 | value: inkwell::values::FunctionValue<'ctx>, 23 | ) -> Self { 24 | Self { r#type, value } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/context/function/evmla_data.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM function EVM legacy assembly data. 3 | //! 4 | 5 | use std::collections::BTreeMap; 6 | 7 | use crate::context::function::block::key::Key as BlockKey; 8 | use crate::context::function::block::Block; 9 | 10 | /// 11 | /// The LLVM function EVM legacy assembly data. 12 | /// 13 | /// Describes some data that is only relevant to the EVM legacy assembly. 14 | /// 15 | #[derive(Debug)] 16 | pub struct EVMLAData<'ctx> { 17 | /// The ordinary blocks with numeric tags. 18 | /// Is only used by the Solidity EVM compiler. 19 | pub blocks: BTreeMap>>, 20 | /// The function stack size. 21 | pub stack_size: usize, 22 | } 23 | 24 | impl<'ctx> EVMLAData<'ctx> { 25 | /// 26 | /// A shortcut constructor. 27 | /// 28 | pub fn new(stack_size: usize) -> Self { 29 | Self { 30 | blocks: BTreeMap::new(), 31 | stack_size, 32 | } 33 | } 34 | 35 | /// 36 | /// Inserts a function block. 37 | /// 38 | pub fn insert_block(&mut self, key: BlockKey, block: Block<'ctx>) { 39 | if let Some(blocks) = self.blocks.get_mut(&key) { 40 | blocks.push(block); 41 | } else { 42 | self.blocks.insert(key, vec![block]); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/context/function/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The common LLVM function entities. 3 | //! 4 | 5 | pub mod block; 6 | pub mod declaration; 7 | pub mod evmla_data; 8 | pub mod r#return; 9 | -------------------------------------------------------------------------------- /src/context/function/return.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM IR generator function return entity. 3 | //! 4 | 5 | use crate::context::pointer::Pointer; 6 | use crate::context::traits::address_space::IAddressSpace; 7 | 8 | /// 9 | /// The LLVM IR generator function return entity. 10 | /// 11 | #[derive(Debug, Clone, Copy)] 12 | pub enum Return<'ctx, AS> 13 | where 14 | AS: IAddressSpace 15 | + Clone 16 | + Copy 17 | + PartialEq 18 | + Eq 19 | + Into 20 | + std::fmt::Debug, 21 | { 22 | /// The function does not return a value. 23 | None, 24 | /// The function returns a primitive value. 25 | Primitive { 26 | /// The primitive value pointer allocated at the function entry. 27 | pointer: Pointer<'ctx, AS>, 28 | }, 29 | /// The function returns a compound value. 30 | /// In this case, the return pointer is allocated on the stack by the callee. 31 | Compound { 32 | /// The structure pointer allocated at the function entry. 33 | pointer: Pointer<'ctx, AS>, 34 | /// The function return type size. 35 | size: usize, 36 | }, 37 | } 38 | 39 | impl<'ctx, AS> Return<'ctx, AS> 40 | where 41 | AS: IAddressSpace 42 | + Clone 43 | + Copy 44 | + PartialEq 45 | + Eq 46 | + Into 47 | + std::fmt::Debug, 48 | { 49 | /// 50 | /// A shortcut constructor. 51 | /// 52 | pub fn none() -> Self { 53 | Self::None 54 | } 55 | 56 | /// 57 | /// A shortcut constructor. 58 | /// 59 | pub fn primitive(pointer: Pointer<'ctx, AS>) -> Self { 60 | Self::Primitive { pointer } 61 | } 62 | 63 | /// 64 | /// A shortcut constructor. 65 | /// 66 | pub fn compound(pointer: Pointer<'ctx, AS>, size: usize) -> Self { 67 | Self::Compound { pointer, size } 68 | } 69 | 70 | /// 71 | /// Returns the pointer to the function return value. 72 | /// 73 | pub fn return_pointer(&self) -> Option> { 74 | match self { 75 | Return::None => None, 76 | Return::Primitive { pointer } => Some(pointer.to_owned()), 77 | Return::Compound { pointer, .. } => Some(pointer.to_owned()), 78 | } 79 | } 80 | 81 | /// 82 | /// Returns the return data size in bytes, based on the default stack alignment. 83 | /// 84 | pub fn return_data_size(&self) -> usize { 85 | era_compiler_common::BYTE_LENGTH_FIELD 86 | * match self { 87 | Self::None => 0, 88 | Self::Primitive { .. } => 1, 89 | Self::Compound { size, .. } => *size, 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/context/loop.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM IR generator loop. 3 | //! 4 | 5 | /// 6 | /// The LLVM IR generator loop. 7 | /// 8 | #[derive(Debug, Clone)] 9 | pub struct Loop<'ctx> { 10 | /// The loop current block. 11 | pub body_block: inkwell::basic_block::BasicBlock<'ctx>, 12 | /// The increment block before the body. 13 | pub continue_block: inkwell::basic_block::BasicBlock<'ctx>, 14 | /// The join block after the body. 15 | pub join_block: inkwell::basic_block::BasicBlock<'ctx>, 16 | } 17 | 18 | impl<'ctx> Loop<'ctx> { 19 | /// 20 | /// A shortcut constructor. 21 | /// 22 | pub fn new( 23 | body_block: inkwell::basic_block::BasicBlock<'ctx>, 24 | continue_block: inkwell::basic_block::BasicBlock<'ctx>, 25 | join_block: inkwell::basic_block::BasicBlock<'ctx>, 26 | ) -> Self { 27 | Self { 28 | body_block, 29 | continue_block, 30 | join_block, 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/context/pointer.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM pointer. 3 | //! 4 | 5 | use inkwell::types::BasicType; 6 | use inkwell::values::BasicValue; 7 | 8 | use crate::context::traits::address_space::IAddressSpace; 9 | use crate::context::IContext; 10 | use crate::eravm::context::global::Global; 11 | 12 | /// 13 | /// The LLVM pointer. 14 | /// 15 | #[derive(Debug, Clone, Copy)] 16 | pub struct Pointer<'ctx, AS> 17 | where 18 | AS: IAddressSpace 19 | + Clone 20 | + Copy 21 | + PartialEq 22 | + Eq 23 | + Into 24 | + std::fmt::Debug, 25 | { 26 | /// The pointee type. 27 | pub r#type: inkwell::types::BasicTypeEnum<'ctx>, 28 | /// The address space. 29 | pub address_space: AS, 30 | /// The pointer value. 31 | pub value: inkwell::values::PointerValue<'ctx>, 32 | } 33 | 34 | impl<'ctx, AS> Pointer<'ctx, AS> 35 | where 36 | AS: IAddressSpace 37 | + Clone 38 | + Copy 39 | + PartialEq 40 | + Eq 41 | + Into 42 | + std::fmt::Debug, 43 | { 44 | /// 45 | /// A shortcut constructor. 46 | /// 47 | pub fn new(r#type: T, address_space: AS, value: inkwell::values::PointerValue<'ctx>) -> Self 48 | where 49 | T: BasicType<'ctx>, 50 | { 51 | Self { 52 | r#type: r#type.as_basic_type_enum(), 53 | address_space, 54 | value, 55 | } 56 | } 57 | 58 | /// 59 | /// Wraps a 256-bit primitive type pointer. 60 | /// 61 | pub fn new_stack_field(context: &C, value: inkwell::values::PointerValue<'ctx>) -> Self 62 | where 63 | C: IContext<'ctx>, 64 | { 65 | Self { 66 | r#type: context.field_type().as_basic_type_enum(), 67 | address_space: AS::stack(), 68 | value, 69 | } 70 | } 71 | 72 | /// 73 | /// Creates a new pointer with the specified `offset`. 74 | /// 75 | pub fn new_with_offset( 76 | context: &C, 77 | address_space: AS, 78 | r#type: T, 79 | offset: inkwell::values::IntValue<'ctx>, 80 | name: &str, 81 | ) -> anyhow::Result 82 | where 83 | C: IContext<'ctx>, 84 | T: BasicType<'ctx>, 85 | { 86 | assert_ne!( 87 | address_space, 88 | AS::stack(), 89 | "Stack pointers cannot be addressed" 90 | ); 91 | 92 | let value = context.builder().build_int_to_ptr( 93 | offset, 94 | context.ptr_type(address_space.into()), 95 | name, 96 | )?; 97 | Ok(Self::new(r#type, address_space, value)) 98 | } 99 | 100 | /// 101 | /// Casts the pointer into another type. 102 | /// 103 | pub fn cast(self, r#type: T) -> Self 104 | where 105 | T: BasicType<'ctx>, 106 | { 107 | Self { 108 | r#type: r#type.as_basic_type_enum(), 109 | address_space: self.address_space, 110 | value: self.value, 111 | } 112 | } 113 | 114 | /// 115 | /// Converts the pointer to a value enum. 116 | /// 117 | pub fn as_basic_value_enum(self) -> inkwell::values::BasicValueEnum<'ctx> { 118 | self.value.as_basic_value_enum() 119 | } 120 | } 121 | 122 | impl<'ctx, AS> From> for Pointer<'ctx, AS> 123 | where 124 | AS: IAddressSpace 125 | + Clone 126 | + Copy 127 | + PartialEq 128 | + Eq 129 | + Into 130 | + std::fmt::Debug, 131 | { 132 | fn from(global: Global<'ctx>) -> Self { 133 | Self { 134 | r#type: global.r#type, 135 | address_space: AS::stack(), 136 | value: global.value.as_pointer_value(), 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/context/traits/address_space.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM IR address space trait. 3 | //! 4 | 5 | /// 6 | /// The LLVM IR address space trait. 7 | /// 8 | pub trait IAddressSpace { 9 | /// 10 | /// Returns the stack address space. 11 | /// 12 | fn stack() -> Self; 13 | } 14 | -------------------------------------------------------------------------------- /src/context/traits/evmla_data.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM IR EVMLA data trait. 3 | //! 4 | 5 | use crate::context::value::Value; 6 | 7 | /// 8 | /// The LLVM IR EVMLA data trait. 9 | /// 10 | pub trait IEVMLAData<'ctx> { 11 | /// 12 | /// Returns the element from the specified stack position. 13 | /// 14 | /// # Panics 15 | /// If `position` is out of bounds. 16 | /// 17 | fn get_element(&self, position: usize) -> &Value<'ctx>; 18 | 19 | /// 20 | /// Sets the element at the specified stack position. 21 | /// 22 | /// # Panics 23 | /// If `position` is out of bounds. 24 | /// 25 | fn set_element(&mut self, position: usize, value: Value<'ctx>); 26 | 27 | /// 28 | /// Sets the compile-time string representation to the element at the specified stack position. 29 | /// 30 | /// # Panics 31 | /// If `position` is out of bounds. 32 | /// 33 | fn set_original(&mut self, position: usize, original: String); 34 | } 35 | -------------------------------------------------------------------------------- /src/context/traits/evmla_function.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM IR EVMLA function trait. 3 | //! 4 | 5 | use crate::context::function::block::key::Key as BlockKey; 6 | use crate::context::function::block::Block; 7 | 8 | /// 9 | /// The LLVM IR EVMLA function trait. 10 | /// 11 | pub trait IEVMLAFunction<'ctx> { 12 | /// 13 | /// Returns the block with the specified tag and initial stack pattern. 14 | /// 15 | /// If there is only one block, it is returned unconditionally. 16 | /// 17 | fn find_block( 18 | &self, 19 | key: &BlockKey, 20 | stack_hash: &[u8; era_compiler_common::BYTE_LENGTH_FIELD], 21 | ) -> anyhow::Result>; 22 | } 23 | -------------------------------------------------------------------------------- /src/context/traits/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM context traits. 3 | //! 4 | 5 | pub mod address_space; 6 | pub mod evmla_data; 7 | pub mod evmla_function; 8 | pub mod solidity_data; 9 | pub mod yul_data; 10 | -------------------------------------------------------------------------------- /src/context/traits/solidity_data.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM IR Solidity data trait. 3 | //! 4 | 5 | use std::collections::BTreeSet; 6 | 7 | /// 8 | /// The LLVM IR Solidity data trait. 9 | /// 10 | pub trait ISolidityData { 11 | /// 12 | /// Returns all runtime code offsets for the specified `id`. 13 | /// 14 | fn offsets(&self, id: &str) -> Option<&BTreeSet>; 15 | } 16 | -------------------------------------------------------------------------------- /src/context/traits/yul_data.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM IR Yul data trait. 3 | //! 4 | 5 | /// 6 | /// The LLVM IR Yul data trait. 7 | /// 8 | pub trait IYulData { 9 | /// 10 | /// Resolves the full contract path by the Yul object identifier. 11 | /// 12 | fn resolve_path(&self, identifier: &str) -> Option<&str>; 13 | } 14 | -------------------------------------------------------------------------------- /src/context/value.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM compile-time value with metadata. 3 | //! 4 | 5 | /// 6 | /// The LLVM compile-time value with metadata. 7 | /// 8 | #[derive(Debug, Clone)] 9 | pub struct Value<'ctx> { 10 | /// The actual LLVM operand. 11 | pub value: inkwell::values::BasicValueEnum<'ctx>, 12 | /// The original AST value. Used mostly for string literals. 13 | pub original: Option, 14 | /// The preserved constant value, if available. 15 | pub constant: Option, 16 | } 17 | 18 | impl<'ctx> Value<'ctx> { 19 | /// The calldata offset argument index. 20 | pub const ARGUMENT_INDEX_CALLDATA_OFFSET: usize = 0; 21 | 22 | /// The calldata length argument index. 23 | pub const ARGUMENT_INDEX_CALLDATA_LENGTH: usize = 1; 24 | 25 | /// 26 | /// A shortcut constructor. 27 | /// 28 | pub fn new(value: inkwell::values::BasicValueEnum<'ctx>) -> Self { 29 | Self { 30 | value, 31 | original: None, 32 | constant: None, 33 | } 34 | } 35 | 36 | /// 37 | /// A shortcut constructor. 38 | /// 39 | pub fn new_with_original( 40 | value: inkwell::values::BasicValueEnum<'ctx>, 41 | original: String, 42 | ) -> Self { 43 | Self { 44 | value, 45 | original: Some(original), 46 | constant: None, 47 | } 48 | } 49 | 50 | /// 51 | /// A shortcut constructor. 52 | /// 53 | pub fn new_with_constant( 54 | value: inkwell::values::BasicValueEnum<'ctx>, 55 | constant: num::BigUint, 56 | ) -> Self { 57 | Self { 58 | value, 59 | original: None, 60 | constant: Some(constant), 61 | } 62 | } 63 | 64 | /// 65 | /// Returns the inner LLVM value. 66 | /// 67 | pub fn to_llvm(&self) -> inkwell::values::BasicValueEnum<'ctx> { 68 | self.value 69 | } 70 | } 71 | 72 | impl<'ctx> From> for Value<'ctx> { 73 | fn from(value: inkwell::values::BasicValueEnum<'ctx>) -> Self { 74 | Self::new(value) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/debug_config/ir_type.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The debug IR type. 3 | //! 4 | 5 | /// 6 | /// The debug IR type. 7 | /// 8 | #[allow(non_camel_case_types)] 9 | #[allow(clippy::upper_case_acronyms)] 10 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 11 | pub enum IRType { 12 | /// Whether to dump the Yul code. 13 | Yul, 14 | /// Whether to dump the EVM legacy assembly code. 15 | EVMLA, 16 | /// Whether to dump the Ethereal IR code. 17 | EthIR, 18 | /// Whether to dump the Vyper LLL IR code. 19 | LLL, 20 | /// Whether to dump the LLVM IR code. 21 | LLVM, 22 | /// Whether to dump the assembly code. 23 | Assembly, 24 | } 25 | 26 | impl IRType { 27 | /// 28 | /// Returns the file extension for the specified IR. 29 | /// 30 | pub fn file_extension(&self) -> &'static str { 31 | match self { 32 | Self::Yul => era_compiler_common::EXTENSION_YUL, 33 | Self::EthIR => era_compiler_common::EXTENSION_ETHIR, 34 | Self::EVMLA => era_compiler_common::EXTENSION_EVMLA, 35 | Self::LLL => era_compiler_common::EXTENSION_LLL, 36 | Self::LLVM => era_compiler_common::EXTENSION_LLVM_SOURCE, 37 | Self::Assembly => era_compiler_common::EXTENSION_ERAVM_ASSEMBLY, 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/debug_config/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The debug configuration. 3 | //! 4 | 5 | pub mod ir_type; 6 | 7 | use std::path::PathBuf; 8 | 9 | use self::ir_type::IRType; 10 | 11 | /// 12 | /// The debug configuration. 13 | /// 14 | #[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)] 15 | pub struct DebugConfig { 16 | /// The directory to dump the IRs to. 17 | pub output_directory: PathBuf, 18 | } 19 | 20 | impl DebugConfig { 21 | /// 22 | /// A shortcut constructor. 23 | /// 24 | pub fn new(output_directory: PathBuf) -> Self { 25 | Self { output_directory } 26 | } 27 | 28 | /// 29 | /// Create a subdirectory and return a copy of `DebugConfig` pointing there. 30 | /// 31 | pub fn create_subdirectory(&self, directory_name: &str) -> anyhow::Result { 32 | let sanitized_name = Self::sanitize_filename_fragment(directory_name); 33 | let subdirectory_path = self.output_directory.join(sanitized_name.as_str()); 34 | std::fs::create_dir_all(subdirectory_path.as_path())?; 35 | Ok(Self { 36 | output_directory: subdirectory_path, 37 | }) 38 | } 39 | 40 | /// 41 | /// Dumps the Yul IR. 42 | /// 43 | pub fn dump_yul(&self, contract_path: &str, code: &str) -> anyhow::Result<()> { 44 | let mut file_path = self.output_directory.to_owned(); 45 | let full_file_name = Self::full_file_name(contract_path, None, IRType::Yul); 46 | file_path.push(full_file_name); 47 | std::fs::write(file_path, code)?; 48 | 49 | Ok(()) 50 | } 51 | 52 | /// 53 | /// Dumps the EVM legacy assembly IR. 54 | /// 55 | pub fn dump_evmla(&self, contract_path: &str, code: &str) -> anyhow::Result<()> { 56 | let mut file_path = self.output_directory.to_owned(); 57 | let full_file_name = Self::full_file_name(contract_path, None, IRType::EVMLA); 58 | file_path.push(full_file_name); 59 | std::fs::write(file_path, code)?; 60 | 61 | Ok(()) 62 | } 63 | 64 | /// 65 | /// Dumps the Ethereal IR. 66 | /// 67 | pub fn dump_ethir(&self, contract_path: &str, code: &str) -> anyhow::Result<()> { 68 | let mut file_path = self.output_directory.to_owned(); 69 | let full_file_name = Self::full_file_name(contract_path, None, IRType::EthIR); 70 | file_path.push(full_file_name); 71 | std::fs::write(file_path, code)?; 72 | 73 | Ok(()) 74 | } 75 | 76 | /// 77 | /// Dumps the LLL IR. 78 | /// 79 | pub fn dump_lll(&self, contract_path: &str, code: &str) -> anyhow::Result<()> { 80 | let mut file_path = self.output_directory.to_owned(); 81 | let full_file_name = Self::full_file_name(contract_path, None, IRType::LLL); 82 | file_path.push(full_file_name); 83 | std::fs::write(file_path, code)?; 84 | 85 | Ok(()) 86 | } 87 | 88 | /// 89 | /// Dumps the unoptimized LLVM IR. 90 | /// 91 | pub fn dump_llvm_ir_unoptimized( 92 | &self, 93 | contract_path: &str, 94 | module: &inkwell::module::Module, 95 | is_size_fallback: bool, 96 | ) -> anyhow::Result<()> { 97 | let llvm_code = module.print_to_string().to_string(); 98 | 99 | let mut suffix = "unoptimized".to_owned(); 100 | if is_size_fallback { 101 | suffix.push_str(".size_fallback"); 102 | } 103 | 104 | let mut file_path = self.output_directory.to_owned(); 105 | let full_file_name = 106 | Self::full_file_name(contract_path, Some(suffix.as_str()), IRType::LLVM); 107 | file_path.push(full_file_name); 108 | std::fs::write(file_path, llvm_code)?; 109 | 110 | Ok(()) 111 | } 112 | 113 | /// 114 | /// Dumps the optimized LLVM IR. 115 | /// 116 | pub fn dump_llvm_ir_optimized( 117 | &self, 118 | contract_path: &str, 119 | module: &inkwell::module::Module, 120 | is_size_fallback: bool, 121 | ) -> anyhow::Result<()> { 122 | let llvm_code = module.print_to_string().to_string(); 123 | 124 | let mut suffix = "optimized".to_owned(); 125 | if is_size_fallback { 126 | suffix.push_str(".size_fallback"); 127 | } 128 | 129 | let mut file_path = self.output_directory.to_owned(); 130 | let full_file_name = 131 | Self::full_file_name(contract_path, Some(suffix.as_str()), IRType::LLVM); 132 | file_path.push(full_file_name); 133 | std::fs::write(file_path, llvm_code)?; 134 | 135 | Ok(()) 136 | } 137 | 138 | /// 139 | /// Dumps the assembly. 140 | /// 141 | pub fn dump_assembly( 142 | &self, 143 | contract_path: &str, 144 | code: &str, 145 | is_size_fallback: bool, 146 | ) -> anyhow::Result<()> { 147 | let suffix = if is_size_fallback { 148 | Some("size_fallback") 149 | } else { 150 | None 151 | }; 152 | 153 | let mut file_path = self.output_directory.to_owned(); 154 | let full_file_name = Self::full_file_name(contract_path, suffix, IRType::Assembly); 155 | file_path.push(full_file_name); 156 | std::fs::write(file_path, code)?; 157 | 158 | Ok(()) 159 | } 160 | 161 | /// 162 | /// Rules to encode a string into a valid filename. 163 | /// 164 | fn sanitize_filename_fragment(string: &str) -> String { 165 | string.replace([' ', ':', '/', '\\'], "_") 166 | } 167 | 168 | /// 169 | /// Creates a full file name, given the contract full path, suffix, and extension. 170 | /// 171 | fn full_file_name(contract_path: &str, suffix: Option<&str>, ir_type: IRType) -> String { 172 | let mut full_file_name = Self::sanitize_filename_fragment(contract_path); 173 | 174 | if let Some(suffix) = suffix { 175 | full_file_name.push('.'); 176 | full_file_name.push_str(suffix); 177 | } 178 | full_file_name.push('.'); 179 | full_file_name.push_str(ir_type.file_extension()); 180 | full_file_name 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/debug_info.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM debug information. 3 | //! 4 | 5 | use inkwell::debug_info::AsDIScope; 6 | use num::Zero; 7 | 8 | /// 9 | /// The LLVM debug information. 10 | /// 11 | pub struct DebugInfo<'ctx> { 12 | /// The compile unit. 13 | compile_unit: inkwell::debug_info::DICompileUnit<'ctx>, 14 | /// The debug info builder. 15 | builder: inkwell::debug_info::DebugInfoBuilder<'ctx>, 16 | } 17 | 18 | impl<'ctx> DebugInfo<'ctx> { 19 | /// 20 | /// A shortcut constructor. 21 | /// 22 | pub fn new(module: &inkwell::module::Module<'ctx>) -> Self { 23 | let (builder, compile_unit) = module.create_debug_info_builder( 24 | true, 25 | inkwell::debug_info::DWARFSourceLanguage::C, 26 | module.get_name().to_string_lossy().as_ref(), 27 | "", 28 | "", 29 | false, 30 | "", 31 | 0, 32 | "", 33 | inkwell::debug_info::DWARFEmissionKind::Full, 34 | 0, 35 | false, 36 | false, 37 | "", 38 | "", 39 | ); 40 | 41 | Self { 42 | compile_unit, 43 | builder, 44 | } 45 | } 46 | 47 | /// 48 | /// Creates a function info. 49 | /// 50 | pub fn create_function( 51 | &self, 52 | name: &str, 53 | ) -> anyhow::Result> { 54 | let subroutine_type = self.builder.create_subroutine_type( 55 | self.compile_unit.get_file(), 56 | Some(self.create_type(era_compiler_common::BIT_LENGTH_FIELD)?), 57 | &[], 58 | inkwell::debug_info::DIFlags::zero(), 59 | ); 60 | 61 | let function = self.builder.create_function( 62 | self.compile_unit.get_file().as_debug_info_scope(), 63 | name, 64 | None, 65 | self.compile_unit.get_file(), 66 | 42, 67 | subroutine_type, 68 | true, 69 | false, 70 | 1, 71 | inkwell::debug_info::DIFlags::zero(), 72 | false, 73 | ); 74 | 75 | self.builder.create_lexical_block( 76 | function.as_debug_info_scope(), 77 | self.compile_unit.get_file(), 78 | 1, 79 | 1, 80 | ); 81 | 82 | Ok(function) 83 | } 84 | 85 | /// 86 | /// Creates a primitive type info. 87 | /// 88 | pub fn create_type( 89 | &self, 90 | bit_length: usize, 91 | ) -> anyhow::Result> { 92 | self.builder 93 | .create_basic_type( 94 | "U256", 95 | bit_length as u64, 96 | 0, 97 | inkwell::debug_info::DIFlags::zero(), 98 | ) 99 | .map(|basic_type| basic_type.as_type()) 100 | .map_err(|error| anyhow::anyhow!("debug info: {error}")) 101 | } 102 | 103 | /// 104 | /// Finalizes the builder. 105 | /// 106 | pub fn finalize(&self) { 107 | self.builder.finalize(); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/eravm/build.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM module build. 3 | //! 4 | 5 | use std::collections::BTreeMap; 6 | 7 | /// 8 | /// The LLVM module build. 9 | /// 10 | #[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)] 11 | pub struct Build { 12 | /// Bytecode. 13 | pub bytecode: Vec, 14 | /// Bytecode hash. Only available after linking. 15 | pub bytecode_hash: Option<[u8; era_compiler_common::BYTE_LENGTH_FIELD]>, 16 | /// Project metadata. 17 | pub metadata: Vec, 18 | /// Hash-to-full-path mapping of the contract factory dependencies. 19 | pub factory_dependencies: BTreeMap, 20 | /// Text assembly. 21 | pub assembly: Option, 22 | } 23 | 24 | impl Build { 25 | /// 26 | /// A shortcut constructor. 27 | /// 28 | pub fn new(bytecode: Vec, metadata: Vec, assembly: Option) -> Self { 29 | Self { 30 | bytecode, 31 | bytecode_hash: None, 32 | metadata, 33 | factory_dependencies: BTreeMap::new(), 34 | assembly, 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/eravm/const.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM context constants. 3 | //! 4 | 5 | /// The EraVM version. 6 | pub const ERAVM_VERSION: semver::Version = semver::Version::new(1, 5, 0); 7 | 8 | /// The heap memory pointer pointer global variable name. 9 | pub static GLOBAL_HEAP_MEMORY_POINTER: &str = "memory_pointer"; 10 | 11 | /// The calldata pointer global variable name. 12 | pub static GLOBAL_CALLDATA_POINTER: &str = "ptr_calldata"; 13 | 14 | /// The calldata size pointer global variable name. 15 | pub static GLOBAL_CALLDATA_SIZE: &str = "calldatasize"; 16 | 17 | /// The return data pointer global variable name. 18 | pub static GLOBAL_RETURN_DATA_POINTER: &str = "ptr_return_data"; 19 | 20 | /// The return data size pointer global variable name. 21 | pub static GLOBAL_RETURN_DATA_SIZE: &str = "returndatasize"; 22 | 23 | /// The decommit pointer global variable name. 24 | pub static GLOBAL_DECOMMIT_POINTER: &str = "ptr_decommit"; 25 | 26 | /// The call flags global variable name. 27 | pub static GLOBAL_CALL_FLAGS: &str = "call_flags"; 28 | 29 | /// The extra ABI data global variable name. 30 | pub static GLOBAL_EXTRA_ABI_DATA: &str = "extra_abi_data"; 31 | 32 | /// The active pointer array global variable name. 33 | pub static GLOBAL_ACTIVE_POINTER_ARRAY: &str = "ptr_active"; 34 | 35 | /// The constant array global variable name prefix. 36 | pub static GLOBAL_CONST_ARRAY_PREFIX: &str = "const_array_"; 37 | 38 | /// The global verbatim getter identifier prefix. 39 | pub static GLOBAL_VERBATIM_GETTER_PREFIX: &str = "get_global::"; 40 | 41 | /// The number of available active pointers. 42 | pub const AVAILABLE_ACTIVE_POINTERS_NUMBER: usize = 16; 43 | 44 | /// The external call data offset in the auxiliary heap. 45 | pub const HEAP_AUX_OFFSET_EXTERNAL_CALL: u64 = 0; 46 | 47 | /// The constructor return data offset in the auxiliary heap. 48 | pub const HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA: u64 = 49 | 8 * (era_compiler_common::BYTE_LENGTH_FIELD as u64); 50 | 51 | /// The number of the extra ABI data arguments. 52 | pub const EXTRA_ABI_DATA_SIZE: usize = 10; 53 | 54 | /// The `create` method deployer signature. 55 | pub static DEPLOYER_SIGNATURE_CREATE: &str = "create(bytes32,bytes32,bytes)"; 56 | 57 | /// The `create2` method deployer signature. 58 | pub static DEPLOYER_SIGNATURE_CREATE2: &str = "create2(bytes32,bytes32,bytes)"; 59 | 60 | /// The absence of system call bit. 61 | pub const NO_SYSTEM_CALL_BIT: bool = false; 62 | 63 | /// The system call bit. 64 | pub const SYSTEM_CALL_BIT: bool = true; 65 | 66 | /// The default disassembler mode. 67 | pub const DISASSEMBLER_DEFAULT_MODE: u64 = 1; 68 | 69 | /// 70 | /// The deployer call header size that consists of: 71 | /// - selector (4 bytes) 72 | /// - salt (32 bytes) 73 | /// - bytecode hash (32 bytes) 74 | /// - constructor arguments offset (32 bytes) 75 | /// - constructor arguments length (32 bytes) 76 | /// 77 | pub const DEPLOYER_CALL_HEADER_SIZE: usize = 78 | era_compiler_common::BYTE_LENGTH_X32 + (era_compiler_common::BYTE_LENGTH_FIELD * 4); 79 | -------------------------------------------------------------------------------- /src/eravm/context/address_space.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The address space aliases. 3 | //! 4 | 5 | use crate::context::traits::address_space::IAddressSpace; 6 | 7 | /// 8 | /// The address space aliases. 9 | /// 10 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 11 | pub enum AddressSpace { 12 | /// The stack memory. 13 | Stack, 14 | /// The heap memory. 15 | Heap, 16 | /// The auxiliary heap memory. 17 | HeapAuxiliary, 18 | /// The generic memory page. 19 | Generic, 20 | /// The code area. 21 | Code, 22 | /// The storage. 23 | Storage, 24 | /// The transient storage. 25 | TransientStorage, 26 | } 27 | 28 | impl IAddressSpace for AddressSpace { 29 | fn stack() -> Self { 30 | Self::Stack 31 | } 32 | } 33 | 34 | impl From for inkwell::AddressSpace { 35 | fn from(value: AddressSpace) -> Self { 36 | match value { 37 | AddressSpace::Stack => Self::from(0), 38 | AddressSpace::Heap => Self::from(1), 39 | AddressSpace::HeapAuxiliary => Self::from(2), 40 | AddressSpace::Generic => Self::from(3), 41 | AddressSpace::Code => Self::from(4), 42 | AddressSpace::Storage => Self::from(5), 43 | AddressSpace::TransientStorage => Self::from(6), 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/eravm/context/evmla_data.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM IR generator EVM legacy assembly data. 3 | //! 4 | 5 | use crate::context::traits::evmla_data::IEVMLAData; 6 | use crate::context::value::Value; 7 | 8 | /// 9 | /// The LLVM IR generator EVM legacy assembly data. 10 | /// 11 | /// Describes some data that is only relevant to the EVM legacy assembly. 12 | /// 13 | #[derive(Debug, Clone)] 14 | pub struct EVMLAData<'ctx> { 15 | /// The Solidity compiler version. 16 | /// Some instruction behave differenly depending on the version. 17 | pub version: semver::Version, 18 | /// The static stack allocated for the current function. 19 | pub stack: Vec>, 20 | } 21 | 22 | impl EVMLAData<'_> { 23 | /// The default stack size. 24 | pub const DEFAULT_STACK_SIZE: usize = 64; 25 | 26 | /// 27 | /// A shortcut constructor. 28 | /// 29 | pub fn new(version: semver::Version) -> Self { 30 | Self { 31 | version, 32 | stack: Vec::with_capacity(Self::DEFAULT_STACK_SIZE), 33 | } 34 | } 35 | } 36 | 37 | impl<'ctx> IEVMLAData<'ctx> for EVMLAData<'ctx> { 38 | fn get_element(&self, position: usize) -> &Value<'ctx> { 39 | &self.stack[position] 40 | } 41 | 42 | fn set_element(&mut self, position: usize, value: Value<'ctx>) { 43 | self.stack[position] = value; 44 | } 45 | 46 | fn set_original(&mut self, position: usize, original: String) { 47 | self.stack[position].original = Some(original); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/eravm/context/function/runtime/deploy_code.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The deploy code function. 3 | //! 4 | 5 | use crate::context::pointer::Pointer; 6 | use crate::context::IContext; 7 | use crate::eravm::context::address_space::AddressSpace; 8 | use crate::eravm::context::function::runtime::Runtime; 9 | use crate::eravm::context::Context; 10 | use crate::eravm::WriteLLVM; 11 | 12 | /// 13 | /// The deploy code function. 14 | /// 15 | /// Is a special function that is only used by the front-end generated code. 16 | /// 17 | #[derive(Debug)] 18 | pub struct DeployCode 19 | where 20 | B: WriteLLVM, 21 | { 22 | /// The deploy code AST representation. 23 | inner: B, 24 | } 25 | 26 | impl DeployCode 27 | where 28 | B: WriteLLVM, 29 | { 30 | /// 31 | /// A shortcut constructor. 32 | /// 33 | pub fn new(inner: B) -> Self { 34 | Self { inner } 35 | } 36 | } 37 | 38 | impl WriteLLVM for DeployCode 39 | where 40 | B: WriteLLVM, 41 | { 42 | fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> { 43 | let function_type = 44 | context.function_type::(vec![], 0, false); 45 | context.add_function( 46 | Runtime::FUNCTION_DEPLOY_CODE, 47 | function_type, 48 | 0, 49 | Some(inkwell::module::Linkage::Private), 50 | )?; 51 | 52 | self.inner.declare(context) 53 | } 54 | 55 | fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> { 56 | context.set_current_function(Runtime::FUNCTION_DEPLOY_CODE)?; 57 | 58 | context.set_basic_block(context.current_function().borrow().entry_block()); 59 | context.set_code_segment(era_compiler_common::CodeSegment::Deploy); 60 | if let Some(vyper) = context.vyper_data.as_ref() { 61 | for index in 0..vyper.immutables_size() / era_compiler_common::BYTE_LENGTH_FIELD { 62 | let offset = (crate::eravm::r#const::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA 63 | as usize) 64 | + (1 + index) * 2 * era_compiler_common::BYTE_LENGTH_FIELD; 65 | let value = index * era_compiler_common::BYTE_LENGTH_FIELD; 66 | let pointer = Pointer::new_with_offset( 67 | context, 68 | AddressSpace::HeapAuxiliary, 69 | context.field_type(), 70 | context.field_const(offset as u64), 71 | "immutable_index_initializer", 72 | )?; 73 | context.build_store(pointer, context.field_const(value as u64))?; 74 | } 75 | } 76 | 77 | self.inner.into_llvm(context)?; 78 | match context 79 | .basic_block() 80 | .get_last_instruction() 81 | .map(|instruction| instruction.get_opcode()) 82 | { 83 | Some(inkwell::values::InstructionOpcode::Br) => {} 84 | Some(inkwell::values::InstructionOpcode::Switch) => {} 85 | _ => context 86 | .build_unconditional_branch(context.current_function().borrow().return_block())?, 87 | } 88 | 89 | context.set_basic_block(context.current_function().borrow().return_block()); 90 | context.build_return(None)?; 91 | 92 | Ok(()) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/eravm/context/function/runtime/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The front-end runtime functions. 3 | //! 4 | 5 | pub mod default_call; 6 | pub mod deploy_code; 7 | pub mod deployer_call; 8 | pub mod entry; 9 | pub mod runtime_code; 10 | 11 | use crate::context::function::declaration::Declaration as FunctionDeclaration; 12 | use crate::context::IContext; 13 | use crate::eravm::context::address_space::AddressSpace; 14 | use crate::eravm::context::Context; 15 | use crate::eravm::WriteLLVM; 16 | 17 | use self::default_call::DefaultCall; 18 | use self::deployer_call::DeployerCall; 19 | 20 | /// 21 | /// The front-end runtime functions. 22 | /// 23 | #[derive(Debug, Default, Clone)] 24 | pub struct Runtime {} 25 | 26 | impl Runtime { 27 | /// The main entry function name. 28 | pub const FUNCTION_ENTRY: &'static str = "__entry"; 29 | 30 | /// The deploy code function name. 31 | pub const FUNCTION_DEPLOY_CODE: &'static str = "__deploy"; 32 | 33 | /// The runtime code function name. 34 | pub const FUNCTION_RUNTIME_CODE: &'static str = "__runtime"; 35 | 36 | /// 37 | /// Returns the corresponding runtime function. 38 | /// 39 | pub fn default_call<'ctx>( 40 | context: &Context<'ctx>, 41 | call_function: FunctionDeclaration<'ctx>, 42 | ) -> FunctionDeclaration<'ctx> { 43 | context 44 | .get_function(DefaultCall::name(call_function).as_str()) 45 | .expect("Always exists") 46 | .borrow() 47 | .declaration() 48 | } 49 | 50 | /// 51 | /// Returns the corresponding runtime function. 52 | /// 53 | pub fn deployer_call<'ctx>( 54 | context: &Context<'ctx>, 55 | address_space: AddressSpace, 56 | ) -> FunctionDeclaration<'ctx> { 57 | context 58 | .get_function(DeployerCall::name(address_space).as_str()) 59 | .expect("Always exists") 60 | .borrow() 61 | .declaration() 62 | } 63 | } 64 | 65 | impl WriteLLVM for Runtime { 66 | fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> { 67 | DefaultCall::new(context.llvm_runtime().far_call).declare(context)?; 68 | DefaultCall::new(context.llvm_runtime().static_call).declare(context)?; 69 | DefaultCall::new(context.llvm_runtime().delegate_call).declare(context)?; 70 | DeployerCall::new(AddressSpace::Heap).declare(context)?; 71 | DeployerCall::new(AddressSpace::HeapAuxiliary).declare(context)?; 72 | 73 | Ok(()) 74 | } 75 | 76 | fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> { 77 | DefaultCall::new(context.llvm_runtime().far_call).into_llvm(context)?; 78 | DefaultCall::new(context.llvm_runtime().static_call).into_llvm(context)?; 79 | DefaultCall::new(context.llvm_runtime().delegate_call).into_llvm(context)?; 80 | DeployerCall::new(AddressSpace::Heap).into_llvm(context)?; 81 | DeployerCall::new(AddressSpace::HeapAuxiliary).into_llvm(context)?; 82 | 83 | Ok(()) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/eravm/context/function/runtime/runtime_code.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The runtime code function. 3 | //! 4 | 5 | use crate::context::IContext; 6 | use crate::eravm::context::function::runtime::Runtime; 7 | use crate::eravm::context::Context; 8 | use crate::eravm::WriteLLVM; 9 | 10 | /// 11 | /// The runtime code function. 12 | /// 13 | /// Is a special function that is only used by the front-end generated code. 14 | /// 15 | #[derive(Debug)] 16 | pub struct RuntimeCode 17 | where 18 | B: WriteLLVM, 19 | { 20 | /// The runtime code AST representation. 21 | inner: B, 22 | } 23 | 24 | impl RuntimeCode 25 | where 26 | B: WriteLLVM, 27 | { 28 | /// 29 | /// A shortcut constructor. 30 | /// 31 | pub fn new(inner: B) -> Self { 32 | Self { inner } 33 | } 34 | } 35 | 36 | impl WriteLLVM for RuntimeCode 37 | where 38 | B: WriteLLVM, 39 | { 40 | fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> { 41 | let function_type = 42 | context.function_type::(vec![], 0, false); 43 | context.add_function( 44 | Runtime::FUNCTION_RUNTIME_CODE, 45 | function_type, 46 | 0, 47 | Some(inkwell::module::Linkage::Private), 48 | )?; 49 | 50 | self.inner.declare(context) 51 | } 52 | 53 | fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> { 54 | context.set_current_function(Runtime::FUNCTION_RUNTIME_CODE)?; 55 | 56 | context.set_basic_block(context.current_function().borrow().entry_block()); 57 | context.set_code_segment(era_compiler_common::CodeSegment::Runtime); 58 | self.inner.into_llvm(context)?; 59 | match context 60 | .basic_block() 61 | .get_last_instruction() 62 | .map(|instruction| instruction.get_opcode()) 63 | { 64 | Some(inkwell::values::InstructionOpcode::Br) => {} 65 | Some(inkwell::values::InstructionOpcode::Switch) => {} 66 | _ => context 67 | .build_unconditional_branch(context.current_function().borrow().return_block())?, 68 | } 69 | 70 | context.set_basic_block(context.current_function().borrow().return_block()); 71 | context.build_return(None)?; 72 | 73 | Ok(()) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/eravm/context/function/vyper_data.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM function Vyper data. 3 | //! 4 | 5 | use std::collections::HashMap; 6 | 7 | /// 8 | /// The LLVM function Vyper data. 9 | /// 10 | /// Describes some data that is only relevant to Vyper. 11 | /// 12 | #[derive(Debug)] 13 | pub struct VyperData { 14 | /// The block-local variables. They are still allocated at the beginning of the function, 15 | /// but their parent block must be known in order to pass the implicit arguments thereto. 16 | /// Is only used by the Vyper LLL IR compiler. 17 | label_arguments: HashMap>, 18 | } 19 | 20 | impl Default for VyperData { 21 | fn default() -> Self { 22 | Self { 23 | label_arguments: HashMap::with_capacity(Self::LABEL_ARGUMENTS_HASHMAP_INITIAL_CAPACITY), 24 | } 25 | } 26 | } 27 | 28 | impl VyperData { 29 | /// The label arguments hashmap default capacity. 30 | const LABEL_ARGUMENTS_HASHMAP_INITIAL_CAPACITY: usize = 16; 31 | 32 | /// 33 | /// A shortcut constructor. 34 | /// 35 | pub fn new() -> Self { 36 | Self::default() 37 | } 38 | 39 | /// 40 | /// Returns the list of a Vyper label arguments. 41 | /// 42 | pub fn label_arguments(&self, label_name: &str) -> Option> { 43 | self.label_arguments.get(label_name).cloned() 44 | } 45 | 46 | /// 47 | /// Inserts arguments for the specified label. 48 | /// 49 | pub fn insert_label_arguments(&mut self, label_name: String, arguments: Vec) { 50 | self.label_arguments.insert(label_name, arguments); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/eravm/context/function/yul_data.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM function Yul data. 3 | //! 4 | 5 | use std::collections::HashMap; 6 | 7 | use num::BigUint; 8 | 9 | /// 10 | /// The LLVM function Yul data. 11 | /// 12 | /// Describes some data that is only relevant to Yul. 13 | /// 14 | #[derive(Debug)] 15 | pub struct YulData { 16 | /// The constants saved to variables. Used for peculiar cases like call simulation. 17 | /// It is a partial implementation of the constant propagation. 18 | constants: HashMap, 19 | } 20 | 21 | impl Default for YulData { 22 | fn default() -> Self { 23 | Self { 24 | constants: HashMap::with_capacity(Self::CONSTANTS_HASHMAP_INITIAL_CAPACITY), 25 | } 26 | } 27 | } 28 | 29 | impl YulData { 30 | /// The constants hashmap default capacity. 31 | const CONSTANTS_HASHMAP_INITIAL_CAPACITY: usize = 16; 32 | 33 | /// 34 | /// A shortcut constructor. 35 | /// 36 | pub fn new() -> Self { 37 | Self::default() 38 | } 39 | 40 | /// 41 | /// Returns a constant if it has been saved. 42 | /// 43 | pub fn get_constant(&self, name: &str) -> Option { 44 | self.constants.get(name).cloned() 45 | } 46 | 47 | /// 48 | /// Saves a constant detected with the partial constant propagation. 49 | /// 50 | pub fn insert_constant(&mut self, name: String, value: BigUint) { 51 | self.constants.insert(name, value); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/eravm/context/global.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM global value. 3 | //! 4 | 5 | use inkwell::types::BasicType; 6 | use inkwell::values::BasicValue; 7 | 8 | use crate::context::IContext; 9 | use crate::eravm::context::address_space::AddressSpace; 10 | use crate::eravm::context::Context; 11 | 12 | /// 13 | /// The LLVM global value. 14 | /// 15 | #[derive(Debug, Clone, Copy)] 16 | pub struct Global<'ctx> { 17 | /// The global type. 18 | pub r#type: inkwell::types::BasicTypeEnum<'ctx>, 19 | /// The global value. 20 | pub value: inkwell::values::GlobalValue<'ctx>, 21 | } 22 | 23 | impl<'ctx> Global<'ctx> { 24 | /// 25 | /// A shortcut constructor. 26 | /// 27 | pub fn new( 28 | context: &mut Context<'ctx>, 29 | r#type: T, 30 | address_space: AddressSpace, 31 | initializer: V, 32 | name: &str, 33 | ) -> anyhow::Result 34 | where 35 | T: BasicType<'ctx>, 36 | V: BasicValue<'ctx>, 37 | { 38 | let r#type = r#type.as_basic_type_enum(); 39 | 40 | let value = context 41 | .module() 42 | .add_global(r#type, Some(address_space.into()), name); 43 | let global = Self { r#type, value }; 44 | 45 | global.value.set_linkage(inkwell::module::Linkage::Private); 46 | global 47 | .value 48 | .set_visibility(inkwell::GlobalVisibility::Default); 49 | global.value.set_externally_initialized(false); 50 | if let AddressSpace::Code = address_space { 51 | global.value.set_constant(true); 52 | } 53 | if !r#type.is_pointer_type() { 54 | global.value.set_initializer(&initializer); 55 | } else { 56 | global.value.set_initializer(&r#type.const_zero()); 57 | context.build_store(global.into(), initializer)?; 58 | } 59 | 60 | Ok(global) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/eravm/context/solidity_data.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM IR generator Solidity data. 3 | //! 4 | 5 | use std::collections::BTreeMap; 6 | use std::collections::BTreeSet; 7 | 8 | use crate::context::traits::solidity_data::ISolidityData; 9 | 10 | /// 11 | /// The LLVM IR generator Solidity data. 12 | /// 13 | /// Describes some data that is only relevant to Solidity. 14 | /// 15 | #[derive(Debug, Default)] 16 | pub struct SolidityData { 17 | /// The immutables identifier-to-offset mapping. Is only used by Solidity due to 18 | /// the arbitrariness of its identifiers. 19 | immutables: BTreeMap, 20 | } 21 | 22 | impl ISolidityData for SolidityData { 23 | fn offsets(&self, _id: &str) -> Option<&BTreeSet> { 24 | None 25 | } 26 | } 27 | 28 | impl SolidityData { 29 | /// 30 | /// A shortcut constructor. 31 | /// 32 | pub fn new() -> Self { 33 | Self::default() 34 | } 35 | 36 | /// 37 | /// Returns the current number of immutables values in the contract. 38 | /// 39 | pub fn immutables_size(&self) -> usize { 40 | self.immutables.len() * era_compiler_common::BYTE_LENGTH_FIELD 41 | } 42 | 43 | /// 44 | /// Allocates memory for an immutable value in the auxiliary heap. 45 | /// 46 | /// If the identifier is already known, just returns its offset. 47 | /// 48 | pub fn allocate_immutable(&mut self, identifier: &str) -> usize { 49 | let number_of_elements = self.immutables.len(); 50 | let new_offset = number_of_elements * era_compiler_common::BYTE_LENGTH_FIELD; 51 | *self 52 | .immutables 53 | .entry(identifier.to_owned()) 54 | .or_insert(new_offset) 55 | } 56 | 57 | /// 58 | /// Gets the offset of the immutable value. 59 | /// 60 | /// If the value is not yet allocated, then it is done forcibly. 61 | /// 62 | pub fn get_or_allocate_immutable(&mut self, identifier: &str) -> usize { 63 | match self.immutables.get(identifier).copied() { 64 | Some(offset) => offset, 65 | None => self.allocate_immutable(identifier), 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/eravm/context/tests.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM IR generator context tests. 3 | //! 4 | 5 | use crate::context::attribute::Attribute; 6 | use crate::context::IContext; 7 | use crate::eravm::context::Context; 8 | use crate::optimizer::settings::Settings as OptimizerSettings; 9 | use crate::optimizer::Optimizer; 10 | 11 | pub fn create_context( 12 | llvm: &inkwell::context::Context, 13 | optimizer_settings: OptimizerSettings, 14 | ) -> Context { 15 | crate::eravm::initialize_target(); 16 | 17 | let module = llvm.create_module("test"); 18 | let optimizer = Optimizer::new(optimizer_settings); 19 | 20 | Context::new(&llvm, module, vec![], optimizer, None) 21 | } 22 | 23 | #[test] 24 | pub fn check_attribute_null_pointer_is_invalid() { 25 | let llvm = inkwell::context::Context::create(); 26 | let mut context = create_context(&llvm, OptimizerSettings::cycles()); 27 | 28 | let function = context 29 | .add_function( 30 | "test", 31 | context 32 | .field_type() 33 | .fn_type(&[context.field_type().into()], false), 34 | 1, 35 | Some(inkwell::module::Linkage::External), 36 | ) 37 | .expect("Failed to add function"); 38 | assert!(function 39 | .borrow() 40 | .declaration() 41 | .value 42 | .attributes(inkwell::attributes::AttributeLoc::Function) 43 | .contains(&llvm.create_enum_attribute(Attribute::NullPointerIsValid as u32, 0))); 44 | } 45 | 46 | #[test] 47 | pub fn check_attribute_optimize_for_size_mode_3() { 48 | let llvm = inkwell::context::Context::create(); 49 | let mut context = create_context(&llvm, OptimizerSettings::cycles()); 50 | 51 | let function = context 52 | .add_function( 53 | "test", 54 | context 55 | .field_type() 56 | .fn_type(&[context.field_type().into()], false), 57 | 1, 58 | Some(inkwell::module::Linkage::External), 59 | ) 60 | .expect("Failed to add function"); 61 | assert!(!function 62 | .borrow() 63 | .declaration() 64 | .value 65 | .attributes(inkwell::attributes::AttributeLoc::Function) 66 | .contains(&llvm.create_enum_attribute(Attribute::OptimizeForSize as u32, 0))); 67 | } 68 | 69 | #[test] 70 | pub fn check_attribute_optimize_for_size_mode_z() { 71 | let llvm = inkwell::context::Context::create(); 72 | let mut context = create_context(&llvm, OptimizerSettings::size()); 73 | 74 | let function = context 75 | .add_function( 76 | "test", 77 | context 78 | .field_type() 79 | .fn_type(&[context.field_type().into()], false), 80 | 1, 81 | Some(inkwell::module::Linkage::External), 82 | ) 83 | .expect("Failed to add function"); 84 | assert!(function 85 | .borrow() 86 | .declaration() 87 | .value 88 | .attributes(inkwell::attributes::AttributeLoc::Function) 89 | .contains(&llvm.create_enum_attribute(Attribute::OptimizeForSize as u32, 0))); 90 | } 91 | 92 | #[test] 93 | pub fn check_attribute_min_size_mode_3() { 94 | let llvm = inkwell::context::Context::create(); 95 | let mut context = create_context(&llvm, OptimizerSettings::cycles()); 96 | 97 | let function = context 98 | .add_function( 99 | "test", 100 | context 101 | .field_type() 102 | .fn_type(&[context.field_type().into()], false), 103 | 1, 104 | Some(inkwell::module::Linkage::External), 105 | ) 106 | .expect("Failed to add function"); 107 | assert!(!function 108 | .borrow() 109 | .declaration() 110 | .value 111 | .attributes(inkwell::attributes::AttributeLoc::Function) 112 | .contains(&llvm.create_enum_attribute(Attribute::MinSize as u32, 0))); 113 | } 114 | 115 | #[test] 116 | pub fn check_attribute_min_size_mode_z() { 117 | let llvm = inkwell::context::Context::create(); 118 | let mut context = create_context(&llvm, OptimizerSettings::size()); 119 | 120 | let function = context 121 | .add_function( 122 | "test", 123 | context 124 | .field_type() 125 | .fn_type(&[context.field_type().into()], false), 126 | 1, 127 | Some(inkwell::module::Linkage::External), 128 | ) 129 | .expect("Failed to add function"); 130 | assert!(function 131 | .borrow() 132 | .declaration() 133 | .value 134 | .attributes(inkwell::attributes::AttributeLoc::Function) 135 | .contains(&llvm.create_enum_attribute(Attribute::MinSize as u32, 0))); 136 | } 137 | -------------------------------------------------------------------------------- /src/eravm/context/vyper_data.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM IR generator Vyper data. 3 | //! 4 | 5 | /// 6 | /// The LLVM IR generator Vyper data. 7 | /// 8 | /// Describes some data that is only relevant to Vyper. 9 | /// 10 | #[derive(Debug)] 11 | pub struct VyperData { 12 | /// The immutables size tracker. Stores the size in bytes. 13 | /// Does not take into account the size of the indexes. 14 | immutables_size: usize, 15 | /// Whether the contract minimal proxy has been used. 16 | is_minimal_proxy_used: bool, 17 | } 18 | 19 | impl VyperData { 20 | /// 21 | /// A shortcut constructor. 22 | /// 23 | pub fn new(immutables_size: usize, is_minimal_proxy_used: bool) -> Self { 24 | Self { 25 | immutables_size, 26 | is_minimal_proxy_used, 27 | } 28 | } 29 | 30 | /// 31 | /// Returns the size of the immutables data of the contract. 32 | /// 33 | pub fn immutables_size(&self) -> usize { 34 | self.immutables_size 35 | } 36 | 37 | /// 38 | /// Sets the minimal proxy usage flag. 39 | /// 40 | pub fn set_is_minimal_proxy_used(&mut self) { 41 | self.is_minimal_proxy_used = true; 42 | } 43 | 44 | /// 45 | /// Returns the minimal proxy usage flag. 46 | /// 47 | pub fn is_minimal_proxy_used(&self) -> bool { 48 | self.is_minimal_proxy_used 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/eravm/context/yul_data.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM IR generator Yul data. 3 | //! 4 | 5 | use std::collections::BTreeMap; 6 | 7 | use num::Zero; 8 | 9 | use crate::context::traits::yul_data::IYulData; 10 | 11 | /// 12 | /// The LLVM IR generator Yul data. 13 | /// 14 | /// Describes some data that is only relevant to Yul. 15 | /// 16 | #[derive(Debug, Default)] 17 | pub struct YulData { 18 | /// The EraVM extensions flag. 19 | /// The call simulations only work if this mode is enabled. 20 | are_eravm_extensions_enabled: bool, 21 | /// Mapping from Yul object identifiers to full contract paths. 22 | identifier_paths: BTreeMap, 23 | /// The list of constant arrays in the code section. 24 | /// It is a temporary storage used until the finalization method is called. 25 | const_arrays: BTreeMap>, 26 | } 27 | 28 | impl YulData { 29 | /// 30 | /// A shortcut constructor. 31 | /// 32 | pub fn new( 33 | are_eravm_extensions_enabled: bool, 34 | identifier_paths: BTreeMap, 35 | ) -> Self { 36 | Self { 37 | are_eravm_extensions_enabled, 38 | identifier_paths, 39 | const_arrays: BTreeMap::new(), 40 | } 41 | } 42 | 43 | /// 44 | /// Whether the EraVM extensions is enabled. 45 | /// 46 | pub fn are_eravm_extensions_enabled(&self) -> bool { 47 | self.are_eravm_extensions_enabled 48 | } 49 | 50 | /// 51 | /// Declares a temporary constant array representation. 52 | /// 53 | pub fn const_array_declare(&mut self, index: u8, size: u16) -> anyhow::Result<()> { 54 | if self.const_arrays.contains_key(&index) { 55 | anyhow::bail!("constant array with index {index} is already declared",); 56 | } 57 | 58 | self.const_arrays 59 | .insert(index, vec![num::BigUint::zero(); size as usize]); 60 | 61 | Ok(()) 62 | } 63 | 64 | /// 65 | /// Sets a value in the constant array representation. 66 | /// 67 | pub fn const_array_set( 68 | &mut self, 69 | index: u8, 70 | offset: u16, 71 | value: num::BigUint, 72 | ) -> anyhow::Result<()> { 73 | let array = self.const_arrays.get_mut(&index).ok_or_else(|| { 74 | anyhow::anyhow!("The constant array with index {} is not declared", index) 75 | })?; 76 | if offset >= array.len() as u16 { 77 | anyhow::bail!( 78 | "constant array with index {index} has size {}, but the offset is {offset}", 79 | array.len(), 80 | ); 81 | } 82 | array[offset as usize] = value; 83 | 84 | Ok(()) 85 | } 86 | 87 | /// 88 | /// Finalizes the constant array declaration. 89 | /// 90 | pub fn const_array_take(&mut self, index: u8) -> anyhow::Result> { 91 | self.const_arrays.remove(&index).ok_or_else(|| { 92 | anyhow::anyhow!("The constant array with index {} is not declared", index) 93 | }) 94 | } 95 | } 96 | 97 | impl IYulData for YulData { 98 | fn resolve_path(&self, identifier: &str) -> Option<&str> { 99 | self.identifier_paths 100 | .get(identifier) 101 | .map(|path| path.as_str()) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/eravm/evm/arithmetic.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the arithmetic operations. 3 | //! 4 | 5 | use inkwell::values::BasicValue; 6 | 7 | use crate::context::IContext; 8 | use crate::eravm::context::Context; 9 | 10 | /// 11 | /// Translates the arithmetic addition. 12 | /// 13 | pub fn addition<'ctx>( 14 | context: &mut Context<'ctx>, 15 | operand_1: inkwell::values::IntValue<'ctx>, 16 | operand_2: inkwell::values::IntValue<'ctx>, 17 | ) -> anyhow::Result> { 18 | Ok(context 19 | .builder() 20 | .build_int_add(operand_1, operand_2, "addition_result")? 21 | .as_basic_value_enum()) 22 | } 23 | 24 | /// 25 | /// Translates the arithmetic subtraction. 26 | /// 27 | pub fn subtraction<'ctx>( 28 | context: &mut Context<'ctx>, 29 | operand_1: inkwell::values::IntValue<'ctx>, 30 | operand_2: inkwell::values::IntValue<'ctx>, 31 | ) -> anyhow::Result> { 32 | Ok(context 33 | .builder() 34 | .build_int_sub(operand_1, operand_2, "subtraction_result")? 35 | .as_basic_value_enum()) 36 | } 37 | 38 | /// 39 | /// Translates the arithmetic multiplication. 40 | /// 41 | pub fn multiplication<'ctx>( 42 | context: &mut Context<'ctx>, 43 | operand_1: inkwell::values::IntValue<'ctx>, 44 | operand_2: inkwell::values::IntValue<'ctx>, 45 | ) -> anyhow::Result> { 46 | Ok(context 47 | .builder() 48 | .build_int_mul(operand_1, operand_2, "multiplication_result")? 49 | .as_basic_value_enum()) 50 | } 51 | 52 | /// 53 | /// Translates the arithmetic division. 54 | /// 55 | pub fn division<'ctx>( 56 | context: &mut Context<'ctx>, 57 | operand_1: inkwell::values::IntValue<'ctx>, 58 | operand_2: inkwell::values::IntValue<'ctx>, 59 | ) -> anyhow::Result> { 60 | Ok(context 61 | .build_call( 62 | context.llvm_runtime().div, 63 | &[ 64 | operand_1.as_basic_value_enum(), 65 | operand_2.as_basic_value_enum(), 66 | ], 67 | "add_mod_call", 68 | )? 69 | .expect("Always exists")) 70 | } 71 | 72 | /// 73 | /// Translates the arithmetic remainder. 74 | /// 75 | pub fn remainder<'ctx>( 76 | context: &mut Context<'ctx>, 77 | operand_1: inkwell::values::IntValue<'ctx>, 78 | operand_2: inkwell::values::IntValue<'ctx>, 79 | ) -> anyhow::Result> { 80 | Ok(context 81 | .build_call( 82 | context.llvm_runtime().r#mod, 83 | &[ 84 | operand_1.as_basic_value_enum(), 85 | operand_2.as_basic_value_enum(), 86 | ], 87 | "add_mod_call", 88 | )? 89 | .expect("Always exists")) 90 | } 91 | 92 | /// 93 | /// Translates the signed arithmetic division. 94 | /// 95 | /// Two differences between the EVM and LLVM IR: 96 | /// 1. In case of division by zero, 0 is returned. 97 | /// 2. In case of overflow, the first argument is returned. 98 | /// 99 | pub fn division_signed<'ctx>( 100 | context: &mut Context<'ctx>, 101 | operand_1: inkwell::values::IntValue<'ctx>, 102 | operand_2: inkwell::values::IntValue<'ctx>, 103 | ) -> anyhow::Result> { 104 | Ok(context 105 | .build_call( 106 | context.llvm_runtime().sdiv, 107 | &[ 108 | operand_1.as_basic_value_enum(), 109 | operand_2.as_basic_value_enum(), 110 | ], 111 | "add_mod_call", 112 | )? 113 | .expect("Always exists")) 114 | } 115 | 116 | /// 117 | /// Translates the signed arithmetic remainder. 118 | /// 119 | pub fn remainder_signed<'ctx>( 120 | context: &mut Context<'ctx>, 121 | operand_1: inkwell::values::IntValue<'ctx>, 122 | operand_2: inkwell::values::IntValue<'ctx>, 123 | ) -> anyhow::Result> { 124 | Ok(context 125 | .build_call( 126 | context.llvm_runtime().smod, 127 | &[ 128 | operand_1.as_basic_value_enum(), 129 | operand_2.as_basic_value_enum(), 130 | ], 131 | "add_mod_call", 132 | )? 133 | .expect("Always exists")) 134 | } 135 | -------------------------------------------------------------------------------- /src/eravm/evm/bitwise.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the bitwise operations. 3 | //! 4 | 5 | use inkwell::values::BasicValue; 6 | 7 | use crate::context::IContext; 8 | use crate::eravm::context::Context; 9 | 10 | /// 11 | /// Translates the bitwise OR. 12 | /// 13 | pub fn or<'ctx>( 14 | context: &mut Context<'ctx>, 15 | operand_1: inkwell::values::IntValue<'ctx>, 16 | operand_2: inkwell::values::IntValue<'ctx>, 17 | ) -> anyhow::Result> { 18 | Ok(context 19 | .builder() 20 | .build_or(operand_1, operand_2, "or_result")? 21 | .as_basic_value_enum()) 22 | } 23 | 24 | /// 25 | /// Translates the bitwise XOR. 26 | /// 27 | pub fn xor<'ctx>( 28 | context: &mut Context<'ctx>, 29 | operand_1: inkwell::values::IntValue<'ctx>, 30 | operand_2: inkwell::values::IntValue<'ctx>, 31 | ) -> anyhow::Result> { 32 | Ok(context 33 | .builder() 34 | .build_xor(operand_1, operand_2, "xor_result")? 35 | .as_basic_value_enum()) 36 | } 37 | 38 | /// 39 | /// Translates the bitwise AND. 40 | /// 41 | pub fn and<'ctx>( 42 | context: &mut Context<'ctx>, 43 | operand_1: inkwell::values::IntValue<'ctx>, 44 | operand_2: inkwell::values::IntValue<'ctx>, 45 | ) -> anyhow::Result> { 46 | Ok(context 47 | .builder() 48 | .build_and(operand_1, operand_2, "and_result")? 49 | .as_basic_value_enum()) 50 | } 51 | 52 | /// 53 | /// Translates the bitwise shift left. 54 | /// 55 | pub fn shift_left<'ctx>( 56 | context: &mut Context<'ctx>, 57 | operand_1: inkwell::values::IntValue<'ctx>, 58 | operand_2: inkwell::values::IntValue<'ctx>, 59 | ) -> anyhow::Result> { 60 | Ok(context 61 | .build_call( 62 | context.llvm_runtime().shl, 63 | &[ 64 | operand_1.as_basic_value_enum(), 65 | operand_2.as_basic_value_enum(), 66 | ], 67 | "shl_call", 68 | )? 69 | .expect("Always exists")) 70 | } 71 | 72 | /// 73 | /// Translates the bitwise shift right. 74 | /// 75 | pub fn shift_right<'ctx>( 76 | context: &mut Context<'ctx>, 77 | operand_1: inkwell::values::IntValue<'ctx>, 78 | operand_2: inkwell::values::IntValue<'ctx>, 79 | ) -> anyhow::Result> { 80 | Ok(context 81 | .build_call( 82 | context.llvm_runtime().shr, 83 | &[ 84 | operand_1.as_basic_value_enum(), 85 | operand_2.as_basic_value_enum(), 86 | ], 87 | "shr_call", 88 | )? 89 | .expect("Always exists")) 90 | } 91 | 92 | /// 93 | /// Translates the arithmetic bitwise shift right. 94 | /// 95 | pub fn shift_right_arithmetic<'ctx>( 96 | context: &mut Context<'ctx>, 97 | operand_1: inkwell::values::IntValue<'ctx>, 98 | operand_2: inkwell::values::IntValue<'ctx>, 99 | ) -> anyhow::Result> { 100 | Ok(context 101 | .build_call( 102 | context.llvm_runtime().sar, 103 | &[ 104 | operand_1.as_basic_value_enum(), 105 | operand_2.as_basic_value_enum(), 106 | ], 107 | "sar_call", 108 | )? 109 | .expect("Always exists")) 110 | } 111 | 112 | /// 113 | /// Translates the `byte` instruction. 114 | /// 115 | pub fn byte<'ctx>( 116 | context: &mut Context<'ctx>, 117 | operand_1: inkwell::values::IntValue<'ctx>, 118 | operand_2: inkwell::values::IntValue<'ctx>, 119 | ) -> anyhow::Result> { 120 | Ok(context 121 | .build_call( 122 | context.llvm_runtime().byte, 123 | &[ 124 | operand_1.as_basic_value_enum(), 125 | operand_2.as_basic_value_enum(), 126 | ], 127 | "byte_call", 128 | )? 129 | .expect("Always exists")) 130 | } 131 | -------------------------------------------------------------------------------- /src/eravm/evm/calldata.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the calldata instructions. 3 | //! 4 | 5 | use inkwell::types::BasicType; 6 | 7 | use crate::context::pointer::Pointer; 8 | use crate::context::IContext; 9 | use crate::eravm::context::address_space::AddressSpace; 10 | use crate::eravm::context::Context; 11 | 12 | /// 13 | /// Translates the calldata load. 14 | /// 15 | pub fn load<'ctx>( 16 | context: &mut Context<'ctx>, 17 | offset: inkwell::values::IntValue<'ctx>, 18 | ) -> anyhow::Result> { 19 | let calldata_pointer_global = context.get_global(crate::eravm::GLOBAL_CALLDATA_POINTER)?; 20 | let calldata_pointer_pointer = calldata_pointer_global.into(); 21 | let calldata_pointer = context.build_load(calldata_pointer_pointer, "calldata_pointer")?; 22 | let calldata_pointer = context.build_gep( 23 | Pointer::new( 24 | context.byte_type(), 25 | calldata_pointer_pointer.address_space, 26 | calldata_pointer.into_pointer_value(), 27 | ), 28 | &[offset], 29 | context.field_type().as_basic_type_enum(), 30 | "calldata_pointer_with_offset", 31 | )?; 32 | let value = context.build_load(calldata_pointer, "calldata_value")?; 33 | Ok(value) 34 | } 35 | 36 | /// 37 | /// Translates the calldata size. 38 | /// 39 | pub fn size<'ctx>( 40 | context: &mut Context<'ctx>, 41 | ) -> anyhow::Result> { 42 | let value = context.get_global_value(crate::eravm::GLOBAL_CALLDATA_SIZE)?; 43 | 44 | Ok(value) 45 | } 46 | 47 | /// 48 | /// Translates the calldata copy. 49 | /// 50 | pub fn copy<'ctx>( 51 | context: &mut Context<'ctx>, 52 | destination_offset: inkwell::values::IntValue<'ctx>, 53 | source_offset: inkwell::values::IntValue<'ctx>, 54 | size: inkwell::values::IntValue<'ctx>, 55 | ) -> anyhow::Result<()> { 56 | let destination = Pointer::new_with_offset( 57 | context, 58 | AddressSpace::Heap, 59 | context.byte_type(), 60 | destination_offset, 61 | "calldata_copy_destination_pointer", 62 | )?; 63 | 64 | let calldata_pointer_global = context.get_global(crate::eravm::GLOBAL_CALLDATA_POINTER)?; 65 | let calldata_pointer_pointer = calldata_pointer_global.into(); 66 | let calldata_pointer = context.build_load(calldata_pointer_pointer, "calldata_pointer")?; 67 | let source = context.build_gep( 68 | Pointer::new( 69 | context.byte_type(), 70 | calldata_pointer_pointer.address_space, 71 | calldata_pointer.into_pointer_value(), 72 | ), 73 | &[source_offset], 74 | context.byte_type().as_basic_type_enum(), 75 | "calldata_source_pointer", 76 | )?; 77 | 78 | context.build_memcpy( 79 | context.intrinsics().memory_copy_from_generic, 80 | destination, 81 | source, 82 | size, 83 | "calldata_copy_memcpy_from_child", 84 | )?; 85 | Ok(()) 86 | } 87 | -------------------------------------------------------------------------------- /src/eravm/evm/comparison.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the comparison operations. 3 | //! 4 | 5 | use inkwell::values::BasicValue; 6 | 7 | use crate::context::IContext; 8 | use crate::eravm::context::Context; 9 | 10 | /// 11 | /// Translates the comparison operations. 12 | /// 13 | /// There is not difference between the EVM and LLVM IR behaviors. 14 | /// 15 | pub fn compare<'ctx>( 16 | context: &mut Context<'ctx>, 17 | operand_1: inkwell::values::IntValue<'ctx>, 18 | operand_2: inkwell::values::IntValue<'ctx>, 19 | operation: inkwell::IntPredicate, 20 | ) -> anyhow::Result> { 21 | let result = context.builder().build_int_compare( 22 | operation, 23 | operand_1, 24 | operand_2, 25 | "comparison_result", 26 | )?; 27 | let result = context.builder().build_int_z_extend_or_bit_cast( 28 | result, 29 | context.field_type(), 30 | "comparison_result_extended", 31 | )?; 32 | Ok(result.as_basic_value_enum()) 33 | } 34 | -------------------------------------------------------------------------------- /src/eravm/evm/context.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the context getter instructions. 3 | //! 4 | 5 | use inkwell::values::BasicValue; 6 | 7 | use crate::context::IContext; 8 | use crate::eravm::context::Context; 9 | 10 | /// 11 | /// Translates the `gas_limit` instruction. 12 | /// 13 | pub fn gas_limit<'ctx>( 14 | context: &mut Context<'ctx>, 15 | ) -> anyhow::Result> { 16 | crate::eravm::evm::call::request( 17 | context, 18 | context.field_const(zkevm_opcode_defs::ADDRESS_SYSTEM_CONTEXT.into()), 19 | "blockGasLimit()", 20 | vec![], 21 | ) 22 | } 23 | 24 | /// 25 | /// Translates the `gas_price` instruction. 26 | /// 27 | pub fn gas_price<'ctx>( 28 | context: &mut Context<'ctx>, 29 | ) -> anyhow::Result> { 30 | crate::eravm::evm::call::request( 31 | context, 32 | context.field_const(zkevm_opcode_defs::ADDRESS_SYSTEM_CONTEXT.into()), 33 | "gasPrice()", 34 | vec![], 35 | ) 36 | } 37 | 38 | /// 39 | /// Translates the `tx.origin` instruction. 40 | /// 41 | pub fn origin<'ctx>( 42 | context: &mut Context<'ctx>, 43 | ) -> anyhow::Result> { 44 | crate::eravm::evm::call::request( 45 | context, 46 | context.field_const(zkevm_opcode_defs::ADDRESS_SYSTEM_CONTEXT.into()), 47 | "origin()", 48 | vec![], 49 | ) 50 | } 51 | 52 | /// 53 | /// Translates the `chain_id` instruction. 54 | /// 55 | pub fn chain_id<'ctx>( 56 | context: &mut Context<'ctx>, 57 | ) -> anyhow::Result> { 58 | crate::eravm::evm::call::request( 59 | context, 60 | context.field_const(zkevm_opcode_defs::ADDRESS_SYSTEM_CONTEXT.into()), 61 | "chainId()", 62 | vec![], 63 | ) 64 | } 65 | 66 | /// 67 | /// Translates the `block_number` instruction. 68 | /// 69 | pub fn block_number<'ctx>( 70 | context: &mut Context<'ctx>, 71 | ) -> anyhow::Result> { 72 | crate::eravm::evm::call::request( 73 | context, 74 | context.field_const(zkevm_opcode_defs::ADDRESS_SYSTEM_CONTEXT.into()), 75 | "getBlockNumber()", 76 | vec![], 77 | ) 78 | } 79 | 80 | /// 81 | /// Translates the `block_timestamp` instruction. 82 | /// 83 | pub fn block_timestamp<'ctx>( 84 | context: &mut Context<'ctx>, 85 | ) -> anyhow::Result> { 86 | crate::eravm::evm::call::request( 87 | context, 88 | context.field_const(zkevm_opcode_defs::ADDRESS_SYSTEM_CONTEXT.into()), 89 | "getBlockTimestamp()", 90 | vec![], 91 | ) 92 | } 93 | 94 | /// 95 | /// Translates the `block_hash` instruction. 96 | /// 97 | pub fn block_hash<'ctx>( 98 | context: &mut Context<'ctx>, 99 | index: inkwell::values::IntValue<'ctx>, 100 | ) -> anyhow::Result> { 101 | crate::eravm::evm::call::request( 102 | context, 103 | context.field_const(zkevm_opcode_defs::ADDRESS_SYSTEM_CONTEXT.into()), 104 | "getBlockHashEVM(uint256)", 105 | vec![index], 106 | ) 107 | } 108 | 109 | /// 110 | /// Translates the `difficulty` instruction. 111 | /// 112 | pub fn difficulty<'ctx>( 113 | context: &mut Context<'ctx>, 114 | ) -> anyhow::Result> { 115 | crate::eravm::evm::call::request( 116 | context, 117 | context.field_const(zkevm_opcode_defs::ADDRESS_SYSTEM_CONTEXT.into()), 118 | "difficulty()", 119 | vec![], 120 | ) 121 | } 122 | 123 | /// 124 | /// Translates the `coinbase` instruction. 125 | /// 126 | pub fn coinbase<'ctx>( 127 | context: &mut Context<'ctx>, 128 | ) -> anyhow::Result> { 129 | crate::eravm::evm::call::request( 130 | context, 131 | context.field_const(zkevm_opcode_defs::ADDRESS_SYSTEM_CONTEXT.into()), 132 | "coinbase()", 133 | vec![], 134 | ) 135 | } 136 | 137 | /// 138 | /// Translates the `basefee` instruction. 139 | /// 140 | pub fn basefee<'ctx>( 141 | context: &mut Context<'ctx>, 142 | ) -> anyhow::Result> { 143 | crate::eravm::evm::call::request( 144 | context, 145 | context.field_const(zkevm_opcode_defs::ADDRESS_SYSTEM_CONTEXT.into()), 146 | "baseFee()", 147 | vec![], 148 | ) 149 | } 150 | 151 | /// 152 | /// Translates the `msize` instruction. 153 | /// 154 | pub fn msize<'ctx>( 155 | context: &mut Context<'ctx>, 156 | ) -> anyhow::Result> { 157 | let meta = context 158 | .build_call(context.intrinsics().meta, &[], "msize_meta")? 159 | .expect("Always exists"); 160 | let meta_shifted = context.builder().build_right_shift( 161 | meta.into_int_value(), 162 | context.field_const(era_compiler_common::BIT_LENGTH_X64 as u64), 163 | false, 164 | "msize_meta_shifted", 165 | )?; 166 | let result = 167 | context 168 | .builder() 169 | .build_and(meta_shifted, context.field_const(u32::MAX as u64), "msize")?; 170 | Ok(result.as_basic_value_enum()) 171 | } 172 | -------------------------------------------------------------------------------- /src/eravm/evm/crypto.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the cryptographic operations. 3 | //! 4 | 5 | use inkwell::values::BasicValue; 6 | 7 | use crate::context::IContext; 8 | use crate::eravm::context::address_space::AddressSpace; 9 | use crate::eravm::context::function::Function as EraVMFunction; 10 | use crate::eravm::context::Context; 11 | 12 | /// 13 | /// Translates the `sha3` instruction. 14 | /// 15 | pub fn sha3<'ctx>( 16 | context: &mut Context<'ctx>, 17 | offset: inkwell::values::IntValue<'ctx>, 18 | length: inkwell::values::IntValue<'ctx>, 19 | ) -> anyhow::Result> { 20 | let offset_pointer = context.builder().build_int_to_ptr( 21 | offset, 22 | context.ptr_type(AddressSpace::Heap.into()), 23 | "sha3_offset_pointer", 24 | )?; 25 | 26 | Ok(context 27 | .build_invoke( 28 | context.llvm_runtime().sha3, 29 | &[ 30 | offset_pointer.as_basic_value_enum(), 31 | length.as_basic_value_enum(), 32 | context 33 | .bool_const( 34 | context 35 | .get_function(EraVMFunction::ZKSYNC_NEAR_CALL_ABI_EXCEPTION_HANDLER) 36 | .is_some(), 37 | ) 38 | .as_basic_value_enum(), 39 | ], 40 | "sha3_call", 41 | )? 42 | .expect("Always exists")) 43 | } 44 | -------------------------------------------------------------------------------- /src/eravm/evm/ether_gas.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the value and balance operations. 3 | //! 4 | 5 | use crate::context::IContext; 6 | use crate::eravm::context::Context; 7 | 8 | /// 9 | /// Translates the `gas` instruction. 10 | /// 11 | pub fn gas<'ctx>( 12 | context: &mut Context<'ctx>, 13 | ) -> anyhow::Result> { 14 | Ok(context 15 | .build_call(context.intrinsics().gas_left, &[], "gas_left")? 16 | .expect("Always exists")) 17 | } 18 | 19 | /// 20 | /// Translates the `value` instruction. 21 | /// 22 | pub fn value<'ctx>( 23 | context: &mut Context<'ctx>, 24 | ) -> anyhow::Result> { 25 | Ok(context 26 | .build_call(context.intrinsics().get_u128, &[], "get_u128_value")? 27 | .expect("Always exists")) 28 | } 29 | 30 | /// 31 | /// Translates the `balance` instructions. 32 | /// 33 | pub fn balance<'ctx>( 34 | context: &mut Context<'ctx>, 35 | address: inkwell::values::IntValue<'ctx>, 36 | ) -> anyhow::Result> { 37 | crate::eravm::evm::call::request( 38 | context, 39 | context.field_const(zkevm_opcode_defs::ADDRESS_ETH_TOKEN.into()), 40 | "balanceOf(uint256)", 41 | vec![address], 42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /src/eravm/evm/event.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates a log or event call. 3 | //! 4 | 5 | use inkwell::values::BasicValue; 6 | 7 | use crate::context::IContext; 8 | use crate::eravm::context::address_space::AddressSpace; 9 | use crate::eravm::context::Context; 10 | 11 | /// 12 | /// Translates a log or event call. 13 | /// 14 | /// The decoding logic is implemented in a system contract, which is called from here. 15 | /// 16 | /// There are several cases of the translation for the sake of efficiency, since the front-end 17 | /// emits topics and values sequentially by one, but the LLVM intrinsic and bytecode instruction 18 | /// accept two at once. 19 | /// 20 | pub fn log<'ctx>( 21 | context: &mut Context<'ctx>, 22 | input_offset: inkwell::values::IntValue<'ctx>, 23 | input_length: inkwell::values::IntValue<'ctx>, 24 | topics: Vec>, 25 | ) -> anyhow::Result<()> { 26 | let failure_block = context.append_basic_block("event_failure_block"); 27 | let join_block = context.append_basic_block("event_join_block"); 28 | 29 | let gas = crate::eravm::evm::ether_gas::gas(context)?.into_int_value(); 30 | let abi_data = crate::eravm::utils::abi_data( 31 | context, 32 | input_offset, 33 | input_length, 34 | Some(gas), 35 | AddressSpace::Heap, 36 | true, 37 | )?; 38 | let mut extra_abi_data = Vec::with_capacity(1 + topics.len()); 39 | extra_abi_data.push(context.field_const(topics.len() as u64)); 40 | extra_abi_data.extend(topics); 41 | 42 | let result = context 43 | .build_call( 44 | context.llvm_runtime().far_call, 45 | crate::eravm::utils::external_call_arguments( 46 | context, 47 | abi_data.as_basic_value_enum(), 48 | context.field_const(zkevm_opcode_defs::ADDRESS_EVENT_WRITER as u64), 49 | extra_abi_data, 50 | None, 51 | ) 52 | .as_slice(), 53 | "event_writer_call_external", 54 | )? 55 | .expect("Always returns a value"); 56 | 57 | let result_status_code_boolean = context.builder().build_extract_value( 58 | result.into_struct_value(), 59 | 1, 60 | "event_writer_external_result_status_code_boolean", 61 | )?; 62 | context.build_conditional_branch( 63 | result_status_code_boolean.into_int_value(), 64 | join_block, 65 | failure_block, 66 | )?; 67 | 68 | context.set_basic_block(failure_block); 69 | crate::eravm::evm::r#return::revert(context, context.field_const(0), context.field_const(0))?; 70 | 71 | context.set_basic_block(join_block); 72 | Ok(()) 73 | } 74 | -------------------------------------------------------------------------------- /src/eravm/evm/ext_code.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the external code operations. 3 | //! 4 | 5 | use crate::context::IContext; 6 | use crate::eravm::context::Context; 7 | 8 | /// 9 | /// Translates the `extcodesize` instruction. 10 | /// 11 | pub fn size<'ctx>( 12 | context: &mut Context<'ctx>, 13 | address: inkwell::values::IntValue<'ctx>, 14 | ) -> anyhow::Result> { 15 | crate::eravm::evm::call::request( 16 | context, 17 | context.field_const(zkevm_opcode_defs::ADDRESS_ACCOUNT_CODE_STORAGE.into()), 18 | "getCodeSize(uint256)", 19 | vec![address], 20 | ) 21 | } 22 | 23 | /// 24 | /// Translates the `extcodehash` instruction. 25 | /// 26 | pub fn hash<'ctx>( 27 | context: &mut Context<'ctx>, 28 | address: inkwell::values::IntValue<'ctx>, 29 | ) -> anyhow::Result> { 30 | crate::eravm::evm::call::request( 31 | context, 32 | context.field_const(zkevm_opcode_defs::ADDRESS_ACCOUNT_CODE_STORAGE.into()), 33 | "getCodeHash(uint256)", 34 | vec![address], 35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /src/eravm/evm/immutable.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the contract immutable operations. 3 | //! 4 | 5 | use crate::context::pointer::Pointer; 6 | use crate::context::IContext; 7 | use crate::eravm::context::address_space::AddressSpace; 8 | use crate::eravm::context::Context; 9 | 10 | /// 11 | /// Translates the contract immutable load. 12 | /// 13 | /// In the deploy code the values are read from the auxiliary heap. 14 | /// In the runtime code they are requested from the system contract. 15 | /// 16 | pub fn load<'ctx>( 17 | context: &mut Context<'ctx>, 18 | index: inkwell::values::IntValue<'ctx>, 19 | ) -> anyhow::Result> { 20 | match context.code_segment() { 21 | None => { 22 | panic!("Contract code segment is undefined"); 23 | } 24 | Some(era_compiler_common::CodeSegment::Deploy) => { 25 | let index_double = context.builder().build_int_mul( 26 | index, 27 | context.field_const(2), 28 | "immutable_load_index_double", 29 | )?; 30 | let offset_absolute = context.builder().build_int_add( 31 | index_double, 32 | context.field_const( 33 | crate::eravm::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA 34 | + (3 * era_compiler_common::BYTE_LENGTH_FIELD) as u64, 35 | ), 36 | "immutable_offset_absolute", 37 | )?; 38 | let immutable_pointer = Pointer::new_with_offset( 39 | context, 40 | AddressSpace::HeapAuxiliary, 41 | context.field_type(), 42 | offset_absolute, 43 | "immutable_pointer", 44 | )?; 45 | let immutable_value = context.build_load(immutable_pointer, "immutable_value")?; 46 | Ok(immutable_value) 47 | } 48 | Some(era_compiler_common::CodeSegment::Runtime) => { 49 | let code_address = context 50 | .build_call( 51 | context.intrinsics().code_source, 52 | &[], 53 | "immutable_code_address", 54 | )? 55 | .expect("Always exists") 56 | .into_int_value(); 57 | crate::eravm::evm::call::request( 58 | context, 59 | context.field_const(zkevm_opcode_defs::ADDRESS_IMMUTABLE_SIMULATOR.into()), 60 | "getImmutable(address,uint256)", 61 | vec![code_address, index], 62 | ) 63 | } 64 | } 65 | } 66 | 67 | /// 68 | /// Translates the contract immutable store. 69 | /// 70 | /// In the deploy code the values are written to the auxiliary heap at the predefined offset, 71 | /// being prepared for returning to the system contract for saving. 72 | /// 73 | /// Ignored in the runtime code. 74 | /// 75 | pub fn store<'ctx>( 76 | context: &mut Context<'ctx>, 77 | index: inkwell::values::IntValue<'ctx>, 78 | value: inkwell::values::IntValue<'ctx>, 79 | ) -> anyhow::Result<()> { 80 | match context.code_segment() { 81 | None => { 82 | anyhow::bail!("code segment is undefined"); 83 | } 84 | Some(era_compiler_common::CodeSegment::Deploy) => { 85 | let index_double = context.builder().build_int_mul( 86 | index, 87 | context.field_const(2), 88 | "immutable_load_index_double", 89 | )?; 90 | let index_offset_absolute = context.builder().build_int_add( 91 | index_double, 92 | context.field_const( 93 | crate::eravm::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA 94 | + (2 * era_compiler_common::BYTE_LENGTH_FIELD) as u64, 95 | ), 96 | "index_offset_absolute", 97 | )?; 98 | let index_offset_pointer = Pointer::new_with_offset( 99 | context, 100 | AddressSpace::HeapAuxiliary, 101 | context.field_type(), 102 | index_offset_absolute, 103 | "immutable_index_pointer", 104 | )?; 105 | context.build_store(index_offset_pointer, index)?; 106 | 107 | let value_offset_absolute = context.builder().build_int_add( 108 | index_offset_absolute, 109 | context.field_const(era_compiler_common::BYTE_LENGTH_FIELD as u64), 110 | "value_offset_absolute", 111 | )?; 112 | let value_offset_pointer = Pointer::new_with_offset( 113 | context, 114 | AddressSpace::HeapAuxiliary, 115 | context.field_type(), 116 | value_offset_absolute, 117 | "immutable_value_pointer", 118 | )?; 119 | context.build_store(value_offset_pointer, value)?; 120 | 121 | Ok(()) 122 | } 123 | Some(era_compiler_common::CodeSegment::Runtime) => { 124 | anyhow::bail!("immutable writes are not available in runtime code"); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/eravm/evm/math.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the mathematical operations. 3 | //! 4 | 5 | use inkwell::values::BasicValue; 6 | 7 | use crate::context::IContext; 8 | use crate::eravm::context::Context; 9 | 10 | /// 11 | /// Translates the `addmod` instruction. 12 | /// 13 | pub fn add_mod<'ctx>( 14 | context: &mut Context<'ctx>, 15 | operand_1: inkwell::values::IntValue<'ctx>, 16 | operand_2: inkwell::values::IntValue<'ctx>, 17 | modulo: inkwell::values::IntValue<'ctx>, 18 | ) -> anyhow::Result> { 19 | Ok(context 20 | .build_call( 21 | context.llvm_runtime().add_mod, 22 | &[ 23 | operand_1.as_basic_value_enum(), 24 | operand_2.as_basic_value_enum(), 25 | modulo.as_basic_value_enum(), 26 | ], 27 | "add_mod_call", 28 | )? 29 | .expect("Always exists")) 30 | } 31 | 32 | /// 33 | /// Translates the `mulmod` instruction. 34 | /// 35 | pub fn mul_mod<'ctx>( 36 | context: &mut Context<'ctx>, 37 | operand_1: inkwell::values::IntValue<'ctx>, 38 | operand_2: inkwell::values::IntValue<'ctx>, 39 | modulo: inkwell::values::IntValue<'ctx>, 40 | ) -> anyhow::Result> { 41 | Ok(context 42 | .build_call( 43 | context.llvm_runtime().mul_mod, 44 | &[ 45 | operand_1.as_basic_value_enum(), 46 | operand_2.as_basic_value_enum(), 47 | modulo.as_basic_value_enum(), 48 | ], 49 | "mul_mod_call", 50 | )? 51 | .expect("Always exists")) 52 | } 53 | 54 | /// 55 | /// Translates the `exp` instruction. 56 | /// 57 | pub fn exponent<'ctx>( 58 | context: &mut Context<'ctx>, 59 | value: inkwell::values::IntValue<'ctx>, 60 | exponent: inkwell::values::IntValue<'ctx>, 61 | ) -> anyhow::Result> { 62 | Ok(context 63 | .build_call( 64 | context.llvm_runtime().exp, 65 | &[value.as_basic_value_enum(), exponent.as_basic_value_enum()], 66 | "exp_call", 67 | )? 68 | .expect("Always exists")) 69 | } 70 | 71 | /// 72 | /// Translates the `signextend` instruction. 73 | /// 74 | pub fn sign_extend<'ctx>( 75 | context: &mut Context<'ctx>, 76 | bytes: inkwell::values::IntValue<'ctx>, 77 | value: inkwell::values::IntValue<'ctx>, 78 | ) -> anyhow::Result> { 79 | Ok(context 80 | .build_call( 81 | context.llvm_runtime().sign_extend, 82 | &[bytes.as_basic_value_enum(), value.as_basic_value_enum()], 83 | "sign_extend_call", 84 | )? 85 | .expect("Always exists")) 86 | } 87 | -------------------------------------------------------------------------------- /src/eravm/evm/memory.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the heap memory operations. 3 | //! 4 | 5 | use inkwell::values::BasicValue; 6 | 7 | use crate::context::pointer::Pointer; 8 | use crate::context::IContext; 9 | use crate::eravm::context::address_space::AddressSpace; 10 | use crate::eravm::context::Context; 11 | 12 | /// 13 | /// Translates the `mload` instruction. 14 | /// 15 | /// Uses the main heap. 16 | /// 17 | pub fn load<'ctx>( 18 | context: &mut Context<'ctx>, 19 | offset: inkwell::values::IntValue<'ctx>, 20 | ) -> anyhow::Result> { 21 | let pointer = Pointer::new_with_offset( 22 | context, 23 | AddressSpace::Heap, 24 | context.field_type(), 25 | offset, 26 | "memory_load_pointer", 27 | )?; 28 | let result = context.build_load(pointer, "memory_load_result")?; 29 | Ok(result) 30 | } 31 | 32 | /// 33 | /// Translates the `mstore` instruction. 34 | /// 35 | /// Uses the main heap. 36 | /// 37 | pub fn store<'ctx>( 38 | context: &mut Context<'ctx>, 39 | offset: inkwell::values::IntValue<'ctx>, 40 | value: inkwell::values::IntValue<'ctx>, 41 | ) -> anyhow::Result<()> { 42 | let pointer = Pointer::new_with_offset( 43 | context, 44 | AddressSpace::Heap, 45 | context.field_type(), 46 | offset, 47 | "memory_store_pointer", 48 | )?; 49 | context.build_store(pointer, value)?; 50 | Ok(()) 51 | } 52 | 53 | /// 54 | /// Translates the `mstore8` instruction. 55 | /// 56 | /// Uses the main heap. 57 | /// 58 | pub fn store_byte<'ctx>( 59 | context: &mut Context<'ctx>, 60 | offset: inkwell::values::IntValue<'ctx>, 61 | value: inkwell::values::IntValue<'ctx>, 62 | ) -> anyhow::Result<()> { 63 | let offset_pointer = Pointer::new_with_offset( 64 | context, 65 | AddressSpace::Heap, 66 | context.byte_type(), 67 | offset, 68 | "mstore8_offset_pointer", 69 | )?; 70 | context.build_call( 71 | context.llvm_runtime().mstore8, 72 | &[ 73 | offset_pointer.value.as_basic_value_enum(), 74 | value.as_basic_value_enum(), 75 | ], 76 | "mstore8_call", 77 | )?; 78 | Ok(()) 79 | } 80 | -------------------------------------------------------------------------------- /src/eravm/evm/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The EVM instructions translation utils. 3 | //! 4 | 5 | pub mod arithmetic; 6 | pub mod bitwise; 7 | pub mod call; 8 | pub mod calldata; 9 | pub mod comparison; 10 | pub mod context; 11 | pub mod create; 12 | pub mod crypto; 13 | pub mod ether_gas; 14 | pub mod event; 15 | pub mod ext_code; 16 | pub mod immutable; 17 | pub mod math; 18 | pub mod memory; 19 | pub mod r#return; 20 | pub mod return_data; 21 | pub mod storage; 22 | -------------------------------------------------------------------------------- /src/eravm/evm/return.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the transaction return operations. 3 | //! 4 | 5 | use crate::context::pointer::Pointer; 6 | use crate::context::IContext; 7 | use crate::eravm::context::address_space::AddressSpace; 8 | use crate::eravm::context::Context; 9 | 10 | /// 11 | /// Translates the `return` instruction. 12 | /// 13 | /// Unlike in EVM, ZKsync constructors return the array of contract immutables. 14 | /// 15 | pub fn r#return<'ctx>( 16 | context: &mut Context<'ctx>, 17 | offset: inkwell::values::IntValue<'ctx>, 18 | length: inkwell::values::IntValue<'ctx>, 19 | ) -> anyhow::Result<()> { 20 | match context.code_segment() { 21 | None => { 22 | anyhow::bail!("Contract code segment is undefined"); 23 | } 24 | Some(era_compiler_common::CodeSegment::Deploy) => { 25 | let immutables_offset_pointer = Pointer::new_with_offset( 26 | context, 27 | AddressSpace::HeapAuxiliary, 28 | context.field_type(), 29 | context.field_const(crate::eravm::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA), 30 | "immutables_offset_pointer", 31 | )?; 32 | context.build_store( 33 | immutables_offset_pointer, 34 | context.field_const(era_compiler_common::BYTE_LENGTH_FIELD as u64), 35 | )?; 36 | 37 | let immutables_number_pointer = Pointer::new_with_offset( 38 | context, 39 | AddressSpace::HeapAuxiliary, 40 | context.field_type(), 41 | context.field_const( 42 | crate::eravm::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA 43 | + (era_compiler_common::BYTE_LENGTH_FIELD as u64), 44 | ), 45 | "immutables_number_pointer", 46 | )?; 47 | let immutable_values_size = context.immutables_size(); 48 | context.build_store( 49 | immutables_number_pointer, 50 | context.field_const( 51 | (immutable_values_size / era_compiler_common::BYTE_LENGTH_FIELD) as u64, 52 | ), 53 | )?; 54 | let immutables_size = context.builder().build_int_mul( 55 | context.field_const(immutable_values_size as u64), 56 | context.field_const(2), 57 | "immutables_size", 58 | )?; 59 | let return_data_length = context.builder().build_int_add( 60 | immutables_size, 61 | context.field_const((era_compiler_common::BYTE_LENGTH_FIELD * 2) as u64), 62 | "return_data_length", 63 | )?; 64 | 65 | context.build_exit( 66 | context.llvm_runtime().r#return, 67 | context.field_const(crate::eravm::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA), 68 | return_data_length, 69 | )?; 70 | } 71 | Some(era_compiler_common::CodeSegment::Runtime) => { 72 | context.build_exit(context.llvm_runtime().r#return, offset, length)?; 73 | } 74 | } 75 | 76 | Ok(()) 77 | } 78 | 79 | /// 80 | /// Translates the `revert` instruction. 81 | /// 82 | pub fn revert<'ctx>( 83 | context: &mut Context<'ctx>, 84 | offset: inkwell::values::IntValue<'ctx>, 85 | length: inkwell::values::IntValue<'ctx>, 86 | ) -> anyhow::Result<()> { 87 | context.build_exit(context.llvm_runtime().revert, offset, length)?; 88 | Ok(()) 89 | } 90 | 91 | /// 92 | /// Translates the `stop` instruction. 93 | /// 94 | /// Is the same as `return(0, 0)`. 95 | /// 96 | pub fn stop(context: &mut Context) -> anyhow::Result<()> { 97 | r#return(context, context.field_const(0), context.field_const(0)) 98 | } 99 | 100 | /// 101 | /// Translates the `invalid` instruction. 102 | /// 103 | /// Burns all gas using an out-of-bounds memory store, causing a panic. 104 | /// 105 | pub fn invalid(context: &mut Context) -> anyhow::Result<()> { 106 | crate::eravm::evm::memory::store( 107 | context, 108 | context.field_type().const_all_ones(), 109 | context.field_const(0), 110 | )?; 111 | context.build_call(context.intrinsics().trap, &[], "invalid_trap")?; 112 | context.build_unreachable()?; 113 | Ok(()) 114 | } 115 | -------------------------------------------------------------------------------- /src/eravm/evm/return_data.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the return data instructions. 3 | //! 4 | 5 | use inkwell::types::BasicType; 6 | use inkwell::values::BasicValue; 7 | 8 | use crate::context::pointer::Pointer; 9 | use crate::context::IContext; 10 | use crate::eravm::context::address_space::AddressSpace; 11 | use crate::eravm::context::Context; 12 | 13 | /// 14 | /// Translates the return data size. 15 | /// 16 | pub fn size<'ctx>( 17 | context: &mut Context<'ctx>, 18 | ) -> anyhow::Result> { 19 | match context.get_global_value(crate::eravm::GLOBAL_RETURN_DATA_SIZE) { 20 | Ok(global) => Ok(global), 21 | Err(_error) => Ok(context.field_const(0).as_basic_value_enum()), 22 | } 23 | } 24 | 25 | /// 26 | /// Translates the return data copy. 27 | /// 28 | pub fn copy<'ctx>( 29 | context: &mut Context<'ctx>, 30 | destination_offset: inkwell::values::IntValue<'ctx>, 31 | source_offset: inkwell::values::IntValue<'ctx>, 32 | size: inkwell::values::IntValue<'ctx>, 33 | ) -> anyhow::Result<()> { 34 | let error_block = context.append_basic_block("return_data_copy_error_block"); 35 | let join_block = context.append_basic_block("return_data_copy_join_block"); 36 | 37 | let return_data_size = self::size(context)?.into_int_value(); 38 | let copy_slice_end = 39 | context 40 | .builder() 41 | .build_int_add(source_offset, size, "return_data_copy_slice_end")?; 42 | let is_copy_out_of_bounds = context.builder().build_int_compare( 43 | inkwell::IntPredicate::UGT, 44 | copy_slice_end, 45 | return_data_size, 46 | "return_data_copy_is_out_of_bounds", 47 | )?; 48 | context.build_conditional_branch(is_copy_out_of_bounds, error_block, join_block)?; 49 | 50 | context.set_basic_block(error_block); 51 | crate::eravm::evm::r#return::revert(context, context.field_const(0), context.field_const(0))?; 52 | 53 | context.set_basic_block(join_block); 54 | let destination = Pointer::::new_with_offset( 55 | context, 56 | AddressSpace::Heap, 57 | context.byte_type(), 58 | destination_offset, 59 | "return_data_copy_destination_pointer", 60 | )?; 61 | 62 | let return_data_pointer_global = 63 | context.get_global(crate::eravm::GLOBAL_RETURN_DATA_POINTER)?; 64 | let return_data_pointer_pointer = return_data_pointer_global.into(); 65 | let return_data_pointer = 66 | context.build_load(return_data_pointer_pointer, "return_data_pointer")?; 67 | let source = context.build_gep( 68 | Pointer::::new( 69 | context.byte_type(), 70 | return_data_pointer_pointer.address_space, 71 | return_data_pointer.into_pointer_value(), 72 | ), 73 | &[source_offset], 74 | context.byte_type().as_basic_type_enum(), 75 | "return_data_source_pointer", 76 | )?; 77 | 78 | context.build_memcpy( 79 | context.intrinsics().memory_copy_from_generic, 80 | destination, 81 | source, 82 | size, 83 | "return_data_copy_memcpy_from_return_data", 84 | )?; 85 | Ok(()) 86 | } 87 | -------------------------------------------------------------------------------- /src/eravm/evm/storage.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the storage operations. 3 | //! 4 | 5 | use crate::context::pointer::Pointer; 6 | use crate::context::IContext; 7 | use crate::eravm::context::address_space::AddressSpace; 8 | use crate::eravm::context::Context; 9 | 10 | /// 11 | /// Translates the storage load. 12 | /// 13 | pub fn load<'ctx>( 14 | context: &mut Context<'ctx>, 15 | position: inkwell::values::IntValue<'ctx>, 16 | ) -> anyhow::Result> { 17 | let position_pointer = Pointer::new_with_offset( 18 | context, 19 | AddressSpace::Storage, 20 | context.field_type(), 21 | position, 22 | "storage_load_position_pointer", 23 | )?; 24 | let value = context.build_load(position_pointer, "storage_load_value")?; 25 | Ok(value) 26 | } 27 | 28 | /// 29 | /// Translates the storage store. 30 | /// 31 | pub fn store<'ctx>( 32 | context: &mut Context<'ctx>, 33 | position: inkwell::values::IntValue<'ctx>, 34 | value: inkwell::values::IntValue<'ctx>, 35 | ) -> anyhow::Result<()> { 36 | let position_pointer = Pointer::new_with_offset( 37 | context, 38 | AddressSpace::Storage, 39 | context.field_type(), 40 | position, 41 | "storage_store_position_pointer", 42 | )?; 43 | context.build_store(position_pointer, value)?; 44 | Ok(()) 45 | } 46 | 47 | /// 48 | /// Translates the transient storage load. 49 | /// 50 | pub fn transient_load<'ctx>( 51 | context: &mut Context<'ctx>, 52 | position: inkwell::values::IntValue<'ctx>, 53 | ) -> anyhow::Result> { 54 | let position_pointer = Pointer::new_with_offset( 55 | context, 56 | AddressSpace::TransientStorage, 57 | context.field_type(), 58 | position, 59 | "transient_storage_load_position_pointer", 60 | )?; 61 | let value = context.build_load(position_pointer, "transient_storage_load_value")?; 62 | Ok(value) 63 | } 64 | 65 | /// 66 | /// Translates the transient storage store. 67 | /// 68 | pub fn transient_store<'ctx>( 69 | context: &mut Context<'ctx>, 70 | position: inkwell::values::IntValue<'ctx>, 71 | value: inkwell::values::IntValue<'ctx>, 72 | ) -> anyhow::Result<()> { 73 | let position_pointer = Pointer::new_with_offset( 74 | context, 75 | AddressSpace::TransientStorage, 76 | context.field_type(), 77 | position, 78 | "transient_storage_store_position_pointer", 79 | )?; 80 | context.build_store(position_pointer, value)?; 81 | Ok(()) 82 | } 83 | -------------------------------------------------------------------------------- /src/eravm/extensions/const_array.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the const array instructions of the EraVM Yul extension. 3 | //! 4 | 5 | use inkwell::types::BasicType; 6 | use inkwell::values::BasicValue; 7 | 8 | use crate::context::IContext; 9 | use crate::eravm::context::address_space::AddressSpace; 10 | use crate::eravm::context::Context; 11 | 12 | /// 13 | /// Declares a constant array in the code section. 14 | /// 15 | pub fn declare<'ctx>( 16 | context: &mut Context<'ctx>, 17 | index: u8, 18 | size: u16, 19 | ) -> anyhow::Result> { 20 | context 21 | .yul_mut() 22 | .expect("Always exists") 23 | .const_array_declare(index, size)?; 24 | 25 | Ok(context.field_const(1).as_basic_value_enum()) 26 | } 27 | 28 | /// 29 | /// Sets a value in a constant array in the code section. 30 | /// 31 | pub fn set<'ctx>( 32 | context: &mut Context<'ctx>, 33 | index: u8, 34 | offset: u16, 35 | value: num::BigUint, 36 | ) -> anyhow::Result> { 37 | context 38 | .yul_mut() 39 | .expect("Always exists") 40 | .const_array_set(index, offset, value)?; 41 | 42 | Ok(context.field_const(1).as_basic_value_enum()) 43 | } 44 | 45 | /// 46 | /// Finalizes a constant array in the code section, by extracting it from 47 | /// the temporary compile-time storage, and initializing it in LLVM IR. 48 | /// 49 | pub fn finalize<'ctx>( 50 | context: &mut Context<'ctx>, 51 | index: u8, 52 | ) -> anyhow::Result> { 53 | let const_array = context 54 | .yul_mut() 55 | .expect("Always exists") 56 | .const_array_take(index)?; 57 | let array_type = context.field_type().array_type(const_array.len() as u32); 58 | let array_value = context.field_type().const_array( 59 | const_array 60 | .into_iter() 61 | .map(|value| context.field_const_str_dec(value.to_string().as_str())) 62 | .collect::>>() 63 | .as_slice(), 64 | ); 65 | 66 | context.set_global( 67 | format!( 68 | "{}{:03}", 69 | crate::eravm::r#const::GLOBAL_CONST_ARRAY_PREFIX, 70 | index 71 | ) 72 | .as_str(), 73 | array_type, 74 | AddressSpace::Code, 75 | array_value, 76 | )?; 77 | 78 | Ok(context.field_const(1).as_basic_value_enum()) 79 | } 80 | 81 | /// 82 | /// Gets a value from a constant array in the code section. 83 | /// 84 | pub fn get<'ctx>( 85 | context: &mut Context<'ctx>, 86 | index: u8, 87 | offset: inkwell::values::IntValue<'ctx>, 88 | ) -> anyhow::Result> { 89 | let identifier = format!( 90 | "{}{:03}", 91 | crate::eravm::r#const::GLOBAL_CONST_ARRAY_PREFIX, 92 | index 93 | ); 94 | let global = context.get_global(identifier.as_str())?; 95 | let pointer = global.into(); 96 | 97 | let pointer = context.build_gep( 98 | pointer, 99 | &[context.field_const(0), offset], 100 | context.field_type().as_basic_type_enum(), 101 | format!("{}_pointer", identifier).as_str(), 102 | )?; 103 | let value = context.build_load(pointer, format!("{}_value", identifier).as_str())?; 104 | 105 | Ok(value) 106 | } 107 | -------------------------------------------------------------------------------- /src/eravm/extensions/general.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the general instructions of the EraVM Yul extension. 3 | //! 4 | 5 | use inkwell::values::BasicValue; 6 | 7 | use crate::context::IContext; 8 | use crate::eravm::context::address_space::AddressSpace; 9 | use crate::eravm::context::Context; 10 | 11 | /// 12 | /// Generates a call to L1. 13 | /// 14 | pub fn to_l1<'ctx>( 15 | context: &mut Context<'ctx>, 16 | is_first: inkwell::values::IntValue<'ctx>, 17 | in_0: inkwell::values::IntValue<'ctx>, 18 | in_1: inkwell::values::IntValue<'ctx>, 19 | ) -> anyhow::Result> { 20 | let join_block = context.append_basic_block("contract_call_toL1_join_block"); 21 | 22 | let contract_call_tol1_is_first_block = 23 | context.append_basic_block("contract_call_toL1_is_first_block"); 24 | let contract_call_tol1_is_not_first_block = 25 | context.append_basic_block("contract_call_toL1_is_not_first_block"); 26 | 27 | let is_first_equals_zero = context.builder().build_int_compare( 28 | inkwell::IntPredicate::EQ, 29 | is_first, 30 | context.field_const(0), 31 | "contract_call_toL1_is_first_equals_zero", 32 | )?; 33 | context.build_conditional_branch( 34 | is_first_equals_zero, 35 | contract_call_tol1_is_not_first_block, 36 | contract_call_tol1_is_first_block, 37 | )?; 38 | 39 | { 40 | context.set_basic_block(contract_call_tol1_is_not_first_block); 41 | let is_first = context.field_const(0); 42 | context.build_call( 43 | context.intrinsics().to_l1, 44 | &[ 45 | in_0.as_basic_value_enum(), 46 | in_1.as_basic_value_enum(), 47 | is_first.as_basic_value_enum(), 48 | ], 49 | "contract_call_simulation_tol1", 50 | )?; 51 | context.build_unconditional_branch(join_block)?; 52 | } 53 | 54 | { 55 | context.set_basic_block(contract_call_tol1_is_first_block); 56 | let is_first = context.field_const(1); 57 | context.build_call( 58 | context.intrinsics().to_l1, 59 | &[ 60 | in_0.as_basic_value_enum(), 61 | in_1.as_basic_value_enum(), 62 | is_first.as_basic_value_enum(), 63 | ], 64 | "contract_call_simulation_tol1", 65 | )?; 66 | context.build_unconditional_branch(join_block)?; 67 | } 68 | 69 | context.set_basic_block(join_block); 70 | Ok(context.field_const(1).as_basic_value_enum()) 71 | } 72 | 73 | /// 74 | /// Generates a `code source` call. 75 | /// 76 | pub fn code_source<'ctx>( 77 | context: &mut Context<'ctx>, 78 | ) -> anyhow::Result> { 79 | let result = context 80 | .build_call( 81 | context.intrinsics().code_source, 82 | &[], 83 | "contract_call_simulation_code_source", 84 | )? 85 | .expect("Always exists"); 86 | Ok(result) 87 | } 88 | 89 | /// 90 | /// Generates a precompile call. 91 | /// 92 | pub fn precompile<'ctx>( 93 | context: &mut Context<'ctx>, 94 | in_0: inkwell::values::IntValue<'ctx>, 95 | gas_left: inkwell::values::IntValue<'ctx>, 96 | ) -> anyhow::Result> { 97 | let result = context 98 | .build_call( 99 | context.intrinsics().precompile, 100 | &[in_0.as_basic_value_enum(), gas_left.as_basic_value_enum()], 101 | "contract_call_simulation_precompile", 102 | )? 103 | .expect("Always exists"); 104 | Ok(result) 105 | } 106 | 107 | /// 108 | /// Generates a decommit call. 109 | /// 110 | pub fn decommit<'ctx>( 111 | context: &mut Context<'ctx>, 112 | in_0: inkwell::values::IntValue<'ctx>, 113 | gas_left: inkwell::values::IntValue<'ctx>, 114 | ) -> anyhow::Result> { 115 | let result = context 116 | .build_call( 117 | context.intrinsics().decommit, 118 | &[in_0.as_basic_value_enum(), gas_left.as_basic_value_enum()], 119 | "contract_call_simulation_decommit", 120 | )? 121 | .expect("Always exists"); 122 | context.set_global( 123 | crate::eravm::GLOBAL_DECOMMIT_POINTER, 124 | context.ptr_type(AddressSpace::Generic.into()), 125 | AddressSpace::Stack, 126 | result, 127 | )?; 128 | Ok(result) 129 | } 130 | 131 | /// 132 | /// Generates a `meta` call. 133 | /// 134 | pub fn meta<'ctx>( 135 | context: &mut Context<'ctx>, 136 | ) -> anyhow::Result> { 137 | let result = context 138 | .build_call( 139 | context.intrinsics().meta, 140 | &[], 141 | "contract_call_simulation_meta", 142 | )? 143 | .expect("Always exists"); 144 | Ok(result) 145 | } 146 | 147 | /// 148 | /// Generates a `u128` context value setter call. 149 | /// 150 | pub fn set_context_value<'ctx>( 151 | context: &mut Context<'ctx>, 152 | value: inkwell::values::IntValue<'ctx>, 153 | ) -> anyhow::Result> { 154 | context.build_call( 155 | context.intrinsics().set_u128, 156 | &[value.as_basic_value_enum()], 157 | "contract_call_simulation_set_context_value", 158 | )?; 159 | Ok(context.field_const(1).as_basic_value_enum()) 160 | } 161 | 162 | /// 163 | /// Generates a public data price setter call. 164 | /// 165 | pub fn set_pubdata_price<'ctx>( 166 | context: &mut Context<'ctx>, 167 | value: inkwell::values::IntValue<'ctx>, 168 | ) -> anyhow::Result> { 169 | context.build_call( 170 | context.intrinsics().set_pubdata_price, 171 | &[value.as_basic_value_enum()], 172 | "contract_call_simulation_set_pubdata_price", 173 | )?; 174 | Ok(context.field_const(1).as_basic_value_enum()) 175 | } 176 | 177 | /// 178 | /// Generates a transaction counter increment call. 179 | /// 180 | pub fn increment_tx_counter<'ctx>( 181 | context: &mut Context<'ctx>, 182 | ) -> anyhow::Result> { 183 | context.build_call( 184 | context.intrinsics().increment_tx_counter, 185 | &[], 186 | "contract_call_simulation_increment_tx_counter", 187 | )?; 188 | Ok(context.field_const(1).as_basic_value_enum()) 189 | } 190 | 191 | /// 192 | /// Generates an event call. 193 | /// 194 | pub fn event<'ctx>( 195 | context: &mut Context<'ctx>, 196 | operand_1: inkwell::values::IntValue<'ctx>, 197 | operand_2: inkwell::values::IntValue<'ctx>, 198 | is_initializer: bool, 199 | ) -> anyhow::Result> { 200 | context.build_call( 201 | context.intrinsics().event, 202 | &[ 203 | operand_1.as_basic_value_enum(), 204 | operand_2.as_basic_value_enum(), 205 | context 206 | .field_const(u64::from(is_initializer)) 207 | .as_basic_value_enum(), 208 | ], 209 | if is_initializer { 210 | "event_initialize" 211 | } else { 212 | "event_write" 213 | }, 214 | )?; 215 | Ok(context.field_const(1).as_basic_value_enum()) 216 | } 217 | -------------------------------------------------------------------------------- /src/eravm/extensions/math.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the math instructions of the EraVM Yul extension. 3 | //! 4 | 5 | use inkwell::values::BasicValue; 6 | 7 | use crate::context::IContext; 8 | use crate::eravm::context::Context; 9 | 10 | /// 11 | /// Performs a multiplication, returning the higher register, that is the overflown part. 12 | /// 13 | pub fn multiplication_512<'ctx>( 14 | context: &mut Context<'ctx>, 15 | operand_1: inkwell::values::IntValue<'ctx>, 16 | operand_2: inkwell::values::IntValue<'ctx>, 17 | ) -> anyhow::Result> { 18 | let operand_1_extended = context.builder().build_int_z_extend_or_bit_cast( 19 | operand_1, 20 | context.integer_type(era_compiler_common::BIT_LENGTH_FIELD * 2), 21 | "multiplication_512_operand_1_extended", 22 | )?; 23 | let operand_2_extended = context.builder().build_int_z_extend_or_bit_cast( 24 | operand_2, 25 | context.integer_type(era_compiler_common::BIT_LENGTH_FIELD * 2), 26 | "multiplication_512_operand_2_extended", 27 | )?; 28 | let result_extended = context.builder().build_int_mul( 29 | operand_1_extended, 30 | operand_2_extended, 31 | "multiplication_512_result_extended", 32 | )?; 33 | let result_shifted = context.builder().build_right_shift( 34 | result_extended, 35 | context.integer_const( 36 | era_compiler_common::BIT_LENGTH_FIELD * 2, 37 | era_compiler_common::BIT_LENGTH_FIELD as u64, 38 | ), 39 | false, 40 | "multiplication_512_result_shifted", 41 | )?; 42 | let result = context.builder().build_int_truncate_or_bit_cast( 43 | result_shifted, 44 | context.field_type(), 45 | "multiplication_512_result", 46 | )?; 47 | 48 | Ok(result.as_basic_value_enum()) 49 | } 50 | -------------------------------------------------------------------------------- /src/eravm/extensions/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The EraVM instructions translation utils. 3 | //! 4 | 5 | pub mod abi; 6 | pub mod call; 7 | pub mod const_array; 8 | pub mod general; 9 | pub mod math; 10 | -------------------------------------------------------------------------------- /src/eravm/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM EraVM context library. 3 | //! 4 | 5 | pub mod build; 6 | pub mod r#const; 7 | pub mod context; 8 | pub mod evm; 9 | pub mod extensions; 10 | pub mod utils; 11 | 12 | pub use self::r#const::*; 13 | 14 | use std::collections::BTreeMap; 15 | 16 | use crate::debug_config::DebugConfig; 17 | use crate::target_machine::TargetMachine; 18 | 19 | use self::build::Build; 20 | use self::context::Context; 21 | 22 | /// 23 | /// Initializes the EraVM target machine. 24 | /// 25 | pub fn initialize_target() { 26 | inkwell::targets::Target::initialize_eravm(&inkwell::targets::InitializationConfig::default()); 27 | } 28 | 29 | /// 30 | /// Translates `assembly_text` to an object code. 31 | /// 32 | pub fn assemble( 33 | target_machine: &TargetMachine, 34 | contract_path: &str, 35 | assembly_text: &str, 36 | debug_config: Option<&DebugConfig>, 37 | ) -> anyhow::Result { 38 | if let Some(debug_config) = debug_config { 39 | debug_config.dump_assembly(contract_path, assembly_text, false)?; 40 | } 41 | 42 | let assembly_buffer = inkwell::memory_buffer::MemoryBuffer::create_from_memory_range( 43 | assembly_text.as_bytes(), 44 | "assembly_buffer", 45 | false, 46 | ); 47 | 48 | let bytecode_buffer = target_machine 49 | .assemble(&assembly_buffer) 50 | .map_err(|error| anyhow::anyhow!("assembling: {error}"))?; 51 | Ok(bytecode_buffer) 52 | } 53 | 54 | /// 55 | /// Disassembles `bytecode`, returning textual representation. 56 | /// 57 | pub fn disassemble( 58 | target_machine: &TargetMachine, 59 | bytecode_buffer: &inkwell::memory_buffer::MemoryBuffer, 60 | ) -> anyhow::Result { 61 | let disassembly_buffer = target_machine 62 | .disassemble(bytecode_buffer, 0, DISASSEMBLER_DEFAULT_MODE) 63 | .map_err(|error| anyhow::anyhow!("disassembling: {error}"))?; 64 | 65 | let disassembly_text = String::from_utf8_lossy(disassembly_buffer.as_slice()); 66 | Ok(disassembly_text.to_string()) 67 | } 68 | 69 | /// 70 | /// Links `bytecode_buffer` with `linker_symbols` and `factory_dependencies`. 71 | /// 72 | pub fn link( 73 | bytecode_buffer: inkwell::memory_buffer::MemoryBuffer, 74 | linker_symbols: &BTreeMap, 75 | factory_dependencies: &BTreeMap, 76 | ) -> anyhow::Result<( 77 | inkwell::memory_buffer::MemoryBuffer, 78 | era_compiler_common::ObjectFormat, 79 | )> { 80 | if !bytecode_buffer.is_elf_eravm() { 81 | return Ok((bytecode_buffer, era_compiler_common::ObjectFormat::Raw)); 82 | } 83 | 84 | let bytecode_buffer_linked = bytecode_buffer 85 | .link_eravm(linker_symbols, factory_dependencies) 86 | .map_err(|error| anyhow::anyhow!("linking: {error}"))?; 87 | let object_format = if bytecode_buffer_linked.is_elf_eravm() { 88 | era_compiler_common::ObjectFormat::ELF 89 | } else { 90 | era_compiler_common::ObjectFormat::Raw 91 | }; 92 | Ok((bytecode_buffer_linked, object_format)) 93 | } 94 | 95 | /// 96 | /// Computes the EraVM bytecode hash. 97 | /// 98 | /// # Panics 99 | /// If `bytecode_buffer` is an ELF object. 100 | /// 101 | /// # Errors 102 | /// If the bytecode size is not an odd number of 32-byte words. 103 | /// 104 | pub fn hash( 105 | bytecode_buffer: &inkwell::memory_buffer::MemoryBuffer, 106 | ) -> anyhow::Result<[u8; era_compiler_common::BYTE_LENGTH_FIELD]> { 107 | assert!( 108 | !bytecode_buffer.is_elf_eravm(), 109 | "bytecode is still an unlinked ELF object" 110 | ); 111 | 112 | let bytecode_words: Vec<[u8; era_compiler_common::BYTE_LENGTH_FIELD]> = bytecode_buffer 113 | .as_slice() 114 | .chunks(era_compiler_common::BYTE_LENGTH_FIELD) 115 | .map(|word| word.try_into().expect("Always valid")) 116 | .collect(); 117 | let bytecode_hash = zkevm_opcode_defs::utils::bytecode_to_code_hash_for_mode::< 118 | { era_compiler_common::BYTE_LENGTH_X64 }, 119 | zkevm_opcode_defs::decoding::EncodingModeProduction, 120 | >(bytecode_words.as_slice()) 121 | .map_err(|_| anyhow::anyhow!("bytecode hashing error"))?; 122 | Ok(bytecode_hash) 123 | } 124 | 125 | /// 126 | /// Converts `bytecode_buffer` and auxiliary data into a build. 127 | /// 128 | pub fn build( 129 | bytecode_buffer: inkwell::memory_buffer::MemoryBuffer, 130 | metadata_hash: Option, 131 | cbor_data: Option<(String, Vec<(String, semver::Version)>)>, 132 | assembly_text: Option, 133 | ) -> anyhow::Result { 134 | let metadata = match (metadata_hash, cbor_data) { 135 | (Some(era_compiler_common::Hash::IPFS(hash)), Some((cbor_key, cbor_data))) => { 136 | let cbor = era_compiler_common::CBOR::new( 137 | Some(( 138 | era_compiler_common::EraVMMetadataHashType::IPFS, 139 | hash.as_bytes(), 140 | )), 141 | cbor_key, 142 | cbor_data, 143 | ); 144 | cbor.to_vec() 145 | } 146 | (None, Some((cbor_key, cbor_data))) => { 147 | let cbor = era_compiler_common::CBOR::<'_, String>::new(None, cbor_key, cbor_data); 148 | cbor.to_vec() 149 | } 150 | (Some(era_compiler_common::Hash::Keccak256(hash)), _) => hash.to_vec(), 151 | (_, None) => vec![], 152 | }; 153 | 154 | let bytecode_buffer_with_metadata = if metadata.is_empty() { 155 | bytecode_buffer 156 | } else { 157 | bytecode_buffer 158 | .append_metadata_eravm(metadata.as_slice()) 159 | .map_err(|error| anyhow::anyhow!("bytecode metadata appending error: {error}"))? 160 | }; 161 | let bytecode = bytecode_buffer_with_metadata.as_slice().to_vec(); 162 | 163 | let build = Build::new(bytecode, metadata, assembly_text); 164 | Ok(build) 165 | } 166 | 167 | /// 168 | /// Implemented by items which are translated into LLVM IR. 169 | /// 170 | pub trait WriteLLVM { 171 | /// 172 | /// Declares the entity in the LLVM IR. 173 | /// Is usually performed in order to use the item before defining it. 174 | /// 175 | fn declare(&mut self, _context: &mut Context) -> anyhow::Result<()> { 176 | Ok(()) 177 | } 178 | 179 | /// 180 | /// Translates the entity into LLVM IR. 181 | /// 182 | fn into_llvm(self, context: &mut Context) -> anyhow::Result<()>; 183 | } 184 | 185 | /// 186 | /// The dummy LLVM writable entity. 187 | /// 188 | #[derive(Debug, Default, Clone)] 189 | pub struct DummyLLVMWritable {} 190 | 191 | impl WriteLLVM for DummyLLVMWritable { 192 | fn into_llvm(self, _context: &mut Context) -> anyhow::Result<()> { 193 | Ok(()) 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/evm/build.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM module build. 3 | //! 4 | 5 | use std::collections::BTreeMap; 6 | use std::collections::BTreeSet; 7 | 8 | use crate::evm::warning::Warning; 9 | 10 | /// 11 | /// The LLVM module build. 12 | /// 13 | #[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)] 14 | pub struct Build { 15 | /// Bytecode. 16 | pub bytecode: Option>, 17 | /// Text assembly. 18 | pub assembly: Option, 19 | /// Mapping with immutables. 20 | pub immutables: Option>>, 21 | /// Warnings produced during compilation. 22 | pub warnings: Vec, 23 | } 24 | 25 | impl Build { 26 | /// 27 | /// A shortcut constructor. 28 | /// 29 | pub fn new( 30 | bytecode: Option>, 31 | assembly: Option, 32 | immutables: Option>>, 33 | warnings: Vec, 34 | ) -> Self { 35 | Self { 36 | bytecode, 37 | assembly, 38 | immutables, 39 | warnings, 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/evm/const.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM context constants. 3 | //! 4 | 5 | /// The deploy bytecode size limit. 6 | pub const DEPLOY_CODE_SIZE_LIMIT: usize = 49152; 7 | 8 | /// The runtime bytecode size limit. 9 | pub const RUNTIME_CODE_SIZE_LIMIT: usize = 24576; 10 | -------------------------------------------------------------------------------- /src/evm/context/address_space.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The address space aliases. 3 | //! 4 | 5 | use crate::context::traits::address_space::IAddressSpace; 6 | 7 | /// 8 | /// The address space aliases. 9 | /// 10 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 11 | pub enum AddressSpace { 12 | /// The stack memory. 13 | Stack, 14 | /// The heap memory. 15 | Heap, 16 | /// The calldata memory. 17 | Calldata, 18 | /// The return data memory. 19 | ReturnData, 20 | /// The code memory. 21 | Code, 22 | /// The storage. 23 | Storage, 24 | /// The transient storage. 25 | TransientStorage, 26 | } 27 | 28 | impl IAddressSpace for AddressSpace { 29 | fn stack() -> Self { 30 | Self::Stack 31 | } 32 | } 33 | 34 | impl From for inkwell::AddressSpace { 35 | fn from(value: AddressSpace) -> Self { 36 | match value { 37 | AddressSpace::Stack => Self::from(0), 38 | AddressSpace::Heap => Self::from(1), 39 | AddressSpace::Calldata => Self::from(2), 40 | AddressSpace::ReturnData => Self::from(3), 41 | AddressSpace::Code => Self::from(4), 42 | AddressSpace::Storage => Self::from(5), 43 | AddressSpace::TransientStorage => Self::from(6), 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/evm/context/evmla_data.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM IR generator EVM legacy assembly data. 3 | //! 4 | 5 | use crate::context::traits::evmla_data::IEVMLAData; 6 | use crate::context::value::Value; 7 | 8 | /// 9 | /// The LLVM IR generator EVM legacy assembly data. 10 | /// 11 | /// Describes some data that is only relevant to the EVM legacy assembly. 12 | /// 13 | #[derive(Debug, Clone)] 14 | pub struct EVMLAData<'ctx> { 15 | /// The Solidity compiler version. 16 | /// Some instruction behave differenly depending on the version. 17 | pub version: semver::Version, 18 | /// The static stack allocated for the current function. 19 | pub stack: Vec>, 20 | } 21 | 22 | impl EVMLAData<'_> { 23 | /// The default stack size. 24 | pub const DEFAULT_STACK_SIZE: usize = 64; 25 | 26 | /// 27 | /// A shortcut constructor. 28 | /// 29 | pub fn new(version: semver::Version) -> Self { 30 | Self { 31 | version, 32 | stack: Vec::with_capacity(Self::DEFAULT_STACK_SIZE), 33 | } 34 | } 35 | } 36 | 37 | impl<'ctx> IEVMLAData<'ctx> for EVMLAData<'ctx> { 38 | fn get_element(&self, position: usize) -> &Value<'ctx> { 39 | &self.stack[position] 40 | } 41 | 42 | fn set_element(&mut self, position: usize, value: Value<'ctx>) { 43 | self.stack[position] = value; 44 | } 45 | 46 | fn set_original(&mut self, position: usize, original: String) { 47 | self.stack[position].original = Some(original); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/evm/context/function/runtime/entry.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The entry function. 3 | //! 4 | 5 | use crate::context::IContext; 6 | use crate::evm::context::Context; 7 | use crate::evm::WriteLLVM; 8 | 9 | /// 10 | /// The entry function. 11 | /// 12 | /// Is a special runtime function that is only used by the front-end generated code. 13 | /// 14 | #[derive(Debug)] 15 | pub struct Entry 16 | where 17 | B: WriteLLVM, 18 | { 19 | /// The runtime code AST representation. 20 | inner: B, 21 | } 22 | 23 | impl Entry 24 | where 25 | B: WriteLLVM, 26 | { 27 | /// 28 | /// A shortcut constructor. 29 | /// 30 | pub fn new(inner: B) -> Self { 31 | Self { inner } 32 | } 33 | } 34 | 35 | impl WriteLLVM for Entry 36 | where 37 | B: WriteLLVM, 38 | { 39 | fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> { 40 | let function_type = context.function_type::(vec![], 0); 41 | context.add_function( 42 | crate::r#const::ENTRY_FUNCTION_NAME, 43 | function_type, 44 | 0, 45 | Some(inkwell::module::Linkage::External), 46 | )?; 47 | 48 | self.inner.declare(context) 49 | } 50 | 51 | fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> { 52 | context.set_current_function(crate::r#const::ENTRY_FUNCTION_NAME)?; 53 | 54 | context.set_basic_block(context.current_function().borrow().entry_block()); 55 | self.inner.into_llvm(context)?; 56 | match context 57 | .basic_block() 58 | .get_last_instruction() 59 | .map(|instruction| instruction.get_opcode()) 60 | { 61 | Some(inkwell::values::InstructionOpcode::Br) => {} 62 | Some(inkwell::values::InstructionOpcode::Switch) => {} 63 | _ => context 64 | .build_unconditional_branch(context.current_function().borrow().return_block())?, 65 | } 66 | 67 | context.set_basic_block(context.current_function().borrow().return_block()); 68 | crate::evm::instructions::r#return::stop(context)?; 69 | 70 | Ok(()) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/evm/context/function/runtime/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The front-end runtime functions. 3 | //! 4 | 5 | pub mod entry; 6 | -------------------------------------------------------------------------------- /src/evm/context/function/vyper_data.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM function Vyper data. 3 | //! 4 | 5 | use std::collections::HashMap; 6 | 7 | /// 8 | /// The LLVM function Vyper data. 9 | /// 10 | /// Describes some data that is only relevant to Vyper. 11 | /// 12 | #[derive(Debug)] 13 | pub struct VyperData { 14 | /// The block-local variables. They are still allocated at the beginning of the function, 15 | /// but their parent block must be known in order to pass the implicit arguments thereto. 16 | /// Is only used by the Vyper LLL IR compiler. 17 | label_arguments: HashMap>, 18 | } 19 | 20 | impl Default for VyperData { 21 | fn default() -> Self { 22 | Self { 23 | label_arguments: HashMap::with_capacity(Self::LABEL_ARGUMENTS_HASHMAP_INITIAL_CAPACITY), 24 | } 25 | } 26 | } 27 | 28 | impl VyperData { 29 | /// The label arguments hashmap default capacity. 30 | const LABEL_ARGUMENTS_HASHMAP_INITIAL_CAPACITY: usize = 16; 31 | 32 | /// 33 | /// A shortcut constructor. 34 | /// 35 | pub fn new() -> Self { 36 | Self::default() 37 | } 38 | 39 | /// 40 | /// Returns the list of a Vyper label arguments. 41 | /// 42 | pub fn label_arguments(&self, label_name: &str) -> Option> { 43 | self.label_arguments.get(label_name).cloned() 44 | } 45 | 46 | /// 47 | /// Inserts arguments for the specified label. 48 | /// 49 | pub fn insert_label_arguments(&mut self, label_name: String, arguments: Vec) { 50 | self.label_arguments.insert(label_name, arguments); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/evm/context/solidity_data.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM IR generator Solidity data. 3 | //! 4 | 5 | use std::collections::BTreeMap; 6 | use std::collections::BTreeSet; 7 | 8 | use crate::context::traits::solidity_data::ISolidityData; 9 | 10 | /// 11 | /// The LLVM IR generator Solidity data. 12 | /// 13 | /// Describes some data that is only relevant to Solidity. 14 | /// 15 | #[derive(Debug, Default)] 16 | pub struct SolidityData { 17 | /// The immutables identifier-to-offset mapping. 18 | immutables: BTreeMap>, 19 | } 20 | 21 | impl ISolidityData for SolidityData { 22 | fn offsets(&self, id: &str) -> Option<&BTreeSet> { 23 | self.immutables.get(id) 24 | } 25 | } 26 | 27 | impl SolidityData { 28 | /// 29 | /// A shortcut constructor. 30 | /// 31 | pub fn new(immutables: BTreeMap>) -> Self { 32 | Self { immutables } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/evm/context/yul_data.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM IR generator Yul data. 3 | //! 4 | 5 | use std::collections::BTreeMap; 6 | 7 | use crate::context::traits::yul_data::IYulData; 8 | 9 | /// 10 | /// The LLVM IR generator Yul data. 11 | /// 12 | /// Describes some data that is only relevant to Yul. 13 | /// 14 | #[derive(Debug, Default)] 15 | pub struct YulData { 16 | /// Mapping from Yul object identifiers to full contract paths. 17 | identifier_paths: BTreeMap, 18 | } 19 | 20 | impl YulData { 21 | /// 22 | /// A shortcut constructor. 23 | /// 24 | pub fn new(identifier_paths: BTreeMap) -> Self { 25 | Self { identifier_paths } 26 | } 27 | } 28 | 29 | impl IYulData for YulData { 30 | fn resolve_path(&self, identifier: &str) -> Option<&str> { 31 | self.identifier_paths 32 | .get(identifier) 33 | .map(|path| path.as_str()) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/evm/instructions/call.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates a contract call. 3 | //! 4 | 5 | use inkwell::values::BasicValue; 6 | 7 | use crate::context::pointer::Pointer; 8 | use crate::context::IContext; 9 | use crate::evm::context::address_space::AddressSpace; 10 | use crate::evm::context::Context; 11 | 12 | /// 13 | /// Translates an external call. 14 | /// 15 | #[allow(clippy::too_many_arguments)] 16 | pub fn call<'ctx>( 17 | context: &mut Context<'ctx>, 18 | gas: inkwell::values::IntValue<'ctx>, 19 | address: inkwell::values::IntValue<'ctx>, 20 | value: inkwell::values::IntValue<'ctx>, 21 | input_offset: inkwell::values::IntValue<'ctx>, 22 | input_length: inkwell::values::IntValue<'ctx>, 23 | output_offset: inkwell::values::IntValue<'ctx>, 24 | output_length: inkwell::values::IntValue<'ctx>, 25 | ) -> anyhow::Result> { 26 | let input_offset_pointer = Pointer::new_with_offset( 27 | context, 28 | AddressSpace::Heap, 29 | context.byte_type(), 30 | input_offset, 31 | "call_input_offset_pointer", 32 | )?; 33 | let output_offset_pointer = Pointer::new_with_offset( 34 | context, 35 | AddressSpace::Heap, 36 | context.byte_type(), 37 | output_offset, 38 | "call_output_offset_pointer", 39 | )?; 40 | 41 | Ok(context 42 | .build_call( 43 | context.intrinsics().call, 44 | &[ 45 | gas.as_basic_value_enum(), 46 | address.as_basic_value_enum(), 47 | value.as_basic_value_enum(), 48 | input_offset_pointer.as_basic_value_enum(), 49 | input_length.as_basic_value_enum(), 50 | output_offset_pointer.as_basic_value_enum(), 51 | output_length.as_basic_value_enum(), 52 | ], 53 | "call", 54 | )? 55 | .expect("Always exists")) 56 | } 57 | 58 | /// 59 | /// Translates a static call. 60 | /// 61 | #[allow(clippy::too_many_arguments)] 62 | pub fn static_call<'ctx>( 63 | context: &mut Context<'ctx>, 64 | gas: inkwell::values::IntValue<'ctx>, 65 | address: inkwell::values::IntValue<'ctx>, 66 | input_offset: inkwell::values::IntValue<'ctx>, 67 | input_length: inkwell::values::IntValue<'ctx>, 68 | output_offset: inkwell::values::IntValue<'ctx>, 69 | output_length: inkwell::values::IntValue<'ctx>, 70 | ) -> anyhow::Result> { 71 | let input_offset_pointer = Pointer::new_with_offset( 72 | context, 73 | AddressSpace::Heap, 74 | context.byte_type(), 75 | input_offset, 76 | "staticcall_input_offset_pointer", 77 | )?; 78 | let output_offset_pointer = Pointer::new_with_offset( 79 | context, 80 | AddressSpace::Heap, 81 | context.byte_type(), 82 | output_offset, 83 | "staticcall_output_offset_pointer", 84 | )?; 85 | 86 | Ok(context 87 | .build_call( 88 | context.intrinsics().staticcall, 89 | &[ 90 | gas.as_basic_value_enum(), 91 | address.as_basic_value_enum(), 92 | input_offset_pointer.as_basic_value_enum(), 93 | input_length.as_basic_value_enum(), 94 | output_offset_pointer.as_basic_value_enum(), 95 | output_length.as_basic_value_enum(), 96 | ], 97 | "static_call", 98 | )? 99 | .expect("Always exists")) 100 | } 101 | 102 | /// 103 | /// Translates a delegate call. 104 | /// 105 | #[allow(clippy::too_many_arguments)] 106 | pub fn delegate_call<'ctx>( 107 | context: &mut Context<'ctx>, 108 | gas: inkwell::values::IntValue<'ctx>, 109 | address: inkwell::values::IntValue<'ctx>, 110 | input_offset: inkwell::values::IntValue<'ctx>, 111 | input_length: inkwell::values::IntValue<'ctx>, 112 | output_offset: inkwell::values::IntValue<'ctx>, 113 | output_length: inkwell::values::IntValue<'ctx>, 114 | ) -> anyhow::Result> { 115 | let input_offset_pointer = Pointer::new_with_offset( 116 | context, 117 | AddressSpace::Heap, 118 | context.byte_type(), 119 | input_offset, 120 | "delegatecall_input_offset_pointer", 121 | )?; 122 | let output_offset_pointer = Pointer::new_with_offset( 123 | context, 124 | AddressSpace::Heap, 125 | context.byte_type(), 126 | output_offset, 127 | "delegatecall_output_offset_pointer", 128 | )?; 129 | 130 | Ok(context 131 | .build_call( 132 | context.intrinsics().delegatecall, 133 | &[ 134 | gas.as_basic_value_enum(), 135 | address.as_basic_value_enum(), 136 | input_offset_pointer.as_basic_value_enum(), 137 | input_length.as_basic_value_enum(), 138 | output_offset_pointer.as_basic_value_enum(), 139 | output_length.as_basic_value_enum(), 140 | ], 141 | "delegate_call", 142 | )? 143 | .expect("Always exists")) 144 | } 145 | 146 | /// 147 | /// Translates the `linkersymbol` instruction. 148 | /// 149 | pub fn linker_symbol<'ctx>( 150 | context: &mut Context<'ctx>, 151 | path: &str, 152 | ) -> anyhow::Result> { 153 | Ok(context 154 | .build_call_metadata( 155 | context.intrinsics().linkersymbol, 156 | &[context 157 | .llvm() 158 | .metadata_node(&[context.llvm().metadata_string(path).into()]) 159 | .into()], 160 | format!("linker_symbol_{path}").as_str(), 161 | )? 162 | .expect("Always exists")) 163 | } 164 | -------------------------------------------------------------------------------- /src/evm/instructions/calldata.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the calldata instructions. 3 | //! 4 | 5 | use crate::context::pointer::Pointer; 6 | use crate::context::IContext; 7 | use crate::evm::context::address_space::AddressSpace; 8 | use crate::evm::context::Context; 9 | 10 | /// 11 | /// Translates the calldata load. 12 | /// 13 | pub fn load<'ctx>( 14 | context: &mut Context<'ctx>, 15 | offset: inkwell::values::IntValue<'ctx>, 16 | ) -> anyhow::Result> { 17 | let pointer = Pointer::new_with_offset( 18 | context, 19 | AddressSpace::Calldata, 20 | context.field_type(), 21 | offset, 22 | "calldataload_pointer", 23 | )?; 24 | let result = context.build_load(pointer, "calldata_load_result")?; 25 | Ok(result) 26 | } 27 | 28 | /// 29 | /// Translates the calldata size. 30 | /// 31 | pub fn size<'ctx>( 32 | context: &mut Context<'ctx>, 33 | ) -> anyhow::Result> { 34 | Ok(context 35 | .build_call(context.intrinsics().calldatasize, &[], "calldatasize")? 36 | .expect("Always exists")) 37 | } 38 | 39 | /// 40 | /// Translates the calldata copy. 41 | /// 42 | pub fn copy<'ctx>( 43 | context: &mut Context<'ctx>, 44 | destination_offset: inkwell::values::IntValue<'ctx>, 45 | source_offset: inkwell::values::IntValue<'ctx>, 46 | size: inkwell::values::IntValue<'ctx>, 47 | ) -> anyhow::Result<()> { 48 | let destination = Pointer::new_with_offset( 49 | context, 50 | AddressSpace::Heap, 51 | context.byte_type(), 52 | destination_offset, 53 | "calldatacopy_destination_pointer", 54 | )?; 55 | 56 | let source = Pointer::new_with_offset( 57 | context, 58 | AddressSpace::Calldata, 59 | context.byte_type(), 60 | source_offset, 61 | "calldatacopy_source_pointer", 62 | )?; 63 | 64 | context.build_memcpy( 65 | context.intrinsics().memory_copy_from_calldata, 66 | destination, 67 | source, 68 | size, 69 | "calldatacopy_memcpy", 70 | )?; 71 | Ok(()) 72 | } 73 | -------------------------------------------------------------------------------- /src/evm/instructions/code.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the external code operations. 3 | //! 4 | 5 | use inkwell::values::BasicValue; 6 | 7 | use crate::context::pointer::Pointer; 8 | use crate::context::IContext; 9 | use crate::evm::context::address_space::AddressSpace; 10 | use crate::evm::context::Context; 11 | 12 | /// 13 | /// Translates the `codesize` instruction. 14 | /// 15 | pub fn size<'ctx>( 16 | context: &mut Context<'ctx>, 17 | ) -> anyhow::Result> { 18 | Ok(context 19 | .build_call(context.intrinsics().codesize, &[], "codesize")? 20 | .expect("Always exists")) 21 | } 22 | 23 | /// 24 | /// Translates the `codecopy` instruction. 25 | /// 26 | pub fn copy<'ctx>( 27 | context: &mut Context<'ctx>, 28 | destination_offset: inkwell::values::IntValue<'ctx>, 29 | source_offset: inkwell::values::IntValue<'ctx>, 30 | size: inkwell::values::IntValue<'ctx>, 31 | ) -> anyhow::Result<()> { 32 | let destination = Pointer::new_with_offset( 33 | context, 34 | AddressSpace::Heap, 35 | context.byte_type(), 36 | destination_offset, 37 | "codecopy_destination_pointer", 38 | )?; 39 | 40 | let source = Pointer::new_with_offset( 41 | context, 42 | AddressSpace::Code, 43 | context.byte_type(), 44 | source_offset, 45 | "codecopy_source_pointer", 46 | )?; 47 | 48 | context.build_memcpy( 49 | context.intrinsics().memory_copy_from_code, 50 | destination, 51 | source, 52 | size, 53 | "codecopy_memcpy", 54 | )?; 55 | Ok(()) 56 | } 57 | 58 | /// 59 | /// Translates the `dataoffset` instruction. 60 | /// 61 | pub fn data_offset<'ctx>( 62 | context: &mut Context<'ctx>, 63 | object_name: &str, 64 | ) -> anyhow::Result> { 65 | let object_name = context 66 | .llvm() 67 | .metadata_node(&[context.llvm().metadata_string(object_name).into()]); 68 | 69 | Ok(context 70 | .build_call_metadata( 71 | context.intrinsics().dataoffset, 72 | &[object_name.into()], 73 | "dataoffset", 74 | )? 75 | .expect("Always exists")) 76 | } 77 | 78 | /// 79 | /// Translates the `datasize` instruction. 80 | /// 81 | pub fn data_size<'ctx>( 82 | context: &mut Context<'ctx>, 83 | object_name: &str, 84 | ) -> anyhow::Result> { 85 | let object_name = context 86 | .llvm() 87 | .metadata_node(&[context.llvm().metadata_string(object_name).into()]); 88 | 89 | Ok(context 90 | .build_call_metadata( 91 | context.intrinsics().datasize, 92 | &[object_name.into()], 93 | "datasize", 94 | )? 95 | .expect("Always exists")) 96 | } 97 | 98 | /// 99 | /// Translates the `extcodesize` instruction. 100 | /// 101 | pub fn ext_size<'ctx>( 102 | context: &mut Context<'ctx>, 103 | address: inkwell::values::IntValue<'ctx>, 104 | ) -> anyhow::Result> { 105 | Ok(context 106 | .build_call( 107 | context.intrinsics().extcodesize, 108 | &[address.as_basic_value_enum()], 109 | "extcodesize", 110 | )? 111 | .expect("Always exists")) 112 | } 113 | 114 | /// 115 | /// Translates the `extcodecopy` instruction. 116 | /// 117 | pub fn ext_copy<'ctx>( 118 | context: &mut Context<'ctx>, 119 | address: inkwell::values::IntValue<'ctx>, 120 | destination_offset: inkwell::values::IntValue<'ctx>, 121 | source_offset: inkwell::values::IntValue<'ctx>, 122 | size: inkwell::values::IntValue<'ctx>, 123 | ) -> anyhow::Result<()> { 124 | let destination = Pointer::new_with_offset( 125 | context, 126 | AddressSpace::Heap, 127 | context.byte_type(), 128 | destination_offset, 129 | "extcodecopy_destination_pointer", 130 | )?; 131 | 132 | let source = Pointer::new_with_offset( 133 | context, 134 | AddressSpace::Code, 135 | context.byte_type(), 136 | source_offset, 137 | "extcodecopy_source_pointer", 138 | )?; 139 | 140 | context.build_call( 141 | context.intrinsics().extcodecopy, 142 | &[ 143 | address.as_basic_value_enum(), 144 | destination.as_basic_value_enum(), 145 | source.as_basic_value_enum(), 146 | size.as_basic_value_enum(), 147 | ], 148 | "extcodecopy", 149 | )?; 150 | Ok(()) 151 | } 152 | 153 | /// 154 | /// Translates the `extcodehash` instruction. 155 | /// 156 | pub fn ext_hash<'ctx>( 157 | context: &mut Context<'ctx>, 158 | address: inkwell::values::IntValue<'ctx>, 159 | ) -> anyhow::Result> { 160 | Ok(context 161 | .build_call( 162 | context.intrinsics().extcodehash, 163 | &[address.as_basic_value_enum()], 164 | "extcodehash", 165 | )? 166 | .expect("Always exists")) 167 | } 168 | -------------------------------------------------------------------------------- /src/evm/instructions/comparison.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the comparison operations. 3 | //! 4 | 5 | use inkwell::values::BasicValue; 6 | 7 | use crate::context::IContext; 8 | use crate::evm::context::Context; 9 | 10 | /// 11 | /// Translates the comparison operations. 12 | /// 13 | /// There is not difference between the EVM and LLVM IR behaviors. 14 | /// 15 | pub fn compare<'ctx>( 16 | context: &mut Context<'ctx>, 17 | operand_1: inkwell::values::IntValue<'ctx>, 18 | operand_2: inkwell::values::IntValue<'ctx>, 19 | operation: inkwell::IntPredicate, 20 | ) -> anyhow::Result> { 21 | let result = context.builder().build_int_compare( 22 | operation, 23 | operand_1, 24 | operand_2, 25 | "comparison_result", 26 | )?; 27 | let result = context.builder().build_int_z_extend_or_bit_cast( 28 | result, 29 | context.field_type(), 30 | "comparison_result_extended", 31 | )?; 32 | Ok(result.as_basic_value_enum()) 33 | } 34 | -------------------------------------------------------------------------------- /src/evm/instructions/context.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the context getter instructions. 3 | //! 4 | 5 | use inkwell::values::BasicValue; 6 | 7 | use crate::context::IContext; 8 | use crate::evm::context::Context; 9 | 10 | /// 11 | /// Translates the `gas_limit` instruction. 12 | /// 13 | pub fn gas_limit<'ctx>( 14 | context: &mut Context<'ctx>, 15 | ) -> anyhow::Result> { 16 | Ok(context 17 | .build_call(context.intrinsics().gaslimit, &[], "gaslimit")? 18 | .expect("Always exists")) 19 | } 20 | 21 | /// 22 | /// Translates the `gas_price` instruction. 23 | /// 24 | pub fn gas_price<'ctx>( 25 | context: &mut Context<'ctx>, 26 | ) -> anyhow::Result> { 27 | Ok(context 28 | .build_call(context.intrinsics().gasprice, &[], "gasprice")? 29 | .expect("Always exists")) 30 | } 31 | 32 | /// 33 | /// Translates the `tx.origin` instruction. 34 | /// 35 | pub fn origin<'ctx>( 36 | context: &mut Context<'ctx>, 37 | ) -> anyhow::Result> { 38 | Ok(context 39 | .build_call(context.intrinsics().origin, &[], "origin")? 40 | .expect("Always exists")) 41 | } 42 | 43 | /// 44 | /// Translates the `chain_id` instruction. 45 | /// 46 | pub fn chain_id<'ctx>( 47 | context: &mut Context<'ctx>, 48 | ) -> anyhow::Result> { 49 | Ok(context 50 | .build_call(context.intrinsics().chainid, &[], "chainid")? 51 | .expect("Always exists")) 52 | } 53 | 54 | /// 55 | /// Translates the `block_number` instruction. 56 | /// 57 | pub fn block_number<'ctx>( 58 | context: &mut Context<'ctx>, 59 | ) -> anyhow::Result> { 60 | Ok(context 61 | .build_call(context.intrinsics().number, &[], "number")? 62 | .expect("Always exists")) 63 | } 64 | 65 | /// 66 | /// Translates the `block_timestamp` instruction. 67 | /// 68 | pub fn block_timestamp<'ctx>( 69 | context: &mut Context<'ctx>, 70 | ) -> anyhow::Result> { 71 | Ok(context 72 | .build_call(context.intrinsics().timestamp, &[], "timestamp")? 73 | .expect("Always exists")) 74 | } 75 | 76 | /// 77 | /// Translates the `block_hash` instruction. 78 | /// 79 | pub fn block_hash<'ctx>( 80 | context: &mut Context<'ctx>, 81 | index: inkwell::values::IntValue<'ctx>, 82 | ) -> anyhow::Result> { 83 | Ok(context 84 | .build_call( 85 | context.intrinsics().blockhash, 86 | &[index.as_basic_value_enum()], 87 | "blockhash", 88 | )? 89 | .expect("Always exists")) 90 | } 91 | 92 | /// 93 | /// Translates the `difficulty` instruction. 94 | /// 95 | pub fn difficulty<'ctx>( 96 | context: &mut Context<'ctx>, 97 | ) -> anyhow::Result> { 98 | Ok(context 99 | .build_call(context.intrinsics().difficulty, &[], "difficulty")? 100 | .expect("Always exists")) 101 | } 102 | 103 | /// 104 | /// Translates the `coinbase` instruction. 105 | /// 106 | pub fn coinbase<'ctx>( 107 | context: &mut Context<'ctx>, 108 | ) -> anyhow::Result> { 109 | Ok(context 110 | .build_call(context.intrinsics().coinbase, &[], "coinbase")? 111 | .expect("Always exists")) 112 | } 113 | 114 | /// 115 | /// Translates the `basefee` instruction. 116 | /// 117 | pub fn basefee<'ctx>( 118 | context: &mut Context<'ctx>, 119 | ) -> anyhow::Result> { 120 | Ok(context 121 | .build_call(context.intrinsics().basefee, &[], "basefee")? 122 | .expect("Always exists")) 123 | } 124 | 125 | /// 126 | /// Translates the `msize` instruction. 127 | /// 128 | pub fn msize<'ctx>( 129 | context: &mut Context<'ctx>, 130 | ) -> anyhow::Result> { 131 | Ok(context 132 | .build_call(context.intrinsics().msize, &[], "msize")? 133 | .expect("Always exists")) 134 | } 135 | -------------------------------------------------------------------------------- /src/evm/instructions/create.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the contract creation instructions. 3 | //! 4 | 5 | use inkwell::values::BasicValue; 6 | 7 | use crate::context::pointer::Pointer; 8 | use crate::context::IContext; 9 | use crate::evm::context::address_space::AddressSpace; 10 | use crate::evm::context::Context; 11 | 12 | /// 13 | /// Translates the contract `create` instruction. 14 | /// 15 | pub fn create<'ctx>( 16 | context: &mut Context<'ctx>, 17 | value: inkwell::values::IntValue<'ctx>, 18 | input_offset: inkwell::values::IntValue<'ctx>, 19 | input_length: inkwell::values::IntValue<'ctx>, 20 | ) -> anyhow::Result> { 21 | let input_offset_pointer = Pointer::new_with_offset( 22 | context, 23 | AddressSpace::Heap, 24 | context.byte_type(), 25 | input_offset, 26 | "create_input_offset_pointer", 27 | )?; 28 | 29 | Ok(context 30 | .build_call( 31 | context.intrinsics().create, 32 | &[ 33 | value.as_basic_value_enum(), 34 | input_offset_pointer.as_basic_value_enum(), 35 | input_length.as_basic_value_enum(), 36 | ], 37 | "create", 38 | )? 39 | .expect("Always exists")) 40 | } 41 | 42 | /// 43 | /// Translates the contract `create2` instruction. 44 | /// 45 | pub fn create2<'ctx>( 46 | context: &mut Context<'ctx>, 47 | value: inkwell::values::IntValue<'ctx>, 48 | input_offset: inkwell::values::IntValue<'ctx>, 49 | input_length: inkwell::values::IntValue<'ctx>, 50 | salt: inkwell::values::IntValue<'ctx>, 51 | ) -> anyhow::Result> { 52 | let input_offset_pointer = Pointer::new_with_offset( 53 | context, 54 | AddressSpace::Heap, 55 | context.byte_type(), 56 | input_offset, 57 | "create2_input_offset_pointer", 58 | )?; 59 | 60 | Ok(context 61 | .build_call( 62 | context.intrinsics().create2, 63 | &[ 64 | value.as_basic_value_enum(), 65 | input_offset_pointer.as_basic_value_enum(), 66 | input_length.as_basic_value_enum(), 67 | salt.as_basic_value_enum(), 68 | ], 69 | "create2", 70 | )? 71 | .expect("Always exists")) 72 | } 73 | -------------------------------------------------------------------------------- /src/evm/instructions/ether_gas.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the value and balance operations. 3 | //! 4 | 5 | use inkwell::values::BasicValue; 6 | 7 | use crate::context::IContext; 8 | use crate::evm::context::Context; 9 | 10 | /// 11 | /// Translates the `gas` instruction. 12 | /// 13 | pub fn gas<'ctx>( 14 | context: &mut Context<'ctx>, 15 | ) -> anyhow::Result> { 16 | Ok(context 17 | .build_call(context.intrinsics().gas, &[], "gas")? 18 | .expect("Always exists")) 19 | } 20 | 21 | /// 22 | /// Translates the `callvalue` instruction. 23 | /// 24 | pub fn callvalue<'ctx>( 25 | context: &mut Context<'ctx>, 26 | ) -> anyhow::Result> { 27 | Ok(context 28 | .build_call(context.intrinsics().callvalue, &[], "callvalue")? 29 | .expect("Always exists")) 30 | } 31 | 32 | /// 33 | /// Translates the `balance` instructions. 34 | /// 35 | pub fn balance<'ctx>( 36 | context: &mut Context<'ctx>, 37 | address: inkwell::values::IntValue<'ctx>, 38 | ) -> anyhow::Result> { 39 | Ok(context 40 | .build_call( 41 | context.intrinsics().balance, 42 | &[address.as_basic_value_enum()], 43 | "balance", 44 | )? 45 | .expect("Always exists")) 46 | } 47 | 48 | /// 49 | /// Translates the `selfbalance` instructions. 50 | /// 51 | pub fn self_balance<'ctx>( 52 | context: &mut Context<'ctx>, 53 | ) -> anyhow::Result> { 54 | Ok(context 55 | .build_call(context.intrinsics().selfbalance, &[], "selfbalance")? 56 | .expect("Always exists")) 57 | } 58 | 59 | /// 60 | /// Translates the `selfdestruct` instructions. 61 | /// 62 | pub fn self_destruct<'ctx>( 63 | context: &mut Context<'ctx>, 64 | address: inkwell::values::IntValue<'ctx>, 65 | ) -> anyhow::Result> { 66 | Ok(context 67 | .build_call( 68 | context.intrinsics().selfdestruct, 69 | &[address.as_basic_value_enum()], 70 | "selfdestruct", 71 | )? 72 | .expect("Always exists")) 73 | } 74 | -------------------------------------------------------------------------------- /src/evm/instructions/event.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates a log or event call. 3 | //! 4 | 5 | use inkwell::values::BasicValue; 6 | 7 | use crate::context::pointer::Pointer; 8 | use crate::context::IContext; 9 | use crate::evm::context::address_space::AddressSpace; 10 | use crate::evm::context::Context; 11 | 12 | /// 13 | /// Translates an event log call. 14 | /// 15 | pub fn log<'ctx>( 16 | context: &mut Context<'ctx>, 17 | input_offset: inkwell::values::IntValue<'ctx>, 18 | input_length: inkwell::values::IntValue<'ctx>, 19 | topics: Vec>, 20 | ) -> anyhow::Result<()> { 21 | let input_offset_pointer = Pointer::new_with_offset( 22 | context, 23 | AddressSpace::Heap, 24 | context.byte_type(), 25 | input_offset, 26 | format!("log{}_input_offset_pointer", topics.len()).as_str(), 27 | )?; 28 | 29 | match topics.len() { 30 | 0 => context.build_call( 31 | context.intrinsics().log0, 32 | &[ 33 | input_offset_pointer.as_basic_value_enum(), 34 | input_length.as_basic_value_enum(), 35 | ], 36 | "log0", 37 | ), 38 | 1 => context.build_call( 39 | context.intrinsics().log1, 40 | &[ 41 | input_offset_pointer.as_basic_value_enum(), 42 | input_length.as_basic_value_enum(), 43 | topics[0].as_basic_value_enum(), 44 | ], 45 | "log1", 46 | ), 47 | 2 => context.build_call( 48 | context.intrinsics().log2, 49 | &[ 50 | input_offset_pointer.as_basic_value_enum(), 51 | input_length.as_basic_value_enum(), 52 | topics[0].as_basic_value_enum(), 53 | topics[1].as_basic_value_enum(), 54 | ], 55 | "log2", 56 | ), 57 | 3 => context.build_call( 58 | context.intrinsics().log3, 59 | &[ 60 | input_offset_pointer.as_basic_value_enum(), 61 | input_length.as_basic_value_enum(), 62 | topics[0].as_basic_value_enum(), 63 | topics[1].as_basic_value_enum(), 64 | topics[2].as_basic_value_enum(), 65 | ], 66 | "log3", 67 | ), 68 | 4 => context.build_call( 69 | context.intrinsics().log4, 70 | &[ 71 | input_offset_pointer.as_basic_value_enum(), 72 | input_length.as_basic_value_enum(), 73 | topics[0].as_basic_value_enum(), 74 | topics[1].as_basic_value_enum(), 75 | topics[2].as_basic_value_enum(), 76 | topics[3].as_basic_value_enum(), 77 | ], 78 | "log4", 79 | ), 80 | length => panic!("The number of topics must be from 0 to 4, found {}", length), 81 | }?; 82 | Ok(()) 83 | } 84 | -------------------------------------------------------------------------------- /src/evm/instructions/immutable.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the contract immutable operations. 3 | //! 4 | 5 | use crate::context::pointer::Pointer; 6 | use crate::context::traits::solidity_data::ISolidityData; 7 | use crate::context::IContext; 8 | use crate::evm::context::address_space::AddressSpace; 9 | use crate::evm::context::Context; 10 | 11 | /// 12 | /// Translates the `loadimmutable` instruction. 13 | /// 14 | pub fn load<'ctx>( 15 | context: &mut Context<'ctx>, 16 | id: &str, 17 | ) -> anyhow::Result> { 18 | let id = context 19 | .llvm() 20 | .metadata_node(&[context.llvm().metadata_string(id).into()]); 21 | 22 | Ok(context 23 | .build_call_metadata( 24 | context.intrinsics().loadimmutable, 25 | &[id.into()], 26 | "load_immutable", 27 | )? 28 | .expect("Always exists")) 29 | } 30 | 31 | /// 32 | /// Translates the `setimmutable` instruction. 33 | /// 34 | pub fn store<'ctx>( 35 | context: &mut Context<'ctx>, 36 | id: &str, 37 | base_offset: inkwell::values::IntValue<'ctx>, 38 | value: inkwell::values::IntValue<'ctx>, 39 | ) -> anyhow::Result<()> { 40 | let offsets = match context.solidity().expect("Always exists").offsets(id) { 41 | Some(offsets) => offsets, 42 | None => return Ok(()), 43 | }; 44 | 45 | for offset in offsets.iter() { 46 | let immutable_offset = context.builder().build_int_add( 47 | base_offset, 48 | context.field_const(*offset), 49 | "setimmutable_offset", 50 | )?; 51 | let immutable_pointer = Pointer::new_with_offset( 52 | context, 53 | AddressSpace::Heap, 54 | context.byte_type(), 55 | immutable_offset, 56 | "setimmutable_pointer", 57 | )?; 58 | context.build_store(immutable_pointer, value)?; 59 | } 60 | 61 | Ok(()) 62 | } 63 | -------------------------------------------------------------------------------- /src/evm/instructions/math.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the mathematics operation. 3 | //! 4 | 5 | use inkwell::values::BasicValue; 6 | 7 | use crate::context::pointer::Pointer; 8 | use crate::context::IContext; 9 | use crate::evm::context::address_space::AddressSpace; 10 | use crate::evm::context::Context; 11 | 12 | /// 13 | /// Translates the `addmod` instruction. 14 | /// 15 | pub fn add_mod<'ctx>( 16 | context: &mut Context<'ctx>, 17 | operand_1: inkwell::values::IntValue<'ctx>, 18 | operand_2: inkwell::values::IntValue<'ctx>, 19 | modulo: inkwell::values::IntValue<'ctx>, 20 | ) -> anyhow::Result> { 21 | Ok(context 22 | .build_call( 23 | context.intrinsics().addmod, 24 | &[ 25 | operand_1.as_basic_value_enum(), 26 | operand_2.as_basic_value_enum(), 27 | modulo.as_basic_value_enum(), 28 | ], 29 | "addmod", 30 | )? 31 | .expect("Always exists")) 32 | } 33 | 34 | /// 35 | /// Translates the `mulmod` instruction. 36 | /// 37 | pub fn mul_mod<'ctx>( 38 | context: &mut Context<'ctx>, 39 | operand_1: inkwell::values::IntValue<'ctx>, 40 | operand_2: inkwell::values::IntValue<'ctx>, 41 | modulo: inkwell::values::IntValue<'ctx>, 42 | ) -> anyhow::Result> { 43 | Ok(context 44 | .build_call( 45 | context.intrinsics().mulmod, 46 | &[ 47 | operand_1.as_basic_value_enum(), 48 | operand_2.as_basic_value_enum(), 49 | modulo.as_basic_value_enum(), 50 | ], 51 | "mulmod", 52 | )? 53 | .expect("Always exists")) 54 | } 55 | 56 | /// 57 | /// Translates the `exp` instruction. 58 | /// 59 | pub fn exponent<'ctx>( 60 | context: &mut Context<'ctx>, 61 | value: inkwell::values::IntValue<'ctx>, 62 | exponent: inkwell::values::IntValue<'ctx>, 63 | ) -> anyhow::Result> { 64 | Ok(context 65 | .build_call( 66 | context.intrinsics().exp, 67 | &[value.as_basic_value_enum(), exponent.as_basic_value_enum()], 68 | "mulmod", 69 | )? 70 | .expect("Always exists")) 71 | } 72 | 73 | /// 74 | /// Translates the `signextend` instruction. 75 | /// 76 | pub fn sign_extend<'ctx>( 77 | context: &mut Context<'ctx>, 78 | bytes: inkwell::values::IntValue<'ctx>, 79 | value: inkwell::values::IntValue<'ctx>, 80 | ) -> anyhow::Result> { 81 | Ok(context 82 | .build_call( 83 | context.intrinsics().signextend, 84 | &[bytes.as_basic_value_enum(), value.as_basic_value_enum()], 85 | "signextend", 86 | )? 87 | .expect("Always exists")) 88 | } 89 | 90 | /// 91 | /// Translates the `keccak256` instruction. 92 | /// 93 | pub fn keccak256<'ctx>( 94 | context: &mut Context<'ctx>, 95 | input_offset: inkwell::values::IntValue<'ctx>, 96 | input_length: inkwell::values::IntValue<'ctx>, 97 | ) -> anyhow::Result> { 98 | let input_offset_pointer = Pointer::new_with_offset( 99 | context, 100 | AddressSpace::Heap, 101 | context.byte_type(), 102 | input_offset, 103 | "keccak256_input_offset_pointer", 104 | )?; 105 | 106 | Ok(context 107 | .build_call( 108 | context.intrinsics().sha3, 109 | &[ 110 | input_offset_pointer.as_basic_value_enum(), 111 | input_length.as_basic_value_enum(), 112 | ], 113 | "keccak256", 114 | )? 115 | .expect("Always exists")) 116 | } 117 | -------------------------------------------------------------------------------- /src/evm/instructions/memory.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the heap memory operations. 3 | //! 4 | 5 | use inkwell::values::BasicValue; 6 | 7 | use crate::context::pointer::Pointer; 8 | use crate::context::IContext; 9 | use crate::evm::context::address_space::AddressSpace; 10 | use crate::evm::context::Context; 11 | 12 | /// 13 | /// Translates the `mload` instruction. 14 | /// 15 | /// Uses the main heap. 16 | /// 17 | pub fn load<'ctx>( 18 | context: &mut Context<'ctx>, 19 | offset: inkwell::values::IntValue<'ctx>, 20 | ) -> anyhow::Result> { 21 | let pointer = Pointer::new_with_offset( 22 | context, 23 | AddressSpace::Heap, 24 | context.field_type(), 25 | offset, 26 | "memory_load_pointer", 27 | )?; 28 | let result = context.build_load(pointer, "memory_load_result")?; 29 | Ok(result) 30 | } 31 | 32 | /// 33 | /// Translates the `mstore` instruction. 34 | /// 35 | /// Uses the main heap. 36 | /// 37 | pub fn store<'ctx>( 38 | context: &mut Context<'ctx>, 39 | offset: inkwell::values::IntValue<'ctx>, 40 | value: inkwell::values::IntValue<'ctx>, 41 | ) -> anyhow::Result<()> { 42 | let pointer = Pointer::new_with_offset( 43 | context, 44 | AddressSpace::Heap, 45 | context.field_type(), 46 | offset, 47 | "memory_store_pointer", 48 | )?; 49 | context.build_store(pointer, value)?; 50 | Ok(()) 51 | } 52 | 53 | /// 54 | /// Translates the `mstore8` instruction. 55 | /// 56 | /// Uses the main heap. 57 | /// 58 | pub fn store_byte<'ctx>( 59 | context: &mut Context<'ctx>, 60 | offset: inkwell::values::IntValue<'ctx>, 61 | value: inkwell::values::IntValue<'ctx>, 62 | ) -> anyhow::Result<()> { 63 | let pointer = Pointer::new_with_offset( 64 | context, 65 | AddressSpace::Heap, 66 | context.byte_type(), 67 | offset, 68 | "store_byte_pointer", 69 | )?; 70 | 71 | context.build_call( 72 | context.intrinsics().mstore8, 73 | &[pointer.as_basic_value_enum(), value.as_basic_value_enum()], 74 | "mstore8", 75 | )?; 76 | Ok(()) 77 | } 78 | -------------------------------------------------------------------------------- /src/evm/instructions/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The EVM instructions translation utils. 3 | //! 4 | 5 | pub mod arithmetic; 6 | pub mod bitwise; 7 | pub mod call; 8 | pub mod calldata; 9 | pub mod code; 10 | pub mod comparison; 11 | pub mod context; 12 | pub mod create; 13 | pub mod ether_gas; 14 | pub mod event; 15 | pub mod immutable; 16 | pub mod math; 17 | pub mod memory; 18 | pub mod r#return; 19 | pub mod return_data; 20 | pub mod storage; 21 | -------------------------------------------------------------------------------- /src/evm/instructions/return.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the transaction return operations. 3 | //! 4 | 5 | use inkwell::values::BasicValue; 6 | 7 | use crate::context::pointer::Pointer; 8 | use crate::context::IContext; 9 | use crate::evm::context::address_space::AddressSpace; 10 | use crate::evm::context::Context; 11 | 12 | /// 13 | /// Translates the `return` instruction. 14 | /// 15 | pub fn r#return<'ctx>( 16 | context: &mut Context<'ctx>, 17 | offset: inkwell::values::IntValue<'ctx>, 18 | length: inkwell::values::IntValue<'ctx>, 19 | ) -> anyhow::Result<()> { 20 | let offset_pointer = Pointer::new_with_offset( 21 | context, 22 | AddressSpace::Heap, 23 | context.byte_type(), 24 | offset, 25 | "revert_offset_pointer", 26 | )?; 27 | 28 | context.build_call( 29 | context.intrinsics().r#return, 30 | &[ 31 | offset_pointer.as_basic_value_enum(), 32 | length.as_basic_value_enum(), 33 | ], 34 | "return", 35 | )?; 36 | context.build_unreachable()?; 37 | Ok(()) 38 | } 39 | 40 | /// 41 | /// Translates the `revert` instruction. 42 | /// 43 | pub fn revert<'ctx>( 44 | context: &mut Context<'ctx>, 45 | offset: inkwell::values::IntValue<'ctx>, 46 | length: inkwell::values::IntValue<'ctx>, 47 | ) -> anyhow::Result<()> { 48 | let offset_pointer = Pointer::new_with_offset( 49 | context, 50 | AddressSpace::Heap, 51 | context.byte_type(), 52 | offset, 53 | "revert_offset_pointer", 54 | )?; 55 | 56 | context.build_call( 57 | context.intrinsics().revert, 58 | &[ 59 | offset_pointer.as_basic_value_enum(), 60 | length.as_basic_value_enum(), 61 | ], 62 | "revert", 63 | )?; 64 | context.build_unreachable()?; 65 | Ok(()) 66 | } 67 | 68 | /// 69 | /// Translates the `stop` instruction. 70 | /// 71 | pub fn stop(context: &mut Context) -> anyhow::Result<()> { 72 | context.build_call(context.intrinsics().stop, &[], "stop")?; 73 | context.build_unreachable()?; 74 | Ok(()) 75 | } 76 | 77 | /// 78 | /// Translates the `invalid` instruction. 79 | /// 80 | pub fn invalid(context: &mut Context) -> anyhow::Result<()> { 81 | context.build_call(context.intrinsics().invalid, &[], "invalid")?; 82 | context.build_unreachable()?; 83 | Ok(()) 84 | } 85 | -------------------------------------------------------------------------------- /src/evm/instructions/return_data.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the return data instructions. 3 | //! 4 | 5 | use crate::context::pointer::Pointer; 6 | use crate::context::IContext; 7 | use crate::evm::context::address_space::AddressSpace; 8 | use crate::evm::context::Context; 9 | 10 | /// 11 | /// Translates the return data size. 12 | /// 13 | pub fn size<'ctx>( 14 | context: &mut Context<'ctx>, 15 | ) -> anyhow::Result> { 16 | Ok(context 17 | .build_call(context.intrinsics().returndatasize, &[], "returndatasize")? 18 | .expect("Always exists")) 19 | } 20 | 21 | /// 22 | /// Translates the return data copy. 23 | /// 24 | pub fn copy<'ctx>( 25 | context: &mut Context<'ctx>, 26 | destination_offset: inkwell::values::IntValue<'ctx>, 27 | source_offset: inkwell::values::IntValue<'ctx>, 28 | size: inkwell::values::IntValue<'ctx>, 29 | ) -> anyhow::Result<()> { 30 | let destination = Pointer::new_with_offset( 31 | context, 32 | AddressSpace::Heap, 33 | context.byte_type(), 34 | destination_offset, 35 | "returndatacopy_destination_pointer", 36 | )?; 37 | 38 | let source = Pointer::new_with_offset( 39 | context, 40 | AddressSpace::ReturnData, 41 | context.byte_type(), 42 | source_offset, 43 | "returndatacopy_source_pointer", 44 | )?; 45 | 46 | context.build_memcpy( 47 | context.intrinsics().memory_copy_from_return_data, 48 | destination, 49 | source, 50 | size, 51 | "returndatacopy_memcpy", 52 | )?; 53 | Ok(()) 54 | } 55 | -------------------------------------------------------------------------------- /src/evm/instructions/storage.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Translates the contract storage operations. 3 | //! 4 | 5 | use crate::context::pointer::Pointer; 6 | use crate::context::IContext; 7 | use crate::evm::context::address_space::AddressSpace; 8 | use crate::evm::context::Context; 9 | 10 | /// 11 | /// Translates the contract storage load. 12 | /// 13 | pub fn load<'ctx>( 14 | context: &mut Context<'ctx>, 15 | position: inkwell::values::IntValue<'ctx>, 16 | ) -> anyhow::Result> { 17 | let position_pointer = Pointer::new_with_offset( 18 | context, 19 | AddressSpace::Storage, 20 | context.field_type(), 21 | position, 22 | "storage_load_position_pointer", 23 | )?; 24 | let value = context.build_load(position_pointer, "storage_load_value")?; 25 | Ok(value) 26 | } 27 | 28 | /// 29 | /// Translates the contract storage store. 30 | /// 31 | pub fn store<'ctx>( 32 | context: &mut Context<'ctx>, 33 | position: inkwell::values::IntValue<'ctx>, 34 | value: inkwell::values::IntValue<'ctx>, 35 | ) -> anyhow::Result<()> { 36 | let position_pointer = Pointer::new_with_offset( 37 | context, 38 | AddressSpace::Storage, 39 | context.field_type(), 40 | position, 41 | "storage_store_position_pointer", 42 | )?; 43 | context.build_store(position_pointer, value)?; 44 | Ok(()) 45 | } 46 | 47 | /// 48 | /// Translates the transient storage load. 49 | /// 50 | pub fn transient_load<'ctx>( 51 | context: &mut Context<'ctx>, 52 | position: inkwell::values::IntValue<'ctx>, 53 | ) -> anyhow::Result> { 54 | let position_pointer = Pointer::new_with_offset( 55 | context, 56 | AddressSpace::TransientStorage, 57 | context.field_type(), 58 | position, 59 | "transient_storage_load_position_pointer", 60 | )?; 61 | let value = context.build_load(position_pointer, "transient_storage_load_value")?; 62 | Ok(value) 63 | } 64 | 65 | /// 66 | /// Translates the transient storage store. 67 | /// 68 | pub fn transient_store<'ctx>( 69 | context: &mut Context<'ctx>, 70 | position: inkwell::values::IntValue<'ctx>, 71 | value: inkwell::values::IntValue<'ctx>, 72 | ) -> anyhow::Result<()> { 73 | let position_pointer = Pointer::new_with_offset( 74 | context, 75 | AddressSpace::TransientStorage, 76 | context.field_type(), 77 | position, 78 | "transient_storage_store_position_pointer", 79 | )?; 80 | context.build_store(position_pointer, value)?; 81 | Ok(()) 82 | } 83 | -------------------------------------------------------------------------------- /src/evm/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM EVM context library. 3 | //! 4 | 5 | pub mod build; 6 | pub mod r#const; 7 | pub mod context; 8 | pub mod instructions; 9 | pub mod warning; 10 | 11 | use std::collections::BTreeMap; 12 | 13 | use self::context::Context; 14 | 15 | /// 16 | /// Initializes the EVM target machine. 17 | /// 18 | pub fn initialize_target() { 19 | inkwell::targets::Target::initialize_evm(&inkwell::targets::InitializationConfig::default()); 20 | } 21 | 22 | /// 23 | /// Appends metadata to runtime code. 24 | /// 25 | pub fn append_metadata( 26 | bytecode_buffer: inkwell::memory_buffer::MemoryBuffer, 27 | metadata_hash: Option>, 28 | cbor_data: Option<(String, Vec<(String, semver::Version)>)>, 29 | ) -> anyhow::Result { 30 | let metadata = match (metadata_hash, cbor_data) { 31 | (Some(hash), Some((cbor_key, cbor_data))) => { 32 | let cbor = era_compiler_common::CBOR::new( 33 | Some(( 34 | era_compiler_common::EVMMetadataHashType::IPFS, 35 | hash.as_slice(), 36 | )), 37 | cbor_key, 38 | cbor_data, 39 | ); 40 | cbor.to_vec() 41 | } 42 | (None, Some((cbor_key, cbor_data))) => { 43 | let cbor = era_compiler_common::CBOR::<'_, String>::new(None, cbor_key, cbor_data); 44 | cbor.to_vec() 45 | } 46 | (_, None) => vec![], 47 | }; 48 | 49 | if metadata.is_empty() { 50 | return Ok(bytecode_buffer); 51 | } 52 | 53 | bytecode_buffer 54 | .append_metadata_evm(metadata.as_slice()) 55 | .map_err(|error| anyhow::anyhow!("bytecode metadata appending error: {error}")) 56 | } 57 | 58 | /// 59 | /// Assembles the main buffer and its dependencies from `bytecode_buffers`. 60 | /// 61 | pub fn assemble( 62 | bytecode_buffers: &[&inkwell::memory_buffer::MemoryBuffer], 63 | bytecode_buffer_ids: &[&str], 64 | code_segment: era_compiler_common::CodeSegment, 65 | ) -> anyhow::Result { 66 | let code_segment = match code_segment { 67 | era_compiler_common::CodeSegment::Deploy => inkwell::memory_buffer::CodeSegment::Deploy, 68 | era_compiler_common::CodeSegment::Runtime => inkwell::memory_buffer::CodeSegment::Runtime, 69 | }; 70 | inkwell::memory_buffer::MemoryBuffer::assemble_evm( 71 | bytecode_buffers, 72 | bytecode_buffer_ids, 73 | code_segment, 74 | ) 75 | .map_err(|error| anyhow::anyhow!("linking: {error}")) 76 | } 77 | 78 | /// 79 | /// Links `bytecode_buffer` with `linker_symbols`. 80 | /// 81 | pub fn link( 82 | bytecode_buffer: inkwell::memory_buffer::MemoryBuffer, 83 | linker_symbols: &BTreeMap, 84 | ) -> anyhow::Result { 85 | if !bytecode_buffer.is_elf_evm() { 86 | return Ok(bytecode_buffer); 87 | } 88 | 89 | let bytecode_buffer_linked = bytecode_buffer 90 | .link_evm(linker_symbols) 91 | .map_err(|error| anyhow::anyhow!("linking: {error}"))?; 92 | 93 | Ok(bytecode_buffer_linked) 94 | } 95 | 96 | /// 97 | /// Returns minimal deploy code patched with specified identifiers. 98 | /// 99 | pub fn minimal_deploy_code(deploy_code_identifier: &str, runtime_code_identifier: &str) -> String { 100 | format!( 101 | r#" 102 | ; ModuleID = '{deploy_code_identifier}' 103 | source_filename = "{deploy_code_identifier}" 104 | target datalayout = "E-p:256:256-i256:256:256-S256-a:256:256" 105 | target triple = "evm-unknown-unknown" 106 | 107 | ; Function Attrs: mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none) 108 | declare i256 @llvm.evm.datasize(metadata) #0 109 | 110 | ; Function Attrs: mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none) 111 | declare i256 @llvm.evm.dataoffset(metadata) #0 112 | 113 | ; Function Attrs: noreturn nounwind 114 | declare void @llvm.evm.return(ptr addrspace(1), i256) #1 115 | 116 | ; Function Attrs: mustprogress nocallback nofree nounwind willreturn memory(argmem: readwrite) 117 | declare void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) noalias nocapture writeonly, ptr addrspace(4) noalias nocapture readonly, i256, i1 immarg) #2 118 | 119 | ; Function Attrs: nofree noreturn null_pointer_is_valid 120 | define void @__entry() local_unnamed_addr #3 {{ 121 | entry: 122 | %datasize = tail call i256 @llvm.evm.datasize(metadata !0) 123 | %dataoffset = tail call i256 @llvm.evm.dataoffset(metadata !0) 124 | %codecopy_source_pointer = inttoptr i256 %dataoffset to ptr addrspace(4) 125 | tail call void @llvm.memcpy.p1.p4.i256(ptr addrspace(1) align 4294967296 null, ptr addrspace(4) align 1 %codecopy_source_pointer, i256 %datasize, i1 false) 126 | tail call void @llvm.evm.return(ptr addrspace(1) noalias nocapture nofree noundef nonnull align 32 null, i256 %datasize) 127 | unreachable 128 | }} 129 | 130 | attributes #0 = {{ mustprogress nocallback nofree nosync nounwind speculatable willreturn memory(none) }} 131 | attributes #1 = {{ noreturn nounwind }} 132 | attributes #2 = {{ mustprogress nocallback nofree nounwind willreturn memory(argmem: readwrite) }} 133 | attributes #3 = {{ nofree noreturn null_pointer_is_valid }} 134 | 135 | !0 = !{{!"{runtime_code_identifier}"}} 136 | "# 137 | ) 138 | } 139 | 140 | /// 141 | /// Implemented by items which are translated into LLVM IR. 142 | /// 143 | pub trait WriteLLVM { 144 | /// 145 | /// Declares the entity in the LLVM IR. 146 | /// Is usually performed in order to use the item before defining it. 147 | /// 148 | fn declare(&mut self, _context: &mut Context) -> anyhow::Result<()> { 149 | Ok(()) 150 | } 151 | 152 | /// 153 | /// Translates the entity into LLVM IR. 154 | /// 155 | fn into_llvm(self, context: &mut Context) -> anyhow::Result<()>; 156 | } 157 | 158 | /// 159 | /// The dummy LLVM writable entity. 160 | /// 161 | #[derive(Debug, Default, Clone)] 162 | pub struct DummyLLVMWritable {} 163 | 164 | impl WriteLLVM for DummyLLVMWritable { 165 | fn into_llvm(self, _context: &mut Context) -> anyhow::Result<()> { 166 | Ok(()) 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/evm/warning.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! EVM target warning. 3 | //! 4 | 5 | /// 6 | /// EVM target warning. 7 | /// 8 | #[derive(Debug, thiserror::Error, Clone, serde::Serialize, serde::Deserialize)] 9 | pub enum Warning { 10 | /// Deploy code size warning. 11 | #[error( 12 | "{0} bytecode size is {found}B that exceeds the EVM limit of {1}B", 13 | era_compiler_common::CodeSegment::Deploy, 14 | crate::evm::r#const::DEPLOY_CODE_SIZE_LIMIT 15 | )] 16 | DeployCodeSize { 17 | /// Bytecode size. 18 | found: usize, 19 | }, 20 | 21 | /// Runtime code size warning. 22 | #[error( 23 | "{0} bytecode size is {found}B that exceeds the EVM limit of {1}B", 24 | era_compiler_common::CodeSegment::Runtime, 25 | crate::evm::r#const::RUNTIME_CODE_SIZE_LIMIT 26 | )] 27 | RuntimeCodeSize { 28 | /// Bytecode size. 29 | found: usize, 30 | }, 31 | } 32 | 33 | impl Warning { 34 | /// 35 | /// Warning code. 36 | /// 37 | /// Mimic `solc` warning codes where possible for compatibility. 38 | /// 39 | pub fn code(&self) -> Option { 40 | match self { 41 | Self::DeployCodeSize { .. } => Some(3860), 42 | Self::RuntimeCodeSize { .. } => Some(5574), 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/optimizer/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM optimizing tools. 3 | //! 4 | 5 | pub mod settings; 6 | 7 | use crate::target_machine::TargetMachine; 8 | 9 | use self::settings::Settings; 10 | 11 | /// 12 | /// The LLVM optimizing tools. 13 | /// 14 | #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] 15 | pub struct Optimizer { 16 | /// The optimizer settings. 17 | settings: Settings, 18 | } 19 | 20 | impl Optimizer { 21 | /// 22 | /// A shortcut constructor. 23 | /// 24 | pub fn new(settings: Settings) -> Self { 25 | Self { settings } 26 | } 27 | 28 | /// 29 | /// Runs the new pass manager. 30 | /// 31 | pub fn run( 32 | &self, 33 | target_machine: &TargetMachine, 34 | module: &inkwell::module::Module, 35 | ) -> Result<(), inkwell::support::LLVMString> { 36 | target_machine.run_optimization_passes( 37 | module, 38 | format!("default", self.settings.middle_end_as_char()).as_str(), 39 | ) 40 | } 41 | 42 | /// 43 | /// Returns the optimizer settings reference. 44 | /// 45 | pub fn settings(&self) -> &Settings { 46 | &self.settings 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/optimizer/settings/size_level.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM optimizer settings size level. 3 | //! 4 | 5 | /// 6 | /// The LLVM optimizer settings size level. 7 | /// 8 | #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] 9 | pub enum SizeLevel { 10 | /// No size optimizations. 11 | Zero, 12 | /// The default size optimizations. 13 | S, 14 | /// The aggresize size optimizations. 15 | Z, 16 | } 17 | 18 | impl From for u32 { 19 | fn from(level: SizeLevel) -> Self { 20 | match level { 21 | SizeLevel::Zero => 0, 22 | SizeLevel::S => 1, 23 | SizeLevel::Z => 2, 24 | } 25 | } 26 | } 27 | 28 | impl std::fmt::Display for SizeLevel { 29 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 30 | match self { 31 | SizeLevel::Zero => write!(f, "0"), 32 | SizeLevel::S => write!(f, "s"), 33 | SizeLevel::Z => write!(f, "z"), 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/target_machine.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! The LLVM target machine. 3 | //! 4 | 5 | use crate::optimizer::settings::size_level::SizeLevel as OptimizerSettingsSizeLevel; 6 | use crate::optimizer::settings::Settings as OptimizerSettings; 7 | 8 | /// 9 | /// The LLVM target machine. 10 | /// 11 | #[derive(Debug)] 12 | pub struct TargetMachine { 13 | /// The LLVM target. 14 | target: era_compiler_common::Target, 15 | /// The LLVM target machine reference. 16 | target_machine: inkwell::targets::TargetMachine, 17 | /// The optimizer settings. 18 | optimizer_settings: OptimizerSettings, 19 | } 20 | 21 | impl TargetMachine { 22 | /// The LLVM target name. 23 | pub const VM_TARGET_NAME: &'static str = "eravm"; 24 | 25 | /// The LLVM target triple. 26 | pub const VM_TARGET_TRIPLE: &'static str = "eravm-unknown-unknown"; 27 | 28 | /// 29 | /// A shortcut constructor. 30 | /// 31 | /// Supported LLVM options: 32 | /// `-eravm-disable-sha3-sreq-cse` 33 | /// `-eravm-jump-table-density-threshold ` 34 | /// 35 | pub fn new( 36 | target: era_compiler_common::Target, 37 | optimizer_settings: &OptimizerSettings, 38 | llvm_options: &[String], 39 | ) -> anyhow::Result { 40 | let mut arguments = Vec::with_capacity(1 + llvm_options.len()); 41 | arguments.push(target.to_string()); 42 | arguments.extend_from_slice(llvm_options); 43 | if arguments.len() > 1 { 44 | let arguments: Vec<&str> = arguments.iter().map(|argument| argument.as_str()).collect(); 45 | inkwell::support::parse_command_line_options(arguments.as_slice(), "LLVM options"); 46 | } 47 | 48 | let target_machine = inkwell::targets::Target::from_name(target.to_string().as_str()) 49 | .ok_or_else(|| anyhow::anyhow!("LLVM target machine `{target}` not found"))? 50 | .create_target_machine( 51 | &inkwell::targets::TargetTriple::create(target.triple()), 52 | "", 53 | "", 54 | optimizer_settings.level_back_end, 55 | inkwell::targets::RelocMode::Default, 56 | inkwell::targets::CodeModel::Default, 57 | ) 58 | .ok_or_else(|| { 59 | anyhow::anyhow!("LLVM target machine `{target}` initialization error") 60 | })?; 61 | 62 | Ok(Self { 63 | target, 64 | target_machine, 65 | optimizer_settings: optimizer_settings.to_owned(), 66 | }) 67 | } 68 | 69 | /// 70 | /// Sets the target-specific data in the module. 71 | /// 72 | pub fn set_target_data(&self, module: &inkwell::module::Module) { 73 | module.set_triple(&self.target_machine.get_triple()); 74 | module.set_data_layout(&self.target_machine.get_target_data().get_data_layout()); 75 | } 76 | 77 | /// 78 | /// Sets the assembly printer verbosity. 79 | /// 80 | pub fn set_asm_verbosity(&self, verbosity: bool) { 81 | self.target_machine.set_asm_verbosity(verbosity); 82 | } 83 | 84 | /// 85 | /// Translates textual assembly to the object code. 86 | /// 87 | pub fn assemble( 88 | &self, 89 | memory_buffer: &inkwell::memory_buffer::MemoryBuffer, 90 | ) -> Result { 91 | memory_buffer.assemble_eravm(&self.target_machine) 92 | } 93 | 94 | /// 95 | /// Disassembles bytecode into textual representation. 96 | /// 97 | pub fn disassemble( 98 | &self, 99 | memory_buffer: &inkwell::memory_buffer::MemoryBuffer, 100 | pc: u64, 101 | options: u64, 102 | ) -> Result { 103 | memory_buffer.disassemble_eravm(&self.target_machine, pc, options) 104 | } 105 | 106 | /// 107 | /// Writes the LLVM module to a memory buffer. 108 | /// 109 | pub fn write_to_memory_buffer( 110 | &self, 111 | module: &inkwell::module::Module, 112 | file_type: inkwell::targets::FileType, 113 | ) -> Result { 114 | self.target_machine 115 | .write_to_memory_buffer(module, file_type) 116 | } 117 | 118 | /// 119 | /// Runs the optimization passes on `module`. 120 | /// 121 | pub fn run_optimization_passes( 122 | &self, 123 | module: &inkwell::module::Module, 124 | passes: &str, 125 | ) -> Result<(), inkwell::support::LLVMString> { 126 | let pass_builder_options = inkwell::passes::PassBuilderOptions::create(); 127 | pass_builder_options.set_verify_each(self.optimizer_settings.is_verify_each_enabled); 128 | pass_builder_options.set_debug_logging(self.optimizer_settings.is_debug_logging_enabled); 129 | 130 | if let era_compiler_common::Target::EraVM = self.target { 131 | pass_builder_options.set_loop_unrolling( 132 | self.optimizer_settings.level_middle_end_size == OptimizerSettingsSizeLevel::Zero, 133 | ); 134 | pass_builder_options.set_merge_functions(true); 135 | } 136 | 137 | module.run_passes(passes, &self.target_machine, pass_builder_options) 138 | } 139 | 140 | /// 141 | /// Returns the target triple. 142 | /// 143 | pub fn get_triple(&self) -> inkwell::targets::TargetTriple { 144 | self.target_machine.get_triple() 145 | } 146 | 147 | /// 148 | /// Returns the target data. 149 | /// 150 | pub fn get_target_data(&self) -> inkwell::targets::TargetData { 151 | self.target_machine.get_target_data() 152 | } 153 | } 154 | --------------------------------------------------------------------------------