Compare commits

..

129 Commits

Author SHA1 Message Date
DavidK 3184397c43 cargo fmt 2025-01-06 16:29:59 +02:00
DavidK 9c44eeac06 Adds import callback resolution to resolc compiler 2025-01-06 16:09:53 +02:00
Cyrill Leutwiler f49d145e9a update the minium supported rust version to 1.81 (#144)
Signed-off-by: xermicus <cyrill@parity.io>
2024-12-21 09:10:14 +01:00
Cyrill Leutwiler 952c5cc894 resolc-0.1.0-dev.7 (#143)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-12-20 14:33:08 +01:00
Cyrill Leutwiler d8752ec6b5 the basefee opcode (#142)
Signed-off-by: xermicus <cyrill@parity.io>
2024-12-19 18:59:10 +01:00
Cyrill Leutwiler 6ad846a285 the base fee opcode (#141)
Signed-off-by: xermicus <cyrill@parity.io>
2024-12-19 12:44:15 +01:00
Cyrill Leutwiler 3f9771f838 update and fix the linker (#140) 2024-12-18 23:11:06 +01:00
Cyrill Leutwiler 22070a824d the gas limit opcode (#139)
Signed-off-by: xermicus <cyrill@parity.io>
2024-12-18 20:31:42 +01:00
Cyrill Leutwiler d299dd1a19 change getters to register version (#138) 2024-12-18 17:56:37 +01:00
Cyrill Leutwiler 14a598e840 implement the gas opcode (#136) 2024-12-18 17:19:32 +01:00
Cyrill Leutwiler 55ec0988e8 fix the CI runner version (#137)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-12-18 17:03:29 +01:00
Cyrill Leutwiler 909de515c4 Allow arbitrary call data size (#135)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-12-18 14:04:46 +01:00
Sebastian Miasojed afe44ad21b JS: Fix encoding conversion from utf16 to utf8 (#131) 2024-12-11 09:23:02 +01:00
Cyrill Leutwiler 2cb8f82266 calls: supply max ref_time and proof_size limits (#133)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-12-10 16:31:44 +01:00
Cyrill Leutwiler 6f2f158ef1 fix new clippy lint (#132)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-12-10 15:24:25 +01:00
Cyrill Leutwiler 4e5482bad2 fix the commit sha in the version (#129)
Signed-off-by: xermicus <cyrill@parity.io>
2024-12-03 12:34:17 +01:00
Cyrill Leutwiler 0110258d81 release 0.1.0 dev.6 (#128) 2024-11-29 17:45:47 +01:00
Cyrill Leutwiler f0d9d44dce Implement the gasprice opcode (#127) 2024-11-29 17:14:27 +01:00
Cyrill Leutwiler 3f6cd115ee update warnings (#126)
Signed-off-by: xermicus <cyrill@parity.io>
2024-11-29 16:23:36 +01:00
Cyrill Leutwiler 7c00bbb0fc remove unneeded utils (#125)
Signed-off-by: xermicus <cyrill@parity.io>
2024-11-29 16:21:24 +01:00
Cyrill Leutwiler 423a494621 Switch target to 64bit and enable the zbb feature (#120) 2024-11-29 15:56:10 +01:00
Cyrill Leutwiler 08112e3449 Fix broken link in README.md 2024-11-29 10:39:42 +01:00
Jeeyong Um db6ca1fcfa Fix broken link to introduction in README.md (#124) 2024-11-29 10:38:09 +01:00
Sebastian Miasojed 54d154a73c Merge pull request #123 from smiasojed/web
Add web worker compatibility
2024-11-29 10:22:59 +01:00
dependabot[bot] 93f1deb6a5 Bump rustls from 0.23.17 to 0.23.18 (#121)
Bumps [rustls](https://github.com/rustls/rustls) from 0.23.17 to 0.23.18.
- [Release notes](https://github.com/rustls/rustls/releases)
- [Changelog](https://github.com/rustls/rustls/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rustls/rustls/compare/v/0.23.17...v/0.23.18)

---
updated-dependencies:
- dependency-name: rustls
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-29 09:50:29 +01:00
Sebastian Miasojed 9a150b13f3 Remove hardcoded soljson 2024-11-28 16:54:27 +01:00
Sebastian Miasojed 677cef9c0f Remove comment 2024-11-27 15:30:49 +01:00
Sebastian Miasojed d25be523c1 Update deps 2024-11-27 15:28:04 +01:00
Sebastian Miasojed 6a4fd1e991 Add web worker compatibility 2024-11-27 15:17:26 +01:00
Sebastian Miasojed 81915ddbcb Merge pull request #122 from smiasojed/sync
Remove async JS calls
2024-11-27 14:03:26 +01:00
Sebastian Miasojed 0f25bac4bd Fix CI 2024-11-27 08:49:07 +01:00
Sebastian Miasojed 7d41495587 Fmt 2024-11-27 08:30:07 +01:00
Sebastian Miasojed d6d5acfcce Remove deps on solc 2024-11-26 22:35:18 +01:00
Sebastian Miasojed 130ac48bf0 Fix revive wasm CI 2024-11-26 22:11:26 +01:00
Sebastian Miasojed b65fa2e42c Remove async calls from revive 2024-11-26 22:03:57 +01:00
Sebastian Miasojed 1eb1083d40 Merge pull request #111 from smiasojed/resolc.js
Add compilation to NodeJS module
2024-11-26 14:44:44 +01:00
Sebastian Miasojed 4c0c74f7f4 Fix npm build 2024-11-25 15:36:36 +01:00
Sebastian Miasojed 229c0d452d Merge remote-tracking branch 'origin/main' into resolc.js 2024-11-25 10:54:44 +01:00
Sebastian Miasojed 4e024655a1 Add support for esm and cjs modules 2024-11-25 10:26:22 +01:00
Ermal Kaleci 01b5ed5ba3 Implement delegate_call (#80) 2024-11-23 18:05:21 +01:00
Sebastian Miasojed 892c9b5fbe Update clone-llvm.sh
Co-authored-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-11-22 19:52:06 +01:00
xermicus 84d47fa738 fix missing semicolon
Signed-off-by: xermicus <cyrill@parity.io>
2024-11-22 16:14:07 +01:00
Cyrill Leutwiler dbb47fd13e experimental: support for debug info (#118)
Signed-off-by: wpt967 <matt.aw@parity.io>
Signed-off-by: xermicus <cyrill@parity.io>
2024-11-22 08:56:09 +01:00
Cyrill Leutwiler 87f2bcefb3 dump the elf shared object into the debug output directory (#119)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-11-21 21:48:42 +01:00
Sebastian Miasojed 05925f25f1 Update comment 2024-11-21 13:57:00 +01:00
Sebastian Miasojed 8990b2a486 Update emscripten-build-llvm.sh
Co-authored-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-11-21 12:49:23 +01:00
Cyrill Leutwiler 89ddfb28c8 CI: ignore the broken test (#116)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-11-21 12:22:15 +01:00
Sebastian Miasojed 32d4b2309c Fix build script 2024-11-21 10:36:12 +01:00
Sebastian Miasojed a772ff1354 Fix build path 2024-11-21 10:29:21 +01:00
Sebastian Miasojed 0a82addf02 Add directory arg support to clone-llvm 2024-11-20 17:15:55 +01:00
Sebastian Miasojed 491850760f Update readme file 2024-11-20 12:57:41 +01:00
Sebastian Miasojed fcbe00f4a9 Merge branch 'main' into resolc.js 2024-11-20 12:51:10 +01:00
Sebastian Miasojed 77406264ea Improve description 2024-11-20 12:42:29 +01:00
Sebastian Miasojed cdebf69fc0 Fix stdin support 2024-11-20 10:54:15 +01:00
Sebastian Miasojed f36d62ca73 Add stdin support 2024-11-20 10:27:57 +01:00
Sebastian Miasojed 39e504703e Add test with standard json args 2024-11-18 16:52:56 +01:00
Sebastian Miasojed 63da7212a1 Add worker code to resolc.js 2024-11-18 14:49:35 +01:00
Sebastian Miasojed 3072c03600 Fix parallel feature 2024-11-18 13:59:48 +01:00
Sebastian Miasojed d88ddb25bd Refactor soljson compiler 2024-11-18 12:19:36 +01:00
Sebastian Miasojed 87dd77b784 Remove libsolc crate 2024-11-18 11:07:45 +01:00
Sebastian Miasojed cece20deb1 Cleanup 2024-11-18 10:18:21 +01:00
Sebastian Miasojed f57ab96eed Revert changes in llvm build 2024-11-15 16:08:36 +01:00
Cyrill Leutwiler 3232382d96 update dependencies (#115)
Signed-off-by: xermicus <cyrill@parity.io>
2024-11-15 13:50:55 +01:00
Cyrill Leutwiler 6a120463c2 implement the blockhash opcode (#114) 2024-11-15 13:11:04 +01:00
Sebastian Miasojed 881b88354c Rename compiler to solc 2024-11-14 17:09:03 +01:00
Cyrill Leutwiler c9dd347755 Add documentation portal (#99) 2024-11-14 14:21:48 +01:00
Sebastian Miasojed 140545ea15 Fix CI 2024-11-14 11:49:20 +01:00
Sebastian Miasojed 010a2ed223 Fmt 2024-11-14 11:44:48 +01:00
Sebastian Miasojed 563864dd25 Fix CI 2024-11-14 11:26:20 +01:00
Sebastian Miasojed ce8bf3d9ef Apply suggestions from previous review 2024-11-14 11:20:33 +01:00
Cyrill Leutwiler f947984671 update 64bit target flags (#113) 2024-11-14 10:29:21 +01:00
Sebastian Miasojed 14991f40ac Fix CI for wasm path 2024-11-13 15:34:41 +01:00
Sebastian Miasojed 6d16790f83 Update GHA 2024-11-12 17:22:15 +01:00
Sebastian Miasojed 64fefe76b5 Removed not needed libs from linking process 2024-11-12 16:07:21 +01:00
Sebastian Miasojed f59b47df7b Add ltinfo dep to GHA 2024-11-12 12:52:11 +01:00
Sebastian Miasojed 6e6fe20c71 Add Missing dep to GHA 2024-11-12 09:52:06 +01:00
Sebastian Miasojed 93d2f3b9d9 Log LLVM version in GHA 2024-11-12 09:47:43 +01:00
Sebastian Miasojed 8a225871ee Fix deps in GHA 2024-11-08 15:53:27 +01:00
Sebastian Miasojed 677aedc6f3 Switch GHA to ubuntu 2024-11-08 15:45:52 +01:00
Sebastian Miasojed 90423ffb6b Remove llvm-15 env from GHA 2024-11-08 15:36:00 +01:00
Sebastian Miasojed 255176978a Remove old llvm-15 from GHA 2024-11-08 15:27:11 +01:00
Sebastian Miasojed 9a8003afbf Install llvm in GHA 2024-11-08 15:10:42 +01:00
Sebastian Miasojed 7f3d0cecb2 Update cmake versions in GHA 2024-11-08 15:00:14 +01:00
Sebastian Miasojed 18376432f1 Fix GHA emsdk path 2024-11-08 14:01:18 +01:00
Sebastian Miasojed 007b79ee62 Use bash in GHA 2024-11-08 13:51:51 +01:00
Sebastian Miasojed 8c7d18aec7 Update GHA for wasm build 2024-11-08 13:46:00 +01:00
Sebastian Miasojed c0a82ce6d2 Update wasm GHA 2024-11-08 13:38:26 +01:00
Sebastian Miasojed c51d50bc88 Add GHA for wasm target 2024-11-08 13:28:45 +01:00
Sebastian Miasojed 94445bab93 Fix compilation for wasm target 2024-11-08 12:06:11 +01:00
Sebastian Miasojed a934ec204e Add temoprary wasm compilation output 2024-11-08 10:55:26 +01:00
Sebastian Miasojed b6baf6cfd9 Make wasm version to compile 2024-11-08 09:54:10 +01:00
Sebastian Miasojed b7b28efded Update solc compiler version check 2024-11-07 15:30:00 +01:00
Sebastian Miasojed d260472330 Fmt 2024-11-07 15:15:52 +01:00
Sebastian Miasojed 4f6debcbe3 Make native version to compaile again 2024-11-07 15:04:35 +01:00
Sebastian Miasojed 9b23e19479 Fix Cargo.toml 2024-11-07 09:43:44 +01:00
Sebastian Miasojed 88a888d138 Merge remote-tracking branch 'origin/main' into resolc.js 2024-11-06 15:04:34 +01:00
Cyrill Leutwiler 4cce4a729e pre-release v0.1.0-dev.5 (#110) 2024-10-31 15:22:34 +01:00
Cyrill Leutwiler 9267a2af02 use the long revive version string in the contract metadata (#109) 2024-10-31 14:52:06 +01:00
Cyrill Leutwiler 173ace72cb solidity: rename the revive metadata (#106)
Rename the revive metadata fields and includes the commit hash and LLVM version in the revive version (similar to what solc does).

Signed-off-by: xermicus <cyrill@parity.io>
2024-10-31 13:00:35 +01:00
Cyrill Leutwiler 37ab2b6782 declare immutable globals during in declare (#108)
Signed-off-by: xermicus <cyrill@parity.io>
2024-10-31 12:07:39 +01:00
Cyrill Leutwiler 43d2ef3ce9 implement the code size opcodes (#107) 2024-10-31 11:46:47 +01:00
Cyrill Leutwiler 68564f9866 display the git revision version information (#105)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-10-30 14:28:00 +01:00
Cyrill Leutwiler 36d9317831 update the polkadot-sdk and polkavm dependencies (#104)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-10-30 09:28:24 +01:00
Cyrill Leutwiler 5b3b90db83 support the origin opcode (#103)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-10-29 18:40:25 +01:00
Cyrill Leutwiler a4043ecde7 update LLVM target features (#102)
Signed-off-by: xermicus <cyrill@parity.io>
2024-10-29 18:17:29 +01:00
Cyrill Leutwiler f985f42370 update polkadot-sdk and inkwell dependencies (#101)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-10-29 11:27:44 +01:00
Ermal Kaleci aae25107a2 support full storage key space (#100)
- The storage pointer values will no longer be truncated to the register size, allowing for the use of arbitrary storage keys
- Failed storage value reads will now guarantee to return the zero value
2024-10-28 10:18:11 +01:00
Sebastian Miasojed cc38c37481 Set evm.deployedBytecode to the value of evm.bytecode (#95) 2024-10-24 14:59:50 +02:00
Cyrill Leutwiler e56feb95be standard json output: skip serializing keys instead of emitting a null object (#52)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-10-23 15:28:28 +02:00
Cyrill Leutwiler 995b115c5f bump polkadot-sdk and polkavm (#94)
Signed-off-by: xermicus <cyrill@parity.io>
2024-10-22 19:50:58 +02:00
Ermal Kaleci ee83d28a51 Implement extcodehash (#77) 2024-10-21 10:13:50 +02:00
Cyrill Leutwiler 82ae22c163 tidy up the runtime API crate (#85)
- remove unused runtime API imports and constants
- move runtime api symbols into the revive-runtime-api crate

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-10-17 10:55:27 +02:00
Cyrill Leutwiler 20e3560398 Makefile: remove duplicate workspace tests from the test target (#90)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-10-17 10:22:04 +02:00
Cyrill Leutwiler 5bf31db660 bump polkavm and polkadot-sdk (#87)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-10-15 10:46:20 +02:00
Cyrill Leutwiler f0f344a139 disable sbrk and emulate EVM linear memory internally (#76)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
Signed-off-by: xermicus <cyrill@parity.io>
2024-10-14 15:20:00 +02:00
Cyrill Leutwiler 92a18460e4 support solc 0.8.28 (#84)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-10-14 13:25:18 +02:00
Cyrill Leutwiler e5233fc46e revive-runner: consider non-reverted transactions as success (#82)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-10-14 13:24:59 +02:00
Ermal Kaleci adda5a0f12 improve runtime api return value names (#81) 2024-10-14 08:56:26 +02:00
Ermal Kaleci d9842b5427 Fix implementation for balance_of (#79)
The balance_of syscall is now available in pallet-revive.
- Fix balance_of implementation to use correct runtime api
- Add build_address_argument_store helper to be used for address arguments
2024-10-12 12:35:10 +02:00
Cyrill Leutwiler 6335c34a2b remove obsolete runtime builtins
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-10-11 15:58:28 +02:00
Cyrill Leutwiler 49d2157a84 remove near calls
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-10-11 15:58:28 +02:00
Cyrill Leutwiler e131eebf50 remove system mode and request memoization
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
2024-10-11 15:58:28 +02:00
Cyrill Leutwiler 705d1d7866 run signed_remainder test in differential mode (#75) 2024-10-10 19:26:04 +02:00
Cyrill Leutwiler d5d419cefc implement immutable variables codegen (#70) 2024-10-10 13:33:00 +02:00
Cyrill Leutwiler 8b7fe8e3d7 deny clippy warnings by default (#73) 2024-10-09 20:19:12 +02:00
xermicus b344e0cff5 Revert "implement immutable variables codegen"
This reverts commit 0e6a6d12c3.
2024-10-09 03:43:05 +02:00
xermicus 0e6a6d12c3 implement immutable variables codegen
Signed-off-by: xermicus <cyrill@parity.io>
2024-10-09 03:37:56 +02:00
Cyrill Leutwiler 77fe683f18 remove the test encoding option (#65) 2024-10-04 09:59:27 +02:00
Sebastian Miasojed 5ac67bdc0d Initial wasm support 2024-08-29 17:28:31 +02:00
169 changed files with 6018 additions and 3451 deletions
+2 -2
View File
@@ -9,12 +9,12 @@ env:
DEBIAN_CONTAINER_RUNNER: run-debian-builder.sh
REVIVE_DEBIAN_INSTALL: ${{ github.workspace }}/target/release
REVIVE_DEBIAN_BINARY: resolc
RUST_VERSION: "1.80"
RUST_VERSION: "1.81"
jobs:
build-revive-debian-x86:
name: debian-container-x86
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
+79
View File
@@ -0,0 +1,79 @@
name: Build revive-wasm
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
workflow_dispatch:
env:
CARGO_TERM_COLOR: always
REVIVE_WASM_INSTALL_DIR: ${{ github.workspace }}/target/wasm32-unknown-emscripten/release
EMSCRIPTEN_VERSION: 3.1.64
jobs:
build-revive-wasm:
runs-on: ubuntu-22.04
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
- name: Cache LLVM build
id: cache-llvm
uses: actions/cache@v3
with:
path: |
llvm18.0-emscripten
# Use a unique key based on LLVM version or configuration files to avoid cache invalidation
key: llvm-build-${{ runner.os }}-${{ hashFiles('clone-llvm.sh', 'emscripten-build-llvm.sh') }}
- name: Install Dependencies
run: |
sudo apt-get update && sudo apt-get install -y cmake ninja-build libncurses5
rustup target add wasm32-unknown-emscripten
# Install LLVM required for the compiler runtime, runtime-api and stdlib
curl -sSL --output llvm.tar.xz https://github.com/llvm/llvm-project/releases/download/llvmorg-18.1.4/clang+llvm-18.1.4-x86_64-linux-gnu-ubuntu-18.04.tar.xz
tar Jxf llvm.tar.xz
mv clang+llvm-18.1.4-x86_64-linux-gnu-ubuntu-18.04 llvm18/
echo "$(pwd)/llvm18/bin" >> $GITHUB_PATH
# Install Emscripten
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install ${{ env.EMSCRIPTEN_VERSION }}
./emsdk activate ${{ env.EMSCRIPTEN_VERSION }}
- run: |
rustup show
cargo --version
rustup +nightly show
cargo +nightly --version
cmake --version
bash --version
llvm-config --version
- name: Build LLVM
if: steps.cache-llvm.outputs.cache-hit != 'true'
run: |
export EMSDK_ROOT=${PWD}/emsdk
./emscripten-build-llvm.sh
- name: Use Cached LLVM
if: steps.cache-llvm.outputs.cache-hit == 'true'
run: |
echo "Using cached LLVM"
- name: Build revive
run: |
export LLVM_LINK_PREFIX=${PWD}/llvm18.0-emscripten
source ./emsdk/emsdk_env.sh
make install-wasm
- uses: actions/upload-artifact@v4
with:
name: revive-wasm
path: |
${{ env.REVIVE_WASM_INSTALL_DIR }}/resolc.js
${{ env.REVIVE_WASM_INSTALL_DIR }}/resolc.wasm
retention-days: 1
+2 -2
View File
@@ -11,7 +11,7 @@ env:
jobs:
build-ubuntu-x86:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
@@ -19,7 +19,7 @@ jobs:
- name: Install solc
run: |
mkdir -p solc
curl -sSL --output solc/solc https://github.com/ethereum/solidity/releases/download/v0.8.27/solc-static-linux
curl -sSL --output solc/solc https://github.com/ethereum/solidity/releases/download/v0.8.28/solc-static-linux
chmod +x solc/solc
echo "$(pwd)/solc/" >> $GITHUB_PATH
+2
View File
@@ -8,9 +8,11 @@
/*.s
/llvm-project
/llvm18.0
/llvm18.0-emscripten
node_modules
artifacts
tmp
package-lock.json
/*.html
/build
soljson.js
+67
View File
@@ -0,0 +1,67 @@
# Changelog
## Unreleased
## v0.1.0-dev.7
This is a development pre-release.
### Added
- Implement the `GASPRICE` opcode.
- Implement the `BASEFEE` opcode.
- Implement the `GASLIMIT` opcode.
### Changed
- The `GAS` opcode now returns the remaining `ref_time`.
- Contracts can now be supplied call data input of arbitrary size.
- Some syscalls now return the value in a register, slightly improving emitted contract code.
- Calls forward maximum weight limits instead of 0, anticipating a change in polkadot-sdk where weight limits of 0 no longer interprets as uncapped limit.
### Fixed
- A linker bug which was preventing certain contracts from linking with the PVM linker.
- JS: Fix encoding conversion from JS string (UTF-16) to UTF-8.
- The git commit hash slug is always displayed in the version string.
## v0.1.0-dev.6
This is a development pre-release.
# Added
- Implement the `BLOCKHASH` opcode.
- Implement delegate calls.
- Implement the `GASPRICE` opcode. Currently hard-coded to return `1`.
- The ELF shared object contract artifact is dumped into the debug output directory.
- Initial support for emitting debug info (opt in via the `-g` flag)
# Changed
- resolc now emits 64bit PolkaVM blobs, reducing contract code size and execution time.
- The RISC-V bit-manipulation target feature (`zbb`) is enabled.
# Fixed
- Compilation to Wasm (for usage in node and web browsers)
## v0.1.0-dev.5
This is development pre-release.
# Added
- Implement the `CODESIZE` and `EXTCODESIZE` opcodes.
# Changed
- Include the full revive version in the contract metadata.
# Fixed
## v0.1.0-dev-4
This is development pre-release.
# Added
- Support the `ORIGIN` opcode.
# Changed
- Update polkavm to `v0.14.0`.
- Enable the `a`, `fast-unaligned-access` and `xtheadcondmov` LLVM target features, decreasing the code size for some contracts.
# Fixed
Generated
+1953 -1184
View File
File diff suppressed because it is too large Load Diff
+22 -20
View File
@@ -3,7 +3,7 @@ resolver = "2"
members = ["crates/*"]
[workspace.package]
version = "0.1.0"
version = "0.1.0-dev.7"
authors = [
"Cyrill Leutwiler <cyrill@parity.io>",
"Parity Technologies <admin@parity.io>",
@@ -11,21 +11,21 @@ authors = [
license = "MIT/Apache-2.0"
edition = "2021"
repository = "https://github.com/paritytech/revive"
rust-version = "1.80.0"
rust-version = "1.81.0"
[workspace.dependencies]
revive-benchmarks = { version = "0.1.0", path = "crates/benchmarks" }
revive-builtins = { version = "0.1.0", path = "crates/builtins" }
revive-common = { version = "0.1.0", path = "crates/common" }
revive-differential = { version = "0.1.0", path = "crates/differential" }
revive-integration = { version = "0.1.0", path = "crates/integration" }
revive-linker = { version = "0.1.0", path = "crates/linker" }
lld-sys = { version = "0.1.0", path = "crates/lld-sys" }
revive-llvm-context = { version = "0.1.0", path = "crates/llvm-context" }
revive-runtime-api = { version = "0.1.0", path = "crates/runtime-api" }
revive-runner = { version = "0.1.0", path = "crates/runner" }
revive-solidity = { version = "0.1.0", path = "crates/solidity" }
revive-stdlib = { version = "0.1.0", path = "crates/stdlib" }
revive-benchmarks = { version = "0.1.0-dev.7", path = "crates/benchmarks" }
revive-builtins = { version = "0.1.0-dev.7", path = "crates/builtins" }
revive-common = { version = "0.1.0-dev.7", path = "crates/common" }
revive-differential = { version = "0.1.0-dev.7", path = "crates/differential" }
revive-integration = { version = "0.1.0-dev.7", path = "crates/integration" }
revive-linker = { version = "0.1.0-dev.7", path = "crates/linker" }
lld-sys = { version = "0.1.0-dev.7", path = "crates/lld-sys" }
revive-llvm-context = { version = "0.1.0-dev.7", path = "crates/llvm-context" }
revive-runtime-api = { version = "0.1.0-dev.7", path = "crates/runtime-api" }
revive-runner = { version = "0.1.0-dev.7", path = "crates/runner" }
revive-solidity = { version = "0.1.0-dev.7", path = "crates/solidity" }
revive-stdlib = { version = "0.1.0-dev.7", path = "crates/stdlib" }
hex = "0.4"
petgraph = "0.6"
@@ -51,10 +51,10 @@ path-slash = "0.2"
rayon = "1.8"
clap = { version = "4", default-features = false, features = ["derive"] }
rand = "0.8"
polkavm-common = "0.10"
polkavm-linker = "0.10"
polkavm-disassembler = "0.10"
polkavm = "0.10"
polkavm-common = "0.18"
polkavm-linker = "0.18"
polkavm-disassembler = "0.18"
polkavm = "0.18"
alloy-primitives = { version = "0.8", features = ["serde"] }
alloy-sol-types = "0.8"
alloy-genesis = "0.3"
@@ -63,15 +63,17 @@ env_logger = { version = "0.10.0", default-features = false }
serde_stacker = "0.1"
criterion = { version = "0.5", features = ["html_reports"] }
log = { version = "0.4" }
git2 = "0.19.0"
# polkadot-sdk and friends
codec = { version = "3.6.12", default-features = false, package = "parity-scale-codec" }
scale-info = { version = "2.11.1", default-features = false }
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk", rev = "c77095f51119d2eccdc54d2f3518bed0ffbd6d53" }
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk", rev = "243b751abbb94369bbd92c83d8ab159ddfc3c556" }
# llvm
[workspace.dependencies.inkwell]
version = "0.5"
git = "https://github.com/TheDan64/inkwell.git"
rev = "7b410298b6a93450adaa90b1841d5805a3038f12"
default-features = false
features = ["serde", "llvm18-0", "no-libffi-linking", "target-riscv"]
+27 -5
View File
@@ -1,5 +1,22 @@
.PHONY: install format test test-solidity test-cli test-integration test-workspace clean docs docs-build
RUSTFLAGS_EMSCRIPTEN := \
-C link-arg=-sEXPORTED_FUNCTIONS=_main,_free,_malloc \
-C link-arg=-sNO_INVOKE_RUN=1 \
-C link-arg=-sEXIT_RUNTIME=1 \
-C link-arg=-sALLOW_MEMORY_GROWTH=1 \
-C link-arg=-sEXPORTED_RUNTIME_METHODS=FS,callMain,stringToNewUTF8 \
-C link-arg=-sMODULARIZE=1 \
-C link-arg=-sEXPORT_NAME=createRevive \
-C link-arg=-sWASM_ASYNC_COMPILATION=0 \
-C link-arg=-sDYNAMIC_EXECUTION=0 \
-C link-arg=-sALLOW_TABLE_GROWTH=1 \
-C link-arg=--js-library=js/embed/soljson_interface.js \
-C link-arg=--pre-js=js/embed/pre.js \
-C link-arg=-sNODEJS_CATCH_EXIT=0 \
-C link-arg=-sDISABLE_EXCEPTION_CATCHING=0 \
-C opt-level=3
install: install-bin install-npm
install-bin:
@@ -8,6 +25,10 @@ install-bin:
install-npm:
npm install && npm fund
install-wasm:
RUSTFLAGS='$(RUSTFLAGS_EMSCRIPTEN)' cargo build --target wasm32-unknown-emscripten -p revive-solidity --release --no-default-features
npm install
# install-revive: Build and install to the directory specified in REVIVE_INSTALL_DIR
ifeq ($(origin REVIVE_INSTALL_DIR), undefined)
REVIVE_INSTALL_DIR=`pwd`/release/revive-debian
@@ -18,8 +39,10 @@ install-revive:
format:
cargo fmt --all --check
clippy:
cargo clippy --all-features --workspace --tests --benches -- --deny warnings --allow dead_code
test: format clippy test-cli test-workspace
cargo test --workspace
test-integration: install-bin
cargo test --package revive-integration
@@ -45,9 +68,6 @@ bench: install-bin
cargo criterion --all --all-features --message-format=json \
| criterion-table > crates/benchmarks/BENCHMARKS.md
clippy:
cargo clippy --all-features --workspace --tests --benches
docs: docs-build
mdbook serve --open docs/
@@ -59,4 +79,6 @@ clean:
rm -rf node_modules ; \
rm -rf crates/solidity/src/tests/cli-tests/artifacts ; \
cargo uninstall revive-solidity ; \
rm -f package-lock.json
rm -f package-lock.json ; \
rm -rf js/dist ; \
rm -f js/src/resolc.{wasm,js}
+37 -4
View File
@@ -1,14 +1,15 @@
![CI](https://github.com/paritytech/revive/actions/workflows/rust.yml/badge.svg)
[![Docs](https://img.shields.io/badge/Docs-contracts.polkadot.io-brightgreen.svg)](https://contracts.polkadot.io)
# revive
YUL and EVM assembly recompiler to LLVM, targetting RISC-V on [PolkaVM](https://github.com/koute/polkavm).
[Frontend](https://github.com/matter-labs/era-compiler-solidity) and [code generator](https://github.com/matter-labs/era-compiler-llvm-context) are based of ZKSync `zksolc`.
Visit [contracts.polkadot.io](https://contracts.polkadot.io) to learn more about contracts on Polkadot!
## Status
This is experimental software in active development and not ready just yet for production usage.
This is experimental software in active development and not ready just yet for production usage. Please do report any compiler related issues or missing features that are [not yet known to us](https://contracts.polkadot.io/known_issues/) here.
Discussion around the development is hosted on the [Polkadot Forum](https://forum.polkadot.network/t/contracts-update-solidity-on-polkavm/6949#a-new-solidity-compiler-1).
@@ -16,6 +17,14 @@ Discussion around the development is hosted on the [Polkadot Forum](https://foru
`resolc` depends on the [solc](https://github.com/ethereum/solidity) binary installed on your system.
Building from source requires a compatible LLVM build.
### LLVM
`revive` requires a build of LLVM 18.1.4 or later with the RISC-V _embedded_ target, including `compiler-rt`. Use the provided [build-llvm.sh](build-llvm.sh) build script to compile a compatible LLVM build locally in `$PWD/llvm18.0` (don't forget to add that to `$PATH` afterwards).
### The `resolc` Solidity frontend
To install the `resolc` Solidity frontend executable:
```bash
@@ -24,10 +33,23 @@ export PATH=${PWD}/llvm18.0/bin:$PATH
make install-bin
resolc --version
```
### Cross-compilation to WASM
### LLVM
Cross-compiles the Revive compiler to WASM for running it in a Node.js or browser environment.
`revive` requires a build of LLVM 18.1.4 or later including `compiler-rt`. Use the provided [build-llvm.sh](build-llvm.sh) build script to compile a compatible LLVM build locally in `$PWD/llvm18.0` (don't forget to add that to `$PATH` afterwards).
Install [emscripten](https://emscripten.org/docs/getting_started/downloads.html). Tested on version 3.1.64.
To build resolc.js execute:
```bash
bash build-llvm.sh
export PATH=${PWD}/llvm18.0/bin:$PATH
export EMSDK_ROOT=<PATH_TO_EMSCRIPTEN_SDK>
bash emscripten-build-llvm.sh
source $EMSDK_ROOT/emsdk_env.sh
export LLVM_LINK_PREFIX=${PWD}/llvm18.0-emscripten
export PATH=$PATH:$PWD/llvm18.0-emscripten/bin/
make install-wasm
```
### Development
@@ -35,4 +57,15 @@ Please consult the [Makefile](Makefile) targets to learn how to run tests and be
Ensure that your branch passes `make test` locally when submitting a pull request.
## Design overview
`revive` uses [solc](https://github.com/ethereum/solidity/), the Ethereum Solidity compiler, as the [Solidity frontend](crates/solidity/src/lib.rs) to process smart contracts written in Solidity. The YUL IR code (or legacy EVM assembly as a fallback for older `solc` versions) emitted by `solc` is then translated to LLVM IR, targetting [Polkadots `revive` pallet](https://docs.rs/pallet-revive/latest/pallet_revive/trait.SyscallDoc.html).
[Frontend](https://github.com/matter-labs/era-compiler-solidity) and [code generator](https://github.com/matter-labs/era-compiler-llvm-context) are based of ZKSync `zksolc`.
## Tests
Before running the tests, ensure that Geth (Go Ethereum) is installed on your system. Follow the installation guide here: [Installing Geth](https://geth.ethereum.org/docs/getting-started/installing-geth).
Once Geth is installed, you can run the tests using the following command:
```bash
make test
```
+12
View File
@@ -0,0 +1,12 @@
# Release checklist
Prior to the first stable release we neither have formal release processes nor do we follow a fixed release schedule.
To create a new pre-release:
1. Merge a release PR which updates the `-dev.X` versions in the workspace `Cargo.toml` and updates the `CHANGELOG.md` accordingly
2. Push a release tag to `main`
3. Manually trigger the `Build revive-debian` action
4. Create a __pre-release__ from the tag and manually upload the build artifact generated by the action
5. Manually upload `resolc.js` and `resolc.wasm` from the `build-revive-wasm` action artifacts.
6. Update the [contract-docs](https://github.com/paritytech/contract-docs/) accordingly
+30
View File
@@ -0,0 +1,30 @@
# Known issues
The following is known and we are either working on it or it is a hard limitation. Please do not open a new issue.
## Release
`0.1.0-dev-2`
## Missing features
- [Libraries with public functions are not supported](https://github.com/paritytech/revive/issues/91)
- [Automatic import resolution is not supported](https://github.com/paritytech/revive/issues/98)
- The emulated EVM linear contract memory is limited to 64kb in size. Will be fixed with support for metered dynamic memory.
- [The contract calldata is currently limited to 1kb in size](https://github.com/paritytech/revive/issues/57)
- [EIP-4844 opcodes are not supported](https://github.com/paritytech/revive/issues/64)
- [Delegate calls are not supported](https://github.com/paritytech/revive/issues/67)
- [The `blockhash` opcode is not supported](https://github.com/paritytech/revive/issues/61)
- [The `extcodesize` opcode is not supported](https://github.com/paritytech/revive/issues/58)
- [The `origin` opcode is not supported](https://github.com/paritytech/revive/issues/59)
- [Gas limits for contract calls are ignored](https://github.com/paritytech/revive/issues/60)
- [Gas related opcodes are not supported](https://github.com/paritytech/revive/issues/60)
- IPFS metadata hashes are not supported
- [Compiled contract artifacts can exceed the pallet static memory limit and fail to deploy](https://github.com/paritytech/revive/issues/96).
- [Transfers to inexistant accounts will fail if the transferred value lies below the ED.](https://github.com/paritytech/revive/issues/83) Will be fixed in the pallet to make the ED completely transparent for contracts.
## Wontfix
Please consult our documentation to learn more about Solidity and EVM features likely to remain unsupported (and why they will not be supported).
TODO: Insert link to the relevant documentation section.
+3 -7
View File
@@ -5,17 +5,13 @@ set -euo pipefail
INSTALL_DIR="${PWD}/llvm18.0"
mkdir -p ${INSTALL_DIR}
# Clone LLVM 18 (any revision after commit bd32aaa is supposed to work)
if [ ! -d "llvm-project" ]; then
git clone --depth 1 --branch release/18.x https://github.com/llvm/llvm-project.git
fi
# Build LLVM, clang
LLVM_SRC_PREFIX=${PWD}/llvm-project
LLVM_SRC_DIR=${LLVM_SRC_PREFIX}/llvm
LLVM_BUILD_DIR=${PWD}/build/llvm
./clone-llvm.sh "${LLVM_SRC_PREFIX}"
if [ ! -d ${LLVM_BUILD_DIR} ] ; then
mkdir -p ${LLVM_BUILD_DIR}
fi
Executable
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
set -euo pipefail
# Default directory for cloning the llvm-project repository
DEFAULT_DIR="llvm-project"
# Check if a directory argument is provided
if [ $# -eq 1 ]; then
DIR=$1
else
DIR=$DEFAULT_DIR
fi
# Clone LLVM 18 (any revision after commit bd32aaa is supposed to work)
if [ ! -d "${DIR}" ]; then
git clone --depth 1 --branch release/18.x https://github.com/llvm/llvm-project.git "${DIR}"
fi
+28 -28
View File
@@ -17,56 +17,56 @@
| | `EVM` | `PVMInterpreter` |
|:--------|:------------------------|:-------------------------------- |
| **`0`** | `5.97 us` (✅ **1.00x**) | `27.04 us` (❌ *4.53x slower*) |
| **`0`** | `3.36 us` (✅ **1.00x**) | `11.84 us` (❌ *3.52x slower*) |
### OddPorduct
| | `EVM` | `PVMInterpreter` |
|:-------------|:--------------------------|:-------------------------------- |
| **`10000`** | `4.26 ms` (✅ **1.00x**) | `2.88 ms` ( **1.48x faster**) |
| **`100000`** | `42.37 ms` (✅ **1.00x**) | `28.35 ms` ( **1.49x faster**) |
| **`300000`** | `127.88 ms` (✅ **1.00x**) | `88.43 ms` ( **1.45x faster**) |
| | `EVM` | `PVMInterpreter` |
|:-------------|:-------------------------|:-------------------------------- |
| **`10000`** | `3.11 ms` (✅ **1.00x**) | `1.53 ms` (🚀 **2.03x faster**) |
| **`100000`** | `30.70 ms` (✅ **1.00x**) | `15.54 ms` (🚀 **1.98x faster**) |
| **`300000`** | `92.68 ms` (✅ **1.00x**) | `45.47 ms` (🚀 **2.04x faster**) |
### TriangleNumber
| | `EVM` | `PVMInterpreter` |
|:-------------|:--------------------------|:-------------------------------- |
| **`10000`** | `2.85 ms` (✅ **1.00x**) | `2.37 ms` ( **1.20x faster**) |
| **`100000`** | `27.85 ms` (✅ **1.00x**) | `23.01 ms` ( **1.21x faster**) |
| **`360000`** | `103.01 ms` (✅ **1.00x**) | `83.66 ms` ( **1.23x faster**) |
| | `EVM` | `PVMInterpreter` |
|:-------------|:-------------------------|:-------------------------------- |
| **`10000`** | `2.29 ms` (✅ **1.00x**) | `1.09 ms` (🚀 **2.11x faster**) |
| **`100000`** | `22.84 ms` (✅ **1.00x**) | `10.66 ms` (🚀 **2.14x faster**) |
| **`360000`** | `82.29 ms` (✅ **1.00x**) | `37.01 ms` (🚀 **2.22x faster**) |
### FibonacciRecursive
| | `EVM` | `PVMInterpreter` |
|:---------|:--------------------------|:--------------------------------- |
| **`12`** | `195.19 us` (✅ **1.00x**) | `333.53 us` (*1.71x slower*) |
| **`16`** | `1.22 ms` (✅ **1.00x**) | `1.97 ms` (❌ *1.62x slower*) |
| **`20`** | `8.14 ms` (✅ **1.00x**) | `13.20 ms` (*1.62x slower*) |
| **`24`** | `55.09 ms` (✅ **1.00x**) | `88.56 ms` (*1.61x slower*) |
| **`12`** | `135.67 us` (✅ **1.00x**) | `125.02 us` (**1.09x faster**) |
| **`16`** | `903.75 us` (✅ **1.00x**) | `762.79 us` (✅ **1.18x faster**) |
| **`20`** | `6.12 ms` (✅ **1.00x**) | `4.96 ms` (**1.23x faster**) |
| **`24`** | `42.05 ms` (✅ **1.00x**) | `33.86 ms` (**1.24x faster**) |
### FibonacciIterative
| | `EVM` | `PVMInterpreter` |
|:----------|:-------------------------|:--------------------------------- |
| **`64`** | `33.39 us` (✅ **1.00x**) | `86.02 us` (❌ *2.58x slower*) |
| **`128`** | `52.91 us` (✅ **1.00x**) | `126.38 us` (❌ *2.39x slower*) |
| **`256`** | `82.33 us` (✅ **1.00x**) | `208.74 us` (❌ *2.54x slower*) |
| | `EVM` | `PVMInterpreter` |
|:----------|:-------------------------|:-------------------------------- |
| **`64`** | `15.04 us` (✅ **1.00x**) | `29.45 us` (❌ *1.96x slower*) |
| **`128`** | `26.36 us` (✅ **1.00x**) | `42.19 us` (❌ *1.60x slower*) |
| **`256`** | `48.61 us` (✅ **1.00x**) | `65.71 us` (❌ *1.35x slower*) |
### FibonacciBinet
| | `EVM` | `PVMInterpreter` |
|:----------|:-------------------------|:--------------------------------- |
| **`64`** | `32.29 us` (✅ **1.00x**) | `161.75 us` (❌ *5.01x slower*) |
| **`128`** | `36.02 us` (✅ **1.00x**) | `172.59 us` (❌ *4.79x slower*) |
| **`256`** | `41.21 us` (✅ **1.00x**) | `185.30 us` (❌ *4.50x slower*) |
| | `EVM` | `PVMInterpreter` |
|:----------|:-------------------------|:-------------------------------- |
| **`64`** | `15.22 us` (✅ **1.00x**) | `41.46 us` (❌ *2.72x slower*) |
| **`128`** | `17.05 us` (✅ **1.00x**) | `42.84 us` (❌ *2.51x slower*) |
| **`256`** | `19.00 us` (✅ **1.00x**) | `44.36 us` (❌ *2.34x slower*) |
### SHA1
| | `EVM` | `PVMInterpreter` |
|:----------|:--------------------------|:--------------------------------- |
| **`1`** | `160.17 us` (✅ **1.00x**) | `403.46 us` (❌ *2.52x slower*) |
| **`64`** | `286.69 us` (✅ **1.00x**) | `479.79 us` (❌ *1.67x slower*) |
| **`512`** | `1.18 ms` (✅ **1.00x**) | `1.37 ms` (❌ *1.16x slower*) |
| **`1`** | `110.04 us` (✅ **1.00x**) | `216.11 us` (❌ *1.96x slower*) |
| **`64`** | `209.04 us` (✅ **1.00x**) | `309.48 us` (❌ *1.48x slower*) |
| **`512`** | `903.65 us` (✅ **1.00x**) | `980.49 us` (✅ **1.09x slower**) |
---
Made with [criterion-table](https://github.com/nu11ptr/criterion-table)
+1 -1
View File
@@ -46,7 +46,7 @@ fn group<'error, M>(c: &'error mut Criterion<M>, group_name: &str) -> BenchmarkG
where
M: Measurement,
{
return c.benchmark_group(group_name);
c.benchmark_group(group_name)
}
fn bench_baseline(c: &mut Criterion) {
-3
View File
@@ -8,7 +8,4 @@ authors.workspace = true
build = "build.rs"
description = "compiler builtins for the revive compiler"
[features]
riscv-64 = []
[dependencies]
-3
View File
@@ -1,8 +1,5 @@
use std::{env, fs, io::Read, path::Path, process::Command};
#[cfg(not(feature = "riscv-64"))]
pub const BUILTINS_ARCHIVE_FILE: &str = "libclang_rt.builtins-riscv32.a";
#[cfg(feature = "riscv-64")]
pub const BUILTINS_ARCHIVE_FILE: &str = "libclang_rt.builtins-riscv64.a";
fn main() {
-3
View File
@@ -13,9 +13,6 @@ description = "Shared constants of the revive compiler"
[lib]
doctest = false
[features]
riscv-64 = []
[dependencies]
anyhow = { workspace = true }
serde = { workspace = true, features = ["derive"] }
+3
View File
@@ -41,3 +41,6 @@ pub static EXTENSION_POLKAVM_ASSEMBLY: &str = "pvmasm";
/// The PolkaVM bytecode file extension.
pub static EXTENSION_POLKAVM_BINARY: &str = "pvm";
/// The ELF shared object file extension.
pub static EXTENSION_SHARED_OBJECT: &str = "so";
+2 -1
View File
@@ -40,7 +40,7 @@ const EXECUTABLE_ARGS_BENCH: [&str; 6] = [
"-",
];
const GAS_USED_MARKER: &str = "EVM gas used:";
const REVERT_MARKER: &str = "error: execution reverted";
const REVERT_MARKER: &str = " error: ";
/// The geth EVM state dump structure
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
@@ -557,6 +557,7 @@ allocated bytes: 3711"#;
}
#[test]
#[ignore] // https://github.com/ethereum/go-ethereum/issues/30778
fn bench_flipper() {
let log_runtime = Evm::default()
.code_blob(EVM_BIN_RUNTIME_FIXTURE.as_bytes().to_vec())
+8 -8
View File
@@ -1,10 +1,10 @@
{
"Baseline": 912,
"Computation": 4413,
"DivisionArithmetics": 40689,
"ERC20": 54374,
"Events": 1726,
"FibonacciIterative": 3015,
"Flipper": 3612,
"SHA1": 32865
"Baseline": 1110,
"Computation": 2389,
"DivisionArithmetics": 14822,
"ERC20": 23973,
"Events": 1605,
"FibonacciIterative": 2023,
"Flipper": 1989,
"SHA1": 17026
}
+31
View File
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
/* runner.json
{
"differential": false,
"actions": [
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "BaseFee"
}
}
}
},
{
"VerifyCall": {
"success": true
}
}
]
}
*/
contract BaseFee {
constructor() payable {
assert(block.basefee == 0);
}
}
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
/* runner.json
{
"differential": false,
"actions": [
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "BlockHash"
}
},
"data": "4545454545454545454545454545454545454545454545454545454545454545"
}
}
]
}
*/
contract BlockHash {
constructor(bytes32 expected) payable {
assert(blockhash(0) == expected);
assert(blockhash(1) == 0);
assert(
blockhash(
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
) == 0
);
}
}
+6 -11
View File
@@ -21,16 +21,8 @@ pragma solidity ^0.8;
"Solidity": {
"contract": "Caller"
}
}
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"value": 123,
"data": "1eb16e5b000000000000000000000000d8b934580fce35a11b58c6d73adee468a2833fa8"
"value": 123
}
},
{
@@ -49,11 +41,14 @@ contract Callee {
function echo(bytes memory payload) public pure returns (bytes memory) {
return payload;
}
receive() external payable {}
}
contract Caller {
function value_transfer(address payable destination) public payable {
destination.transfer(msg.value);
constructor() payable {
Callee callee = new Callee();
payable(address(callee)).transfer(msg.value);
}
function call(bytes memory payload) public returns (bytes memory) {
+84
View File
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
/* runner.json
{
"differential": true,
"actions": [
{
"Upload": {
"code": {
"Solidity": {
"contract": "Logic"
}
}
}
},
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "Tester"
}
}
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"value": 123,
"data": "6466414b0000000000000000000000000000000000000000000000000000000000000020"
}
}
]
}
*/
contract Logic {
// NOTE: storage layout must be the same as contract Tester
uint256 public num;
address public sender;
uint256 public value;
uint public immutable multiplier = 4;
event DidSetVars();
function setVars(uint256 _num) public payable returns (uint256) {
num = _num * multiplier;
sender = msg.sender;
value = msg.value;
emit DidSetVars();
return _num;
}
}
contract Tester {
uint256 public num;
address public sender;
uint256 public value;
uint public immutable multiplier = 2;
function setVars(uint256 _num) public payable returns (bool, bytes memory) {
Logic impl = new Logic();
// Tester's storage is set, Logic is not modified.
(bool success, bytes memory data) = address(impl).delegatecall(
abi.encodeWithSignature("setVars(uint256)", _num)
);
assert(success);
assert(impl.num() == 0);
assert(impl.sender() == address(0));
assert(impl.value() == 0);
assert(num == _num * 4);
assert(sender == msg.sender);
assert(value == msg.value);
return (success, data);
}
}
+43 -4
View File
@@ -4,11 +4,27 @@ pragma solidity ^0.8;
/* runner.json
{
"differential": true,
"actions": [
{
"Instantiate": {}
}
]
{
"Upload": {
"code": {
"Solidity": {
"contract": "ERC20"
}
}
}
},
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "ERC20Tester"
}
}
}
}
]
}
*/
@@ -82,3 +98,26 @@ contract ERC20 is IERC20 {
emit Transfer(msg.sender, address(0), amount);
}
}
contract ERC20Tester {
constructor() {
address BOB = address(0xffffffffffffffffffffffffffffffffffffff);
ERC20 token = new ERC20();
assert(token.decimals() == 18);
token.mint(300);
assert(token.balanceOf(address(this)) == 300);
token.transfer(BOB, 100);
assert(token.balanceOf(address(this)) == 200);
assert(token.balanceOf(BOB) == 100);
token.approve(address(this), 100);
token.transferFrom(address(this), BOB, 100);
assert(token.balanceOf(BOB) == 200);
assert(token.balanceOf(address(this)) == 100);
token.burn(100);
assert(token.balanceOf(address(this)) == 0);
}
}
+12
View File
@@ -14,4 +14,16 @@ contract ExtCode {
ret := codesize()
}
}
function ExtCodeHash(address who) public view returns (bytes32 ret) {
assembly {
ret := extcodehash(who)
}
}
function CodeHash() public view returns (bytes32 ret) {
assembly {
ret := extcodehash(address())
}
}
}
+32
View File
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
/* runner.json
{
"differential": false,
"actions": [
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "GasLeft"
}
}
}
},
{
"VerifyCall": {
"success": true
}
}
]
}
*/
contract GasLeft {
constructor() payable {
assert(gasleft() > gasleft());
assert(gasleft() > 0 && gasleft() < 0xffffffffffffffff);
}
}
+31
View File
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
/* runner.json
{
"differential": false,
"actions": [
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "GasLimit"
}
}
}
},
{
"VerifyCall": {
"success": true
}
}
]
}
*/
contract GasLimit {
constructor() payable {
assert(block.gaslimit == 2000000000000);
}
}
+31
View File
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
/* runner.json
{
"differential": false,
"actions": [
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "GasPrice"
}
}
}
},
{
"VerifyCall": {
"success": true
}
}
]
}
*/
contract GasPrice {
constructor() payable {
assert(tx.gasprice == 1);
}
}
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
/* runner.json
{
"differential": true,
"actions": [
{
"Upload": {
"code": {
"Solidity": {
"contract": "ImmutablesTester"
}
}
}
},
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "Immutables"
}
}
}
},
{
"Call": {
"dest": {
"Instantiated": 0
}
}
}
]
}
*/
contract ImmutablesTester {
// Read should work in the runtime code
uint public immutable foo;
// Read should work in the runtime code
uint public immutable bar;
// Read should work in the runtime code
uint public immutable zoo;
// Assign and read should work in the constructor
constructor(uint _foo) payable {
foo = _foo;
bar = foo + 1;
zoo = bar + 2;
assert(zoo == _foo + 3);
}
}
contract Immutables {
fallback() external {
ImmutablesTester tester = new ImmutablesTester(127);
assert(tester.foo() == 127);
assert(tester.bar() == tester.foo() + 1);
assert(tester.zoo() == tester.bar() + 2);
}
}
+1 -12
View File
@@ -4,6 +4,7 @@ pragma solidity ^0.8;
/* runner.json
{
"differential": true,
"actions": [
{
"Instantiate": {
@@ -23,12 +24,6 @@ pragma solidity ^0.8;
"data": "f016832c"
}
},
{
"VerifyCall": {
"success": true,
"output": "0000000000000000000000000000000000000000000000000000000000000060"
}
},
{
"Call": {
"dest": {
@@ -36,12 +31,6 @@ pragma solidity ^0.8;
},
"data": "f4a63aa5"
}
},
{
"VerifyCall": {
"success": true,
"output": "0000000000000000000000000000000000000000000000000000000000000084"
}
}
]
}
+17 -1
View File
@@ -22,6 +22,14 @@ pragma solidity ^0.8;
},
"data": "fabc9efaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"data": "558b9f9bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
}
}
]
}
@@ -30,10 +38,18 @@ pragma solidity ^0.8;
contract Storage {
function transient(uint value) public returns (uint ret) {
assembly {
let slot := 123
let slot := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
tstore(slot, value)
let success := call(0, 0, 0, 0, 0, 0, 0)
ret := tload(slot)
}
}
function persistent(uint value) public returns (uint ret) {
assembly {
let slot := 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00
sstore(slot, value)
ret := sload(slot)
}
}
}
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/* runner.json
{
"differential": true,
"actions": [
{
"Upload": {
"code": {
"Solidity": {
"contract": "TransactionOrigin"
}
}
}
},
{
"Instantiate": {
"code": {
"Solidity": {
"contract": "TransactionTester"
}
}
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"data": "f8a8fd6d"
}
}
]
}
*/
contract TransactionTester {
constructor() payable {
assert(tx.origin == new TransactionOrigin().test());
}
function test() public payable returns (address ret) {
ret = tx.origin;
}
}
contract TransactionOrigin {
function test() public payable returns (address ret) {
assert(msg.sender != tx.origin);
ret = tx.origin;
}
}
+28 -15
View File
@@ -5,6 +5,15 @@ pragma solidity ^0.8;
{
"differential": true,
"actions": [
{
"Upload": {
"code": {
"Solidity": {
"contract": "ValueTester"
}
}
}
},
{
"Instantiate": {
"value": 1024,
@@ -23,31 +32,35 @@ pragma solidity ^0.8;
"value": 123,
"data": "3fa4f245"
}
},
{
"Call": {
"dest": {
"Instantiated": 0
},
"data": "52da5fa0"
}
}
]
}
*/
contract Value {
contract ValueTester {
constructor() payable {}
function value() public payable returns (uint ret) {
ret = msg.value;
}
function balance_self() public view returns (uint ret) {
ret = address(this).balance;
}
}
function balance_of(address _address) public view returns (uint ret) {
ret = _address.balance;
contract Value {
constructor() payable {
ValueTester tester = new ValueTester{value: msg.value}();
// own account
assert(address(this).balance == 0);
// tester account
assert(address(tester).balance == msg.value);
assert(tester.balance_self() == msg.value);
// non-existant account
assert(address(0xdeadbeef).balance == 0);
}
function value() public payable returns (uint ret) {
ret = msg.value;
}
}
+6
View File
@@ -178,10 +178,16 @@ sol!(
function ExtCodeSize(address who) public view returns (uint ret);
function CodeSize() public pure returns (uint ret);
function ExtCodeHash(address who) public view returns (bytes32 ret);
function CodeHash() public view returns (bytes32 ret);
}
);
case!("ExtCode.sol", ExtCode, ExtCodeSizeCall, ext_code_size, address: Address);
case!("ExtCode.sol", ExtCode, CodeSizeCall, code_size,);
case!("ExtCode.sol", ExtCode, ExtCodeHashCall, ext_code_hash, address: Address);
case!("ExtCode.sol", ExtCode, CodeHashCall, code_hash,);
sol!(
contract MCopy {
+204 -50
View File
@@ -42,6 +42,14 @@ test_spec!(create, "CreateB", "Create.sol");
test_spec!(call, "Caller", "Call.sol");
test_spec!(transfer, "Transfer", "Transfer.sol");
test_spec!(return_data_oob, "ReturnDataOob", "ReturnDataOob.sol");
test_spec!(immutables, "Immutables", "Immutables.sol");
test_spec!(transaction, "Transaction", "Transaction.sol");
test_spec!(block_hash, "BlockHash", "BlockHash.sol");
test_spec!(delegate, "Delegate", "Delegate.sol");
test_spec!(gas_price, "GasPrice", "GasPrice.sol");
test_spec!(gas_left, "GasLeft", "GasLeft.sol");
test_spec!(gas_limit, "GasLimit", "GasLimit.sol");
test_spec!(base_fee, "BaseFee", "BaseFee.sol");
fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> {
vec![Instantiate {
@@ -229,6 +237,202 @@ fn signed_remainder() {
data: Contract::division_arithmetics_smod(n, d).calldata,
})
}
run_differential(actions);
}
#[test]
fn ext_code_hash() {
let mut actions = instantiate("contracts/ExtCode.sol", "ExtCode");
// First do contract instantiation to figure out address and code hash
let results = Specs {
actions: actions.clone(),
..Default::default()
}
.run();
let (addr, code_hash) = match results.first().cloned() {
Some(CallResult::Instantiate {
result, code_hash, ..
}) => (result.result.unwrap().addr, code_hash),
_ => panic!("instantiate contract failed"),
};
// code hash of itself
actions.push(Call {
origin: TestAddress::Alice,
dest: TestAddress::Instantiated(0),
value: 0,
gas_limit: None,
storage_deposit_limit: None,
data: Contract::code_hash().calldata,
});
actions.push(VerifyCall(VerifyCallExpectation {
success: true,
output: OptionalHex::from(code_hash.as_bytes().to_vec()),
gas_consumed: None,
}));
// code hash for a given contract address
actions.push(Call {
origin: TestAddress::Alice,
dest: TestAddress::Instantiated(0),
value: 0,
gas_limit: None,
storage_deposit_limit: None,
data: Contract::ext_code_hash(Address::from(addr.to_fixed_bytes())).calldata,
});
actions.push(VerifyCall(VerifyCallExpectation {
success: true,
output: OptionalHex::from(code_hash.as_bytes().to_vec()),
gas_consumed: None,
}));
// EOA returns fixed hash
actions.push(Call {
origin: TestAddress::Alice,
dest: TestAddress::Instantiated(0),
value: 0,
gas_limit: None,
storage_deposit_limit: None,
data: Contract::ext_code_hash(Address::from(CHARLIE.to_fixed_bytes())).calldata,
});
actions.push(VerifyCall(VerifyCallExpectation {
success: true,
output: OptionalHex::from(
hex!("c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470").to_vec(),
),
gas_consumed: None,
}));
// non-existing account
actions.push(Call {
origin: TestAddress::Alice,
dest: TestAddress::Instantiated(0),
value: 0,
gas_limit: None,
storage_deposit_limit: None,
data: Contract::ext_code_hash(Address::from([8u8; 20])).calldata,
});
actions.push(VerifyCall(VerifyCallExpectation {
success: true,
output: OptionalHex::from([0u8; 32].to_vec()),
gas_consumed: None,
}));
Specs {
actions,
..Default::default()
}
.run();
}
#[test]
fn ext_code_size() {
let alice = Address::from(ALICE.0);
let own_address = alice.create(0);
let baseline_address = alice.create2([0u8; 32], keccak256(Contract::baseline().pvm_runtime));
let own_code_size = U256::from(
Contract::ext_code_size(Default::default())
.pvm_runtime
.len(),
);
let baseline_code_size = U256::from(Contract::baseline().pvm_runtime.len());
Specs {
actions: vec![
// Instantiate the test contract
instantiate("contracts/ExtCode.sol", "ExtCode").remove(0),
// Instantiate the baseline contract
Instantiate {
origin: TestAddress::Alice,
value: 0,
gas_limit: Some(GAS_LIMIT),
storage_deposit_limit: None,
code: Code::Solidity {
path: Some("contracts/Baseline.sol".into()),
contract: "Baseline".to_string(),
solc_optimizer: None,
pipeline: None,
},
data: vec![],
salt: OptionalHex::from([0; 32]),
},
// Alice is not a contract and returns a code size of 0
Call {
origin: TestAddress::Alice,
dest: TestAddress::Instantiated(0),
value: 0,
gas_limit: None,
storage_deposit_limit: None,
data: Contract::ext_code_size(alice).calldata,
},
VerifyCall(VerifyCallExpectation {
success: true,
output: OptionalHex::from([0u8; 32].to_vec()),
gas_consumed: None,
}),
// Unknown address returns a code size of 0
Call {
origin: TestAddress::Alice,
dest: TestAddress::Instantiated(0),
value: 0,
gas_limit: None,
storage_deposit_limit: None,
data: Contract::ext_code_size(Address::from([0xff; 20])).calldata,
},
VerifyCall(VerifyCallExpectation {
success: true,
output: OptionalHex::from([0u8; 32].to_vec()),
gas_consumed: None,
}),
// Own address via extcodesize returns own code size
Call {
origin: TestAddress::Alice,
dest: TestAddress::Instantiated(0),
value: 0,
gas_limit: None,
storage_deposit_limit: None,
data: Contract::ext_code_size(own_address).calldata,
},
VerifyCall(VerifyCallExpectation {
success: true,
output: OptionalHex::from(own_code_size.to_be_bytes::<32>().to_vec()),
gas_consumed: None,
}),
// Own address via codesize returns own code size
Call {
origin: TestAddress::Alice,
dest: TestAddress::Instantiated(0),
value: 0,
gas_limit: None,
storage_deposit_limit: None,
data: Contract::code_size().calldata,
},
VerifyCall(VerifyCallExpectation {
success: true,
output: OptionalHex::from(own_code_size.to_be_bytes::<32>().to_vec()),
gas_consumed: None,
}),
// Baseline address returns the baseline code size
Call {
origin: TestAddress::Alice,
dest: TestAddress::Instantiated(0),
value: 0,
gas_limit: None,
storage_deposit_limit: None,
data: Contract::ext_code_size(baseline_address).calldata,
},
VerifyCall(VerifyCallExpectation {
success: true,
output: OptionalHex::from(baseline_code_size.to_be_bytes::<32>().to_vec()),
gas_consumed: None,
}),
],
..Default::default()
}
.run();
}
/*
@@ -259,54 +463,4 @@ fn create2_failure() {
assert_eq!(output.flags, ReturnFlags::Revert);
}
#[test]
fn balance() {
let (_, output) = assert_success(&Contract::value_balance_of(Default::default()), false);
let expected = U256::ZERO;
let received = U256::from_be_slice(&output.data);
assert_eq!(expected, received);
let expected = U256::from(54589);
let (mut state, address) = State::new_deployed(Contract::value_balance_of(Default::default()));
state.accounts_mut().get_mut(&address).unwrap().value = expected;
let contract = Contract::value_balance_of(address);
let (_, output) = state
.transaction()
.with_default_account(&contract.pvm_runtime)
.calldata(contract.calldata)
.call();
assert_eq!(ReturnFlags::Success, output.flags);
let received = U256::from_be_slice(&output.data);
assert_eq!(expected, received)
}
#[test]
fn ext_code_size() {
let contract = Contract::ext_code_size(Transaction::default_address());
let (_, output) = assert_success(&contract, false);
let received = U256::from_be_slice(&output.data);
let expected = U256::from(contract.pvm_runtime.len());
assert_eq!(received, expected);
let contract = Contract::ext_code_size(Default::default());
let (_, output) = assert_success(&contract, false);
let received = U256::from_be_slice(&output.data);
let expected = U256::ZERO;
assert_eq!(received, expected);
}
#[test]
fn code_size() {
let contract = Contract::code_size();
let (_, output) = assert_success(&contract, false);
let expected = U256::from(contract.pvm_runtime.len());
let received = U256::from_be_slice(&output.data);
assert_eq!(expected, received);
}
*/
-3
View File
@@ -7,9 +7,6 @@ repository.workspace = true
authors.workspace = true
description = "revive compiler linker utils"
[features]
riscv-64 = []
[dependencies]
inkwell = { workspace = true }
tempfile = { workspace = true }
+4 -15
View File
@@ -8,14 +8,7 @@ SECTIONS {
.text : { KEEP(*(.text.polkavm_export)) *(.text .text.*) }
}"#;
#[cfg(not(feature = "riscv-64"))]
const BUILTINS_ARCHIVE_FILE: &str = "libclang_rt.builtins-riscv32.a";
#[cfg(feature = "riscv-64")]
const BUILTINS_ARCHIVE_FILE: &str = "libclang_rt.builtins-riscv64.a";
#[cfg(not(feature = "riscv-64"))]
const BUILTINS_LIB_NAME: &str = "clang_rt.builtins-riscv32";
#[cfg(feature = "riscv-64")]
const BUILTINS_LIB_NAME: &str = "clang_rt.builtins-riscv64";
fn invoke_lld(cmd_args: &[&str]) -> bool {
@@ -29,9 +22,9 @@ fn invoke_lld(cmd_args: &[&str]) -> bool {
unsafe { LLDELFLink(args.as_ptr(), args.len()) == 0 }
}
fn polkavm_linker<T: AsRef<[u8]>>(code: T) -> anyhow::Result<Vec<u8>> {
pub fn polkavm_linker<T: AsRef<[u8]>>(code: T, strip_binary: bool) -> anyhow::Result<Vec<u8>> {
let mut config = polkavm_linker::Config::default();
config.set_strip(true);
config.set_strip(strip_binary);
config.set_optimize(true);
polkavm_linker::program_from_elf(config, code.as_ref())
@@ -64,6 +57,7 @@ pub fn link<T: AsRef<[u8]>>(input: T) -> anyhow::Result<Vec<u8>> {
"--relocatable",
"--emit-relocs",
"--no-relax",
"--unique",
"--gc-sections",
"--library-path",
dir.path().to_str().expect("should be utf8"),
@@ -79,10 +73,5 @@ pub fn link<T: AsRef<[u8]>>(input: T) -> anyhow::Result<Vec<u8>> {
return Err(anyhow::anyhow!("ld.lld failed"));
}
if env::var("PVM_LINKER_DUMP_SO").is_ok() {
fs::copy(&output_path, "/tmp/out.so")?;
};
let blob = fs::read(&output_path)?;
polkavm_linker(blob)
Ok(fs::read(&output_path)?)
}
+76 -8
View File
@@ -1,5 +1,19 @@
fn llvm_config(arg: &str) -> String {
let output = std::process::Command::new("llvm-config")
use std::{
env,
path::{Path, PathBuf},
};
const LLVM_LINK_PREFIX: &str = "LLVM_LINK_PREFIX";
fn locate_llvm_config() -> PathBuf {
let prefix = env::var_os(LLVM_LINK_PREFIX)
.map(|p| PathBuf::from(p).join("bin"))
.unwrap_or_default();
prefix.join("llvm-config")
}
fn llvm_config(llvm_config_path: &Path, arg: &str) -> String {
let output = std::process::Command::new(llvm_config_path)
.args([arg])
.output()
.unwrap_or_else(|_| panic!("`llvm-config {arg}` failed"));
@@ -8,8 +22,11 @@ fn llvm_config(arg: &str) -> String {
.unwrap_or_else(|_| panic!("output of `llvm-config {arg}` should be utf8"))
}
fn set_rustc_link_flags() {
println!("cargo:rustc-link-search=native={}", llvm_config("--libdir"));
fn set_rustc_link_flags(llvm_config_path: &Path) {
println!(
"cargo:rustc-link-search=native={}",
llvm_config(llvm_config_path, "--libdir")
);
for lib in [
"lldELF",
@@ -22,19 +39,70 @@ fn set_rustc_link_flags() {
"LLVMTargetParser",
"LLVMBinaryFormat",
"LLVMDemangle",
// The `llvm-sys` crate relies on `llvm-config` to obtain a list of required LLVM libraries
// during the build process. This works well in typical native environments, where `llvm-config`
// can accurately list the necessary libraries.
// However, when cross-compiling to WebAssembly using Emscripten, `llvm-config` fails to recognize
// JavaScript-based libraries, making it necessary to manually inject the required dependencies.
"LLVMRISCVDisassembler",
"LLVMRISCVAsmParser",
"LLVMRISCVCodeGen",
"LLVMRISCVDesc",
"LLVMRISCVInfo",
"LLVMExecutionEngine",
"LLVMOption",
"LLVMMCDisassembler",
"LLVMPasses",
"LLVMHipStdPar",
"LLVMCFGuard",
"LLVMCoroutines",
"LLVMipo",
"LLVMVectorize",
"LLVMInstrumentation",
"LLVMFrontendOpenMP",
"LLVMFrontendOffloading",
"LLVMGlobalISel",
"LLVMAsmPrinter",
"LLVMSelectionDAG",
"LLVMCodeGen",
"LLVMTarget",
"LLVMObjCARCOpts",
"LLVMCodeGenTypes",
"LLVMIRPrinter",
"LLVMScalarOpts",
"LLVMInstCombine",
"LLVMAggressiveInstCombine",
"LLVMTransformUtils",
"LLVMBitWriter",
"LLVMAnalysis",
"LLVMProfileData",
"LLVMDebugInfoDWARF",
"LLVMObject",
"LLVMMCParser",
"LLVMIRReader",
"LLVMAsmParser",
"LLVMMC",
"LLVMDebugInfoCodeView",
"LLVMBitReader",
"LLVMRemarks",
"LLVMBitstreamReader",
] {
println!("cargo:rustc-link-lib=static={lib}");
}
#[cfg(target_os = "linux")]
{
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
if target_os == "linux" {
println!("cargo:rustc-link-lib=dylib=stdc++");
println!("cargo:rustc-link-lib=tinfo");
}
}
fn main() {
llvm_config("--cxxflags")
println!("cargo:rerun-if-env-changed={}", LLVM_LINK_PREFIX);
let llvm_config_path = locate_llvm_config();
llvm_config(&llvm_config_path, "--cxxflags")
.split_whitespace()
.fold(&mut cc::Build::new(), |builder, flag| builder.flag(flag))
.flag("-Wno-unused-parameter")
@@ -42,7 +110,7 @@ fn main() {
.file("src/linker.cpp")
.compile("liblinker.a");
set_rustc_link_flags();
set_rustc_link_flags(&llvm_config_path);
println!("cargo:rerun-if-changed=build.rs");
}
-4
View File
@@ -13,10 +13,6 @@ description = "Shared front end code of the revive PolkaVM compilers"
[lib]
doctest = false
[features]
riscv-zbb = []
riscv-64 = []
[dependencies]
anyhow = { workspace = true }
semver = { workspace = true }
@@ -15,6 +15,8 @@ pub enum IRType {
LLVM,
/// Whether to dump the assembly code.
Assembly,
/// Whether to dump the ELF shared object
SO,
/// Whether to jump JSON
#[cfg(debug_assertions)]
JSON,
@@ -31,6 +33,7 @@ impl IRType {
Self::Assembly => revive_common::EXTENSION_POLKAVM_ASSEMBLY,
#[cfg(debug_assertions)]
Self::JSON => revive_common::EXTENSION_JSON,
Self::SO => revive_common::EXTENSION_SHARED_OBJECT,
}
}
}
+66 -33
View File
@@ -13,41 +13,52 @@ use self::ir_type::IRType;
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct DebugConfig {
/// The directory to dump the IRs to.
pub output_directory: PathBuf,
pub output_directory: Option<PathBuf>,
/// Whether debug info should be emitted.
pub emit_debug_info: bool,
}
impl DebugConfig {
/// A shortcut constructor.
pub fn new(output_directory: PathBuf) -> Self {
Self { output_directory }
pub const fn new(output_directory: Option<PathBuf>, emit_debug_info: bool) -> Self {
Self {
output_directory,
emit_debug_info,
}
}
/// Dumps the Yul IR.
pub fn dump_yul(&self, contract_path: &str, code: &str) -> anyhow::Result<()> {
let mut file_path = self.output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, None, IRType::Yul);
file_path.push(full_file_name);
std::fs::write(file_path, code)?;
if let Some(output_directory) = self.output_directory.as_ref() {
let mut file_path = output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, None, IRType::Yul);
file_path.push(full_file_name);
std::fs::write(file_path, code)?;
}
Ok(())
}
/// Dumps the EVM legacy assembly IR.
pub fn dump_evmla(&self, contract_path: &str, code: &str) -> anyhow::Result<()> {
let mut file_path = self.output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, None, IRType::EVMLA);
file_path.push(full_file_name);
std::fs::write(file_path, code)?;
if let Some(output_directory) = self.output_directory.as_ref() {
let mut file_path = output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, None, IRType::EVMLA);
file_path.push(full_file_name);
std::fs::write(file_path, code)?;
}
Ok(())
}
/// Dumps the Ethereal IR.
pub fn dump_ethir(&self, contract_path: &str, code: &str) -> anyhow::Result<()> {
let mut file_path = self.output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, None, IRType::EthIR);
file_path.push(full_file_name);
std::fs::write(file_path, code)?;
if let Some(output_directory) = self.output_directory.as_ref() {
let mut file_path = output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, None, IRType::EthIR);
file_path.push(full_file_name);
std::fs::write(file_path, code)?;
}
Ok(())
}
@@ -58,12 +69,15 @@ impl DebugConfig {
contract_path: &str,
module: &inkwell::module::Module,
) -> anyhow::Result<()> {
let llvm_code = module.print_to_string().to_string();
if let Some(output_directory) = self.output_directory.as_ref() {
let llvm_code = module.print_to_string().to_string();
let mut file_path = self.output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, Some("unoptimized"), IRType::LLVM);
file_path.push(full_file_name);
std::fs::write(file_path, llvm_code)?;
let mut file_path = output_directory.to_owned();
let full_file_name =
Self::full_file_name(contract_path, Some("unoptimized"), IRType::LLVM);
file_path.push(full_file_name);
std::fs::write(file_path, llvm_code)?;
}
Ok(())
}
@@ -74,22 +88,39 @@ impl DebugConfig {
contract_path: &str,
module: &inkwell::module::Module,
) -> anyhow::Result<()> {
let llvm_code = module.print_to_string().to_string();
if let Some(output_directory) = self.output_directory.as_ref() {
let llvm_code = module.print_to_string().to_string();
let mut file_path = self.output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, Some("optimized"), IRType::LLVM);
file_path.push(full_file_name);
std::fs::write(file_path, llvm_code)?;
let mut file_path = output_directory.to_owned();
let full_file_name =
Self::full_file_name(contract_path, Some("optimized"), IRType::LLVM);
file_path.push(full_file_name);
std::fs::write(file_path, llvm_code)?;
}
Ok(())
}
/// Dumps the assembly.
pub fn dump_assembly(&self, contract_path: &str, code: &str) -> anyhow::Result<()> {
let mut file_path = self.output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, None, IRType::Assembly);
file_path.push(full_file_name);
std::fs::write(file_path, code)?;
if let Some(output_directory) = self.output_directory.as_ref() {
let mut file_path = output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, None, IRType::Assembly);
file_path.push(full_file_name);
std::fs::write(file_path, code)?;
}
Ok(())
}
/// Dumps the code object.
pub fn dump_object(&self, contract_path: &str, code: &[u8]) -> anyhow::Result<()> {
if let Some(output_directory) = self.output_directory.as_ref() {
let mut file_path = output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, None, IRType::SO);
file_path.push(full_file_name);
std::fs::write(file_path, code)?;
}
Ok(())
}
@@ -102,10 +133,12 @@ impl DebugConfig {
contract_suffix: Option<&str>,
stage_json: &Vec<u8>,
) -> anyhow::Result<()> {
let mut file_path = self.output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, contract_suffix, IRType::JSON);
file_path.push(full_file_name);
std::fs::write(file_path, stage_json)?;
if let Some(output_directory) = self.output_directory.as_ref() {
let mut file_path = output_directory.to_owned();
let full_file_name = Self::full_file_name(contract_path, contract_suffix, IRType::JSON);
file_path.push(full_file_name);
std::fs::write(file_path, stage_json)?;
}
Ok(())
}
+3 -1
View File
@@ -16,6 +16,7 @@ pub use self::polkavm::context::argument::Argument as PolkaVMArgument;
pub use self::polkavm::context::attribute::Attribute as PolkaVMAttribute;
pub use self::polkavm::context::build::Build as PolkaVMBuild;
pub use self::polkavm::context::code_type::CodeType as PolkaVMCodeType;
pub use self::polkavm::context::debug_info::DebugInfo;
pub use self::polkavm::context::evmla_data::EVMLAData as PolkaVMContextEVMLAData;
pub use self::polkavm::context::function::block::evmla_data::key::Key as PolkaVMFunctionBlockKey;
pub use self::polkavm::context::function::block::evmla_data::EVMLAData as PolkaVMFunctionBlockEVMLAData;
@@ -27,9 +28,11 @@ pub use self::polkavm::context::function::llvm_runtime::LLVMRuntime as PolkaVMLL
pub use self::polkavm::context::function::r#return::Return as PolkaVMFunctionReturn;
pub use self::polkavm::context::function::runtime::deploy_code::DeployCode as PolkaVMDeployCodeFunction;
pub use self::polkavm::context::function::runtime::entry::Entry as PolkaVMEntryFunction;
pub use self::polkavm::context::function::runtime::immutable_data_load::ImmutableDataLoad as PolkaVMImmutableDataLoadFunction;
pub use self::polkavm::context::function::runtime::runtime_code::RuntimeCode as PolkaVMRuntimeCodeFunction;
pub use self::polkavm::context::function::runtime::FUNCTION_DEPLOY_CODE as PolkaVMFunctionDeployCode;
pub use self::polkavm::context::function::runtime::FUNCTION_ENTRY as PolkaVMFunctionEntry;
pub use self::polkavm::context::function::runtime::FUNCTION_LOAD_IMMUTABLE_DATA as PolkaVMFunctionImmutableDataLoad;
pub use self::polkavm::context::function::runtime::FUNCTION_RUNTIME_CODE as PolkaVMFunctionRuntimeCode;
pub use self::polkavm::context::function::yul_data::YulData as PolkaVMFunctionYulData;
pub use self::polkavm::context::function::Function as PolkaVMFunction;
@@ -58,7 +61,6 @@ pub use self::polkavm::evm::return_data as polkavm_evm_return_data;
pub use self::polkavm::evm::storage as polkavm_evm_storage;
pub use self::polkavm::metadata_hash::MetadataHash as PolkaVMMetadataHash;
pub use self::polkavm::r#const as polkavm_const;
pub use self::polkavm::utils as polkavm_utils;
pub use self::polkavm::Dependency as PolkaVMDependency;
pub use self::polkavm::DummyDependency as PolkaVMDummyDependency;
pub use self::polkavm::DummyLLVMWritable as PolkaVMDummyLLVMWritable;
@@ -9,7 +9,7 @@ use itertools::Itertools;
use self::size_level::SizeLevel;
/// The LLVM optimizer settings.
/// The LLVM optimizer and code-gen settings.
#[derive(Debug, Serialize, Deserialize, Clone, Eq)]
pub struct Settings {
/// The middle-end optimization level.
@@ -21,8 +21,6 @@ pub struct Settings {
/// Fallback to optimizing for size if the bytecode is too large.
pub is_fallback_to_size_enabled: bool,
/// Whether the system request memoization is disabled.
pub is_system_request_memoization_disabled: bool,
/// Whether the LLVM `verify each` option is enabled.
pub is_verify_each_enabled: bool,
@@ -43,7 +41,6 @@ impl Settings {
level_back_end,
is_fallback_to_size_enabled: false,
is_system_request_memoization_disabled: false,
is_verify_each_enabled: false,
is_debug_logging_enabled: false,
@@ -65,7 +62,6 @@ impl Settings {
level_back_end,
is_fallback_to_size_enabled: false,
is_system_request_memoization_disabled: false,
is_verify_each_enabled,
is_debug_logging_enabled,
@@ -206,20 +202,10 @@ impl Settings {
self.is_fallback_to_size_enabled = true;
}
/// Disables the system request memoization.
pub fn disable_system_request_memoization(&mut self) {
self.is_system_request_memoization_disabled = true;
}
/// Whether the fallback to optimizing for size is enabled.
pub fn is_fallback_to_size_enabled(&self) -> bool {
self.is_fallback_to_size_enabled
}
/// Whether the system request memoization is disabled.
pub fn is_system_request_memoization_disabled(&self) -> bool {
self.is_system_request_memoization_disabled
}
}
impl PartialEq for Settings {
+1 -35
View File
@@ -1,54 +1,20 @@
//! The LLVM context constants.
/// Runtime API methods.
pub mod runtime_api;
/// The LLVM framework version.
pub const LLVM_VERSION: semver::Version = semver::Version::new(18, 1, 4);
/// The PolkaVM version.
pub const ZKEVM_VERSION: semver::Version = semver::Version::new(1, 3, 2);
/// The register width sized type
/// The pointer width sized type.
pub static XLEN: usize = revive_common::BIT_LENGTH_X32;
/// The heap memory pointer pointer global variable name.
pub static GLOBAL_HEAP_MEMORY_POINTER: &str = "memory_pointer";
/// The calldata pointer global variable name.
pub static GLOBAL_CALLDATA_POINTER: &str = "ptr_calldata";
/// The calldata size global variable name.
pub static GLOBAL_CALLDATA_SIZE: &str = "calldatasize";
/// The call flags global variable name.
pub static GLOBAL_CALL_FLAGS: &str = "call_flags";
/// The constant array global variable name prefix.
pub static GLOBAL_CONST_ARRAY_PREFIX: &str = "const_array_";
/// The global verbatim getter identifier prefix.
pub static GLOBAL_VERBATIM_GETTER_PREFIX: &str = "get_global::";
/// The external call data offset in the auxiliary heap.
pub const HEAP_AUX_OFFSET_EXTERNAL_CALL: u64 = 0;
/// The constructor return data offset in the auxiliary heap.
pub const HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA: u64 =
8 * (revive_common::BYTE_LENGTH_WORD as u64);
/// The `create` method deployer signature.
pub static DEPLOYER_SIGNATURE_CREATE: &str = "create(bytes32,bytes32,bytes)";
/// The `create2` method deployer signature.
pub static DEPLOYER_SIGNATURE_CREATE2: &str = "create2(bytes32,bytes32,bytes)";
/// The absence of system call bit.
pub const NO_SYSTEM_CALL_BIT: bool = false;
/// The system call bit.
pub const SYSTEM_CALL_BIT: bool = true;
/// The deployer call header size that consists of:
/// - bytecode hash (32 bytes)
pub const DEPLOYER_CALL_HEADER_SIZE: usize = revive_common::BYTE_LENGTH_WORD;
@@ -1,77 +0,0 @@
//! Runtime API import and export symbols.
pub mod exports {
/// The contract deploy export.
pub static CALL: &str = "call";
/// The contract call export.
pub static DEPLOY: &str = "deploy";
/// All exported symbols.
/// Useful for configuring common attributes and linkage.
pub static EXPORTS: [&str; 2] = [CALL, DEPLOY];
}
pub mod imports {
pub static ADDRESS: &str = "address";
pub static BALANCE: &str = "balance";
pub static BLOCK_NUMBER: &str = "block_number";
pub static CHAIN_ID: &str = "chain_id";
pub static CALL: &str = "call";
pub static CALLER: &str = "caller";
pub static CODE_SIZE: &str = "code_size";
pub static DEPOSIT_EVENT: &str = "deposit_event";
pub static GET_STORAGE: &str = "get_storage";
pub static HASH_KECCAK_256: &str = "hash_keccak_256";
pub static INPUT: &str = "input";
pub static INSTANTIATE: &str = "instantiate";
pub static NOW: &str = "now";
pub static RETURN: &str = "seal_return";
pub static RETURNDATACOPY: &str = "return_data_copy";
pub static RETURNDATASIZE: &str = "return_data_size";
pub static SET_STORAGE: &str = "set_storage";
pub static VALUE_TRANSFERRED: &str = "value_transferred";
/// All imported runtime API symbols.
/// Useful for configuring common attributes and linkage.
pub static IMPORTS: [&str; 18] = [
ADDRESS,
BALANCE,
BLOCK_NUMBER,
CALL,
CALLER,
CHAIN_ID,
CODE_SIZE,
DEPOSIT_EVENT,
GET_STORAGE,
HASH_KECCAK_256,
INPUT,
INSTANTIATE,
NOW,
RETURN,
RETURNDATACOPY,
RETURNDATASIZE,
SET_STORAGE,
VALUE_TRANSFERRED,
];
}
/// PolkaVM __sbrk API symbol to extend the heap memory.
pub static SBRK: &str = "__sbrk";
@@ -1,7 +1,43 @@
//! The LLVM debug information.
use std::cell::RefCell;
use inkwell::debug_info::AsDIScope;
use num::Zero;
use inkwell::debug_info::DIScope;
/// Debug info scope stack
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ScopeStack<'ctx> {
stack: Vec<DIScope<'ctx>>,
}
// Abstract the type of the DIScope stack.
impl<'ctx> ScopeStack<'ctx> {
pub fn from(item: DIScope<'ctx>) -> Self {
Self { stack: vec![item] }
}
/// Return the top of the scope stack, or None if the stack is empty.
pub fn top(&self) -> Option<DIScope<'ctx>> {
self.stack.last().copied()
}
/// Push a scope onto the stack.
pub fn push(&mut self, scope: DIScope<'ctx>) {
self.stack.push(scope)
}
/// Pop the scope at the top of the stack and return it.
/// Return None if the stack is empty.
pub fn pop(&mut self) -> Option<DIScope<'ctx>> {
self.stack.pop()
}
/// Return the number of scopes on the stack.
pub fn len(&self) -> usize {
self.stack.len()
}
}
/// The LLVM debug information.
pub struct DebugInfo<'ctx> {
@@ -9,6 +45,8 @@ pub struct DebugInfo<'ctx> {
compile_unit: inkwell::debug_info::DICompileUnit<'ctx>,
/// The debug info builder.
builder: inkwell::debug_info::DebugInfoBuilder<'ctx>,
/// Enclosing debug info scopes.
scope_stack: RefCell<ScopeStack<'ctx>>,
}
impl<'ctx> DebugInfo<'ctx> {
@@ -35,19 +73,43 @@ impl<'ctx> DebugInfo<'ctx> {
Self {
compile_unit,
builder,
scope_stack: RefCell::new(ScopeStack::from(compile_unit.as_debug_info_scope())),
}
}
/// Prepare an LLVM-IR module for debug-info generation
pub fn initialize_module(
&self,
llvm: &'ctx inkwell::context::Context,
module: &inkwell::module::Module<'ctx>,
) {
let debug_metadata_value = llvm
.i32_type()
.const_int(inkwell::debug_info::debug_metadata_version() as u64, false);
module.add_basic_value_flag(
"Debug Info Version",
inkwell::module::FlagBehavior::Warning,
debug_metadata_value,
);
self.push_scope(self.compilation_unit().get_file().as_debug_info_scope());
}
/// Finalize debug-info for an LLVM-IR module.
pub fn finalize_module(&self) {
self.builder().finalize()
}
/// Creates a function info.
pub fn create_function(
&self,
name: &str,
) -> anyhow::Result<inkwell::debug_info::DISubprogram<'ctx>> {
let flags = inkwell::debug_info::DIFlagsConstants::ZERO;
let subroutine_type = self.builder.create_subroutine_type(
self.compile_unit.get_file(),
Some(self.create_type(revive_common::BIT_LENGTH_FIELD)?),
Some(self.create_word_type(Some(flags))?.as_type()),
&[],
inkwell::debug_info::DIFlags::zero(),
flags,
);
let function = self.builder.create_function(
@@ -60,7 +122,7 @@ impl<'ctx> DebugInfo<'ctx> {
true,
false,
1,
inkwell::debug_info::DIFlags::zero(),
flags,
false,
);
@@ -74,24 +136,55 @@ impl<'ctx> DebugInfo<'ctx> {
Ok(function)
}
/// Creates a primitive type info.
pub fn create_type(
/// Creates primitive integer type debug-info.
pub fn create_primitive_type(
&self,
bit_length: usize,
) -> anyhow::Result<inkwell::debug_info::DIType<'ctx>> {
flags: Option<inkwell::debug_info::DIFlags>,
) -> anyhow::Result<inkwell::debug_info::DIBasicType<'ctx>> {
let di_flags = flags.unwrap_or(inkwell::debug_info::DIFlagsConstants::ZERO);
let di_encoding: u32 = 0;
let type_name = String::from("U") + bit_length.to_string().as_str();
self.builder
.create_basic_type(
"U256",
bit_length as u64,
0,
inkwell::debug_info::DIFlags::zero(),
)
.map(|basic_type| basic_type.as_type())
.create_basic_type(type_name.as_str(), bit_length as u64, di_encoding, di_flags)
.map_err(|error| anyhow::anyhow!("Debug info error: {}", error))
}
/// Finalizes the builder.
pub fn finalize(&self) {
self.builder.finalize();
/// Returns the debug-info model of word-sized integer types.
pub fn create_word_type(
&self,
flags: Option<inkwell::debug_info::DIFlags>,
) -> anyhow::Result<inkwell::debug_info::DIBasicType<'ctx>> {
self.create_primitive_type(revive_common::BIT_LENGTH_WORD, flags)
}
/// Return the DIBuilder.
pub fn builder(&self) -> &inkwell::debug_info::DebugInfoBuilder<'ctx> {
&self.builder
}
/// Return the compilation unit. {
pub fn compilation_unit(&self) -> &inkwell::debug_info::DICompileUnit<'ctx> {
&self.compile_unit
}
/// Push a debug-info scope onto the stack.
pub fn push_scope(&self, scope: DIScope<'ctx>) {
self.scope_stack.borrow_mut().push(scope)
}
/// Pop the top of the debug-info scope stack and return it.
pub fn pop_scope(&self) -> Option<DIScope<'ctx>> {
self.scope_stack.borrow_mut().pop()
}
/// Return the top of the debug-info scope stack.
pub fn top_scope(&self) -> Option<DIScope<'ctx>> {
self.scope_stack.borrow().top()
}
/// Return the number of debug-info scopes on the scope stack.
pub fn num_scopes(&self) -> usize {
self.scope_stack.borrow().len()
}
}
@@ -13,7 +13,7 @@ pub struct EVMLAData<'ctx> {
pub stack: Vec<Argument<'ctx>>,
}
impl<'ctx> EVMLAData<'ctx> {
impl EVMLAData<'_> {
/// The default stack size.
pub const DEFAULT_STACK_SIZE: usize = 64;
@@ -17,4 +17,8 @@ impl<'ctx> Declaration<'ctx> {
) -> Self {
Self { r#type, value }
}
pub fn function_value(&self) -> inkwell::values::FunctionValue<'ctx> {
self.value
}
}
@@ -11,15 +11,6 @@ use crate::polkavm::context::function::Function;
/// The functions are automatically linked to the LLVM implementations if the signatures match.
#[derive(Debug)]
pub struct LLVMRuntime<'ctx> {
/// The corresponding LLVM runtime function.
pub shl: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub shr: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub sar: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub byte: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub add_mod: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
@@ -31,32 +22,9 @@ pub struct LLVMRuntime<'ctx> {
/// The corresponding LLVM runtime function.
pub sha3: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub r#return: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub revert: FunctionDeclaration<'ctx>,
}
impl<'ctx> LLVMRuntime<'ctx> {
/// The LLVM personality function name.
pub const FUNCTION_PERSONALITY: &'static str = "__personality";
/// The LLVM exception throwing function name.
pub const FUNCTION_CXA_THROW: &'static str = "__cxa_throw";
/// The corresponding runtime function name.
pub const FUNCTION_SHL: &'static str = "__shl";
/// The corresponding runtime function name.
pub const FUNCTION_SHR: &'static str = "__shr";
/// The corresponding runtime function name.
pub const FUNCTION_SAR: &'static str = "__sar";
/// The corresponding runtime function name.
pub const FUNCTION_BYTE: &'static str = "__byte";
/// The corresponding runtime function name.
pub const FUNCTION_ADDMOD: &'static str = "__addmod";
@@ -72,121 +40,12 @@ impl<'ctx> LLVMRuntime<'ctx> {
/// The corresponding runtime function name.
pub const FUNCTION_SHA3: &'static str = "__sha3";
/// The corresponding runtime function name.
pub const FUNCTION_SYSTEM_REQUEST: &'static str = "__system_request";
/// The corresponding runtime function name.
pub const FUNCTION_FARCALL: &'static str = "__farcall";
/// The corresponding runtime function name.
pub const FUNCTION_STATICCALL: &'static str = "__staticcall";
/// The corresponding runtime function name.
pub const FUNCTION_DELEGATECALL: &'static str = "__delegatecall";
/// The corresponding runtime function name.
pub const FUNCTION_MIMICCALL: &'static str = "__mimiccall";
/// The corresponding runtime function name.
pub const FUNCTION_FARCALL_BYREF: &'static str = "__farcall_byref";
/// The corresponding runtime function name.
pub const FUNCTION_STATICCALL_BYREF: &'static str = "__staticcall_byref";
/// The corresponding runtime function name.
pub const FUNCTION_DELEGATECALL_BYREF: &'static str = "__delegatecall_byref";
/// The corresponding runtime function name.
pub const FUNCTION_MIMICCALL_BYREF: &'static str = "__mimiccall_byref";
/// The corresponding runtime function name.
pub const FUNCTION_RETURN: &'static str = "__return";
/// The corresponding runtime function name.
pub const FUNCTION_REVERT: &'static str = "__revert";
/// A shortcut constructor.
pub fn new(
llvm: &'ctx inkwell::context::Context,
module: &inkwell::module::Module<'ctx>,
optimizer: &Optimizer,
) -> Self {
let shl = Self::declare(
module,
Self::FUNCTION_SHL,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into();
2
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, shl, optimizer);
Function::set_pure_function_attributes(llvm, shl);
let shr = Self::declare(
module,
Self::FUNCTION_SHR,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into();
2
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, shr, optimizer);
Function::set_pure_function_attributes(llvm, shr);
let sar = Self::declare(
module,
Self::FUNCTION_SAR,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into();
2
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, sar, optimizer);
Function::set_pure_function_attributes(llvm, sar);
let byte = Self::declare(
module,
Self::FUNCTION_BYTE,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into();
2
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, byte, optimizer);
Function::set_pure_function_attributes(llvm, byte);
let add_mod =
Self::define(module, Self::FUNCTION_ADDMOD).expect("should be declared in stdlib");
Function::set_default_attributes(llvm, add_mod, optimizer);
@@ -236,54 +95,13 @@ impl<'ctx> LLVMRuntime<'ctx> {
false,
);
let r#return = Self::declare(
module,
Self::FUNCTION_RETURN,
llvm.void_type().fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into();
3
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, r#return, optimizer);
let revert = Self::declare(
module,
Self::FUNCTION_REVERT,
llvm.void_type().fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into();
3
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, revert, optimizer);
Self {
shl,
shr,
sar,
byte,
add_mod,
mul_mod,
exp,
sign_extend,
sha3,
r#return,
revert,
}
}
@@ -11,6 +11,8 @@ pub mod yul_data;
use std::collections::HashMap;
use inkwell::debug_info::AsDIScope;
use crate::optimizer::settings::size_level::SizeLevel;
use crate::optimizer::Optimizer;
use crate::polkavm::context::attribute::Attribute;
@@ -48,12 +50,6 @@ pub struct Function<'ctx> {
}
impl<'ctx> Function<'ctx> {
/// The near call ABI function prefix.
pub const ZKSYNC_NEAR_CALL_ABI_PREFIX: &'static str = "ZKSYNC_NEAR_CALL";
/// The near call ABI exception handler name.
pub const ZKSYNC_NEAR_CALL_ABI_EXCEPTION_HANDLER: &'static str = "ZKSYNC_CATCH_NEAR_CALL";
/// The stack hashmap default capacity.
const STACK_HASHMAP_INITIAL_CAPACITY: usize = 64;
@@ -91,13 +87,8 @@ impl<'ctx> Function<'ctx> {
|| (name.starts_with("__")
&& name != self::runtime::FUNCTION_ENTRY
&& name != self::runtime::FUNCTION_DEPLOY_CODE
&& name != self::runtime::FUNCTION_RUNTIME_CODE)
}
/// Checks whether the function is related to the near call ABI.
pub fn is_near_call_abi(name: &str) -> bool {
name.starts_with(Self::ZKSYNC_NEAR_CALL_ABI_PREFIX)
|| name == Self::ZKSYNC_NEAR_CALL_ABI_EXCEPTION_HANDLER
&& name != self::runtime::FUNCTION_RUNTIME_CODE
&& name != self::runtime::FUNCTION_LOAD_IMMUTABLE_DATA)
}
/// Returns the LLVM function declaration.
@@ -105,6 +96,14 @@ impl<'ctx> Function<'ctx> {
self.declaration
}
/// Returns the debug-info scope.
pub fn get_debug_scope(&self) -> Option<inkwell::debug_info::DIScope<'ctx>> {
self.declaration()
.function_value()
.get_subprogram()
.map(|scp| scp.as_debug_info_scope())
}
/// Returns the N-th parameter of the function.
pub fn get_nth_param(&self, index: usize) -> inkwell::values::BasicValueEnum<'ctx> {
self.declaration()
@@ -42,8 +42,7 @@ where
D: Dependency + Clone,
{
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
let function_type =
context.function_type::<inkwell::types::BasicTypeEnum>(vec![], 0, false);
let function_type = context.function_type::<inkwell::types::BasicTypeEnum>(vec![], 0);
context.add_function(
runtime::FUNCTION_DEPLOY_CODE,
function_type,
@@ -55,12 +54,14 @@ where
}
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
context.set_current_function(runtime::FUNCTION_DEPLOY_CODE)?;
context.set_current_function(runtime::FUNCTION_DEPLOY_CODE, None)?;
context.set_basic_block(context.current_function().borrow().entry_block());
context.set_code_type(CodeType::Deploy);
self.inner.into_llvm(context)?;
context.set_debug_location(0, 0, None)?;
match context
.basic_block()
.get_last_instruction()
@@ -73,8 +74,11 @@ where
}
context.set_basic_block(context.current_function().borrow().return_block());
context.set_debug_location(0, 0, None)?;
context.build_return(None);
context.pop_debug_scope();
Ok(())
}
}
@@ -5,7 +5,6 @@ use inkwell::types::BasicType;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::function::runtime;
use crate::polkavm::context::Context;
use crate::polkavm::r#const::*;
use crate::polkavm::Dependency;
use crate::polkavm::WriteLLVM;
@@ -19,26 +18,12 @@ impl Entry {
/// The call flags argument index.
pub const ARGUMENT_INDEX_CALL_FLAGS: usize = 0;
/// The number of mandatory arguments.
pub const MANDATORY_ARGUMENTS_COUNT: usize = 2;
/// Reserve 1kb for calldata.
pub const MAX_CALLDATA_SIZE: usize = 1024;
/// Initializes the global variables.
/// The pointers are not initialized, because it's not possible to create a null pointer.
pub fn initialize_globals<D>(context: &mut Context<D>) -> anyhow::Result<()>
where
D: Dependency + Clone,
{
let calldata_type = context.array_type(context.byte_type(), Self::MAX_CALLDATA_SIZE);
context.set_global(
crate::polkavm::GLOBAL_CALLDATA_POINTER,
calldata_type,
AddressSpace::Stack,
calldata_type.get_undef(),
);
context.set_global(
crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER,
context.llvm().ptr_type(AddressSpace::Heap.into()),
@@ -49,14 +34,17 @@ impl Entry {
context
.get_global(crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER)?
.into(),
context.build_sbrk(context.integer_const(crate::polkavm::XLEN, 0))?,
context.build_sbrk(
context.xlen_type().const_zero(),
context.xlen_type().const_zero(),
)?,
)?;
context.set_global(
crate::polkavm::GLOBAL_CALLDATA_SIZE,
context.word_type(),
context.xlen_type(),
AddressSpace::Stack,
context.word_undef(),
context.xlen_type().get_undef(),
);
context.set_global(
@@ -69,53 +57,22 @@ impl Entry {
Ok(())
}
/// Load the calldata via seal `input` and initialize the calldata end
/// and calldata size globals.
pub fn load_calldata<D>(context: &mut Context<D>) -> anyhow::Result<()>
/// Populate the calldata size global value.
pub fn load_calldata_size<D>(context: &mut Context<D>) -> anyhow::Result<()>
where
D: Dependency + Clone,
{
let input_pointer = context
.get_global(crate::polkavm::GLOBAL_CALLDATA_POINTER)?
let call_data_size_pointer = context
.get_global(crate::polkavm::GLOBAL_CALLDATA_SIZE)?
.value
.as_pointer_value();
let input_pointer_casted = context.builder.build_ptr_to_int(
input_pointer,
context.xlen_type(),
"input_pointer_casted",
)?;
let length_pointer = context.build_alloca_at_entry(context.xlen_type(), "len_ptr");
let length_pointer_casted = context.builder.build_ptr_to_int(
length_pointer.value,
context.xlen_type(),
"length_pointer_casted",
)?;
context.build_store(
length_pointer,
context.integer_const(crate::polkavm::XLEN, Self::MAX_CALLDATA_SIZE as u64),
)?;
context.build_runtime_call(
runtime_api::imports::INPUT,
&[input_pointer_casted.into(), length_pointer_casted.into()],
);
// Store the calldata size
let calldata_size = context
.build_load(length_pointer, "input_size")?
let call_data_size_value = context
.build_runtime_call(revive_runtime_api::polkavm_imports::CALL_DATA_SIZE, &[])
.expect("the call_data_size syscall method should return a value")
.into_int_value();
let calldata_size_casted = context.builder().build_int_z_extend(
calldata_size,
context.word_type(),
"zext_input_len",
)?;
context.set_global(
crate::polkavm::GLOBAL_CALLDATA_SIZE,
context.word_type(),
AddressSpace::Stack,
calldata_size_casted,
);
context
.builder()
.build_store(call_data_size_pointer, call_data_size_value)?;
Ok(())
}
@@ -126,6 +83,8 @@ impl Entry {
where
D: Dependency + Clone,
{
context.set_debug_location(0, 0, None)?;
let is_deploy = context
.current_function()
.borrow()
@@ -176,7 +135,7 @@ where
{
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
let entry_arguments = vec![context.bool_type().as_basic_type_enum()];
let entry_function_type = context.function_type(entry_arguments, 0, false);
let entry_function_type = context.function_type(entry_arguments, 0);
context.add_function(
runtime::FUNCTION_ENTRY,
entry_function_type,
@@ -184,6 +143,18 @@ where
Some(inkwell::module::Linkage::External),
)?;
context.declare_global(
revive_runtime_api::immutable_data::GLOBAL_IMMUTABLE_DATA_POINTER,
context.word_type().array_type(0),
AddressSpace::Stack,
);
context.declare_global(
revive_runtime_api::immutable_data::GLOBAL_IMMUTABLE_DATA_SIZE,
context.xlen_type(),
AddressSpace::Stack,
);
Ok(())
}
@@ -203,17 +174,19 @@ where
true,
);
context.set_current_function(runtime::FUNCTION_ENTRY)?;
context.set_current_function(runtime::FUNCTION_ENTRY, None)?;
context.set_basic_block(context.current_function().borrow().entry_block());
Self::initialize_globals(context)?;
Self::load_calldata(context)?;
Self::load_calldata_size(context)?;
Self::leave_entry(context)?;
context.build_unconditional_branch(context.current_function().borrow().return_block());
context.set_basic_block(context.current_function().borrow().return_block());
context.build_unreachable();
context.pop_debug_scope();
Ok(())
}
}
@@ -0,0 +1,118 @@
//! The immutable data runtime function.
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::function::runtime;
use crate::polkavm::context::pointer::Pointer;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm::WriteLLVM;
/// A function for requesting the immutable data from the runtime.
/// This is a special function that is only used by the front-end generated code.
///
/// The runtime API is called lazily and subsequent calls are no-ops.
///
/// The bytes written is asserted to match the expected length.
/// This should never fail; the length is known.
/// However, this is a one time assertion, hence worth it.
#[derive(Debug)]
pub struct ImmutableDataLoad;
impl<D> WriteLLVM<D> for ImmutableDataLoad
where
D: Dependency + Clone,
{
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
context.add_function(
runtime::FUNCTION_LOAD_IMMUTABLE_DATA,
context.void_type().fn_type(Default::default(), false),
0,
Some(inkwell::module::Linkage::External),
)?;
Ok(())
}
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
context.set_current_function(runtime::FUNCTION_LOAD_IMMUTABLE_DATA, None)?;
context.set_basic_block(context.current_function().borrow().entry_block());
let immutable_data_size_pointer = context
.get_global(revive_runtime_api::immutable_data::GLOBAL_IMMUTABLE_DATA_SIZE)?
.value
.as_pointer_value();
let immutable_data_size = context.build_load(
Pointer::new(
context.xlen_type(),
AddressSpace::Stack,
immutable_data_size_pointer,
),
"immutable_data_size_load",
)?;
let load_immutable_data_block = context.append_basic_block("load_immutables_block");
let return_block = context.current_function().borrow().return_block();
let immutable_data_size_is_zero = context.builder().build_int_compare(
inkwell::IntPredicate::EQ,
context.xlen_type().const_zero(),
immutable_data_size.into_int_value(),
"immutable_data_size_is_zero",
)?;
context.build_conditional_branch(
immutable_data_size_is_zero,
return_block,
load_immutable_data_block,
)?;
context.set_basic_block(load_immutable_data_block);
let output_pointer = context
.get_global(revive_runtime_api::immutable_data::GLOBAL_IMMUTABLE_DATA_POINTER)?
.value
.as_pointer_value();
context.build_runtime_call(
revive_runtime_api::polkavm_imports::GET_IMMUTABLE_DATA,
&[
context
.builder()
.build_ptr_to_int(output_pointer, context.xlen_type(), "ptr_to_xlen")?
.into(),
context
.builder()
.build_ptr_to_int(
immutable_data_size_pointer,
context.xlen_type(),
"ptr_to_xlen",
)?
.into(),
],
);
let bytes_written = context.builder().build_load(
context.xlen_type(),
immutable_data_size_pointer,
"bytes_written",
)?;
context.builder().build_store(
immutable_data_size_pointer,
context.xlen_type().const_zero(),
)?;
let overflow_block = context.append_basic_block("immutable_data_overflow");
let is_overflow = context.builder().build_int_compare(
inkwell::IntPredicate::UGT,
immutable_data_size.into_int_value(),
bytes_written.into_int_value(),
"is_overflow",
)?;
context.build_conditional_branch(is_overflow, overflow_block, return_block)?;
context.set_basic_block(overflow_block);
context.build_call(context.intrinsics().trap, &[], "invalid_trap");
context.build_unreachable();
context.set_basic_block(return_block);
context.build_return(None);
context.pop_debug_scope();
Ok(())
}
}
@@ -2,6 +2,7 @@
pub mod deploy_code;
pub mod entry;
pub mod immutable_data_load;
pub mod runtime_code;
/// The main entry function name.
@@ -12,3 +13,6 @@ pub const FUNCTION_DEPLOY_CODE: &str = "__deploy";
/// The runtime code function name.
pub const FUNCTION_RUNTIME_CODE: &str = "__runtime";
/// The immutable data load function name.
pub const FUNCTION_LOAD_IMMUTABLE_DATA: &str = "__immutable_data_load";
@@ -42,8 +42,7 @@ where
D: Dependency + Clone,
{
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
let function_type =
context.function_type::<inkwell::types::BasicTypeEnum>(vec![], 0, false);
let function_type = context.function_type::<inkwell::types::BasicTypeEnum>(vec![], 0);
context.add_function(
runtime::FUNCTION_RUNTIME_CODE,
function_type,
@@ -55,11 +54,14 @@ where
}
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
context.set_current_function(runtime::FUNCTION_RUNTIME_CODE)?;
context.set_current_function(runtime::FUNCTION_RUNTIME_CODE, None)?;
context.set_basic_block(context.current_function().borrow().entry_block());
context.set_code_type(CodeType::Runtime);
self.inner.into_llvm(context)?;
context.set_debug_location(0, 0, None)?;
match context
.basic_block()
.get_last_instruction()
@@ -74,6 +76,8 @@ where
context.set_basic_block(context.current_function().borrow().return_block());
context.build_unreachable();
context.pop_debug_scope();
Ok(())
}
}
@@ -37,7 +37,7 @@ impl<'ctx> Global<'ctx> {
.add_global(r#type, Some(address_space.into()), name);
let global = Self { r#type, value };
global.value.set_linkage(inkwell::module::Linkage::Private);
global.value.set_linkage(inkwell::module::Linkage::External);
global
.value
.set_visibility(inkwell::GlobalVisibility::Default);
@@ -51,4 +51,31 @@ impl<'ctx> Global<'ctx> {
global
}
/// Construct an external global.
pub fn declare<D, T>(
context: &mut Context<'ctx, D>,
r#type: T,
address_space: AddressSpace,
name: &str,
) -> Self
where
D: PolkaVMDependency + Clone,
T: BasicType<'ctx>,
{
let r#type = r#type.as_basic_type_enum();
let value = context
.module()
.add_global(r#type, Some(address_space.into()), name);
let global = Self { r#type, value };
global.value.set_linkage(inkwell::module::Linkage::External);
global
.value
.set_visibility(inkwell::GlobalVisibility::Default);
global.value.set_externally_initialized(true);
global
}
}
+228 -157
View File
@@ -5,7 +5,7 @@ pub mod argument;
pub mod attribute;
pub mod build;
pub mod code_type;
// pub mod debug_info;
pub mod debug_info;
pub mod evmla_data;
pub mod function;
pub mod global;
@@ -21,12 +21,13 @@ use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use inkwell::debug_info::AsDIScope;
use inkwell::debug_info::DIScope;
use inkwell::types::BasicType;
use inkwell::values::BasicValue;
use crate::optimizer::settings::Settings as OptimizerSettings;
use crate::optimizer::Optimizer;
use crate::polkavm::r#const::*;
use crate::polkavm::DebugConfig;
use crate::polkavm::Dependency;
use crate::target_machine::target::Target;
@@ -36,7 +37,7 @@ use self::address_space::AddressSpace;
use self::attribute::Attribute;
use self::build::Build;
use self::code_type::CodeType;
// use self::debug_info::DebugInfo;
use self::debug_info::DebugInfo;
use self::evmla_data::EVMLAData;
use self::function::declaration::Declaration as FunctionDeclaration;
use self::function::intrinsics::Intrinsics;
@@ -86,9 +87,9 @@ where
/// Whether to append the metadata hash at the end of bytecode.
include_metadata_hash: bool,
/// The debug info of the current module.
// debug_info: DebugInfo<'ctx>,
debug_info: Option<DebugInfo<'ctx>>,
/// The debug configuration telling whether to dump the needed IRs.
debug_config: Option<DebugConfig>,
debug_config: DebugConfig,
/// The Solidity data.
solidity_data: Option<SolidityData>,
@@ -136,10 +137,10 @@ where
)
.expect("the PolkaVM imports module should be linkable");
for import in runtime_api::imports::IMPORTS {
for import in revive_runtime_api::polkavm_imports::IMPORTS {
module
.get_function(import)
.expect("should be declared")
.unwrap_or_else(|| panic!("{import} import should be declared"))
.set_linkage(inkwell::module::Linkage::External);
}
}
@@ -162,6 +163,19 @@ where
})
}
fn link_immutable_data(&self, contract_path: &str) -> anyhow::Result<()> {
let size = self.solidity().immutables_size() as u32;
let immutables = revive_runtime_api::immutable_data::module(self.llvm(), size);
self.module.link_in_module(immutables).map_err(|error| {
anyhow::anyhow!(
"The contract `{}` immutable data module linking error: {}",
contract_path,
error
)
})
}
/// Configure the PolkaVM minimum stack size.
fn set_polkavm_stack_size(
llvm: &'ctx inkwell::context::Context,
@@ -189,6 +203,16 @@ where
);
}
/// Configure the revive datalayout.
fn set_data_layout(
llvm: &'ctx inkwell::context::Context,
module: &inkwell::module::Module<'ctx>,
) {
let source_module = revive_stdlib::module(llvm, "revive_stdlib").unwrap();
let data_layout = source_module.get_data_layout();
module.set_data_layout(&data_layout);
}
/// Initializes a new LLVM context.
pub fn new(
llvm: &'ctx inkwell::context::Context,
@@ -196,8 +220,9 @@ where
optimizer: Optimizer,
dependency_manager: Option<D>,
include_metadata_hash: bool,
debug_config: Option<DebugConfig>,
debug_config: DebugConfig,
) -> Self {
Self::set_data_layout(llvm, &module);
Self::link_stdlib_module(llvm, &module);
Self::link_polkavm_imports(llvm, &module);
Self::set_polkavm_stack_size(llvm, &module, Self::POLKAVM_STACK_SIZE);
@@ -205,6 +230,11 @@ where
let intrinsics = Intrinsics::new(llvm, &module);
let llvm_runtime = LLVMRuntime::new(llvm, &module, &optimizer);
let debug_info = debug_config.emit_debug_info.then(|| {
let debug_info = DebugInfo::new(&module);
debug_info.initialize_module(llvm, &module);
debug_info
});
Self {
llvm,
@@ -221,7 +251,8 @@ where
dependency_manager,
include_metadata_hash,
// debug_info,
debug_info,
debug_config,
solidity_data: None,
@@ -239,13 +270,14 @@ where
let module_clone = self.module.clone();
self.link_polkavm_exports(contract_path)?;
self.link_immutable_data(contract_path)?;
let target_machine = TargetMachine::new(Target::PVM, self.optimizer.settings())?;
target_machine.set_target_data(self.module());
self.module().set_triple(&target_machine.get_triple());
self.debug_config
.dump_llvm_ir_unoptimized(contract_path, self.module())?;
if let Some(ref debug_config) = self.debug_config {
debug_config.dump_llvm_ir_unoptimized(contract_path, self.module())?;
}
self.verify().map_err(|error| {
anyhow::anyhow!(
"The contract `{}` unoptimized LLVM IR verification error: {}",
@@ -263,9 +295,10 @@ where
error
)
})?;
if let Some(ref debug_config) = self.debug_config {
debug_config.dump_llvm_ir_optimized(contract_path, self.module())?;
}
self.debug_config
.dump_llvm_ir_optimized(contract_path, self.module())?;
self.verify().map_err(|error| {
anyhow::anyhow!(
"The contract `{}` optimized LLVM IR verification error: {}",
@@ -284,11 +317,17 @@ where
)
})?;
let bytecode = revive_linker::link(buffer.as_slice())?;
let shared_object = revive_linker::link(buffer.as_slice())?;
self.debug_config
.dump_object(contract_path, &shared_object)?;
let polkavm_bytecode =
revive_linker::polkavm_linker(shared_object, !self.debug_config().emit_debug_info)?;
let build = match crate::polkavm::build_assembly_text(
contract_path,
&bytecode,
&polkavm_bytecode,
metadata_hash,
self.debug_config(),
) {
@@ -381,6 +420,15 @@ where
}
}
/// Declare an external global.
pub fn declare_global<T>(&mut self, name: &str, r#type: T, address_space: AddressSpace)
where
T: BasicType<'ctx> + Clone + Copy,
{
let global = Global::declare(self, r#type, address_space, name);
self.globals.insert(name.to_owned(), global);
}
/// Returns the LLVM intrinsics collection reference.
pub fn intrinsics(&self) -> &Intrinsics<'ctx> {
&self.intrinsics
@@ -391,49 +439,31 @@ where
&self.llvm_runtime
}
/// Declare a function already existing in the module.
pub fn declare_extern_function(
&mut self,
name: &str,
) -> anyhow::Result<Rc<RefCell<Function<'ctx>>>> {
let function = self.module().get_function(name).ok_or_else(|| {
anyhow::anyhow!("Failed to activate an undeclared function `{}`", name)
})?;
let basic_block = self.llvm.append_basic_block(function, name);
let declaration = FunctionDeclaration::new(
self.function_type::<inkwell::types::BasicTypeEnum>(vec![], 0, false),
function,
);
let function = Function::new(
name.to_owned(),
declaration,
FunctionReturn::None,
basic_block,
basic_block,
);
Function::set_default_attributes(self.llvm, function.declaration(), &self.optimizer);
let function = Rc::new(RefCell::new(function));
self.functions.insert(name.to_string(), function.clone());
Ok(function)
}
/// Appends a function to the current module.
pub fn add_function(
&mut self,
name: &str,
r#type: inkwell::types::FunctionType<'ctx>,
return_values_length: usize,
mut linkage: Option<inkwell::module::Linkage>,
linkage: Option<inkwell::module::Linkage>,
) -> anyhow::Result<Rc<RefCell<Function<'ctx>>>> {
if Function::is_near_call_abi(name) && self.is_system_mode() {
linkage = Some(inkwell::module::Linkage::External);
}
let value = self.module().add_function(name, r#type, linkage);
if self.debug_info().is_some() {
self.builder().unset_current_debug_location();
let func_scope = match value.get_subprogram() {
None => {
let fn_name = value.get_name().to_str()?;
let scp = self.build_function_debug_info(fn_name, 0)?;
value.set_subprogram(scp);
scp
}
Some(scp) => scp,
};
self.push_debug_scope(func_scope.as_debug_info_scope());
self.set_debug_location(0, 0, Some(func_scope.as_debug_info_scope()))?;
}
let entry_block = self.llvm.append_basic_block(value, "entry");
let return_block = self.llvm.append_basic_block(value, "return");
@@ -444,12 +474,6 @@ where
let pointer = self.build_alloca(self.word_type(), "return_pointer");
FunctionReturn::primitive(pointer)
}
size if name.starts_with(Function::ZKSYNC_NEAR_CALL_ABI_PREFIX) => {
let first_argument = value.get_first_param().expect("Always exists");
let r#type = self.structure_type(vec![self.word_type(); size].as_slice());
let pointer = first_argument.into_pointer_value();
FunctionReturn::compound(Pointer::new(r#type, AddressSpace::Stack, pointer), size)
}
size => {
self.set_basic_block(entry_block);
let pointer = self.build_alloca(
@@ -470,13 +494,11 @@ where
return_block,
);
Function::set_default_attributes(self.llvm, function.declaration(), &self.optimizer);
if Function::is_near_call_abi(function.name()) && self.is_system_mode() {
Function::set_exception_handler_attributes(self.llvm, function.declaration());
}
let function = Rc::new(RefCell::new(function));
self.functions.insert(name.to_string(), function.clone());
self.pop_debug_scope();
Ok(function)
}
@@ -492,15 +514,95 @@ where
.expect("Must be declared before use")
}
/// Sets the current active function.
pub fn set_current_function(&mut self, name: &str) -> anyhow::Result<()> {
/// Sets the current active function. If debug-info generation is enabled,
/// constructs a debug-scope and pushes in on the scope-stack.
pub fn set_current_function(&mut self, name: &str, line: Option<u32>) -> anyhow::Result<()> {
let function = self.functions.get(name).cloned().ok_or_else(|| {
anyhow::anyhow!("Failed to activate an undeclared function `{}`", name)
})?;
self.current_function = Some(function);
if let Some(scope) = self.current_function().borrow().get_debug_scope() {
self.push_debug_scope(scope);
}
self.set_debug_location(line.unwrap_or_default(), 0, None)?;
Ok(())
}
/// Builds a debug-info scope for a function.
pub fn build_function_debug_info(
&self,
name: &str,
line_no: u32,
) -> anyhow::Result<inkwell::debug_info::DISubprogram<'ctx>> {
let Some(debug_info) = self.debug_info() else {
anyhow::bail!("expected debug-info builders");
};
let builder = debug_info.builder();
let file = debug_info.compilation_unit().get_file();
let scope = file.as_debug_info_scope();
let flags = inkwell::debug_info::DIFlagsConstants::PUBLIC;
let return_type = debug_info.create_word_type(Some(flags))?.as_type();
let subroutine_type = builder.create_subroutine_type(file, Some(return_type), &[], flags);
Ok(builder.create_function(
scope,
name,
None,
file,
line_no,
subroutine_type,
false,
true,
1,
flags,
false,
))
}
/// Set the debug info location.
///
/// No-op if the emitting debug info is disabled.
///
/// If `scope` is `None` the top scope will be used.
pub fn set_debug_location(
&self,
line: u32,
column: u32,
scope: Option<DIScope<'ctx>>,
) -> anyhow::Result<()> {
let Some(debug_info) = self.debug_info() else {
return Ok(());
};
let scope = match scope {
Some(scp) => scp,
None => debug_info.top_scope().expect("expected a debug-info scope"),
};
let location =
debug_info
.builder()
.create_debug_location(self.llvm(), line, column, scope, None);
self.builder().set_current_debug_location(location);
Ok(())
}
/// Pushes a debug-info scope to the stack.
pub fn push_debug_scope(&self, scope: DIScope<'ctx>) {
if let Some(debug_info) = self.debug_info() {
debug_info.push_scope(scope);
}
}
/// Pops the top of the debug-info scope stack.
pub fn pop_debug_scope(&self) {
if let Some(debug_info) = self.debug_info() {
debug_info.pop_scope();
}
}
/// Pushes a new loop context to the stack.
pub fn push_loop(
&mut self,
@@ -534,10 +636,6 @@ where
manager,
name,
self.optimizer.settings().to_owned(),
self.yul_data
.as_ref()
.map(|data| data.is_system_mode())
.unwrap_or_default(),
self.include_metadata_hash,
self.debug_config.clone(),
)
@@ -574,9 +672,14 @@ where
.expect("The dependency manager is unset")
}
/// Returns the debug info.
pub fn debug_info(&self) -> Option<&DebugInfo<'ctx>> {
self.debug_info.as_ref()
}
/// Returns the debug config reference.
pub fn debug_config(&self) -> Option<&DebugConfig> {
self.debug_config.as_ref()
pub fn debug_config(&self) -> &DebugConfig {
&self.debug_config
}
/// Appends a new basic block to the current function.
@@ -632,6 +735,23 @@ where
Pointer::new(r#type, AddressSpace::Stack, pointer)
}
/// Truncate `address` to the ethereum address length and store it as bytes on the stack.
/// The stack allocation will be at the function entry. Returns the stack pointer.
/// This helper should be used when passing address arguments to the runtime, ensuring correct size and endianness.
pub fn build_address_argument_store(
&self,
address: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<Pointer<'ctx>> {
let address_type = self.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS);
let address_pointer = self.build_alloca_at_entry(address_type, "address_pointer");
let address_truncated =
self.builder()
.build_int_truncate(address, address_type, "address_truncated")?;
let address_swapped = self.build_byte_swap(address_truncated.into())?;
self.build_store(address_pointer, address_swapped)?;
Ok(address_pointer)
}
/// Load the address at given pointer and zero extend it to the VM word size.
pub fn build_load_address(
&self,
@@ -678,22 +798,10 @@ where
self.build_byte_swap(value)
}
AddressSpace::Storage | AddressSpace::TransientStorage => {
let storage_key_value = self.builder().build_ptr_to_int(
pointer.value,
self.word_type(),
"storage_ptr_to_int",
)?;
let storage_key_pointer = self.build_alloca(self.word_type(), "storage_key");
let storage_key_pointer_casted = self.builder().build_ptr_to_int(
storage_key_pointer.value,
self.xlen_type(),
"storage_key_pointer_casted",
)?;
self.builder()
.build_store(storage_key_pointer.value, storage_key_value)?;
let storage_value_pointer =
self.build_alloca(self.word_type(), "storage_value_pointer");
self.build_store(storage_value_pointer, self.word_const(0))?;
let storage_value_length_pointer =
self.build_alloca(self.xlen_type(), "storage_value_length_pointer");
self.build_store(
@@ -704,10 +812,10 @@ where
let transient = pointer.address_space == AddressSpace::TransientStorage;
self.build_runtime_call(
runtime_api::imports::GET_STORAGE,
revive_runtime_api::polkavm_imports::GET_STORAGE,
&[
self.xlen_type().const_int(transient as u64, false).into(),
storage_key_pointer_casted.into(),
pointer.to_int(self).into(),
self.xlen_type().const_all_ones().into(),
storage_value_pointer.to_int(self).into(),
storage_value_length_pointer.to_int(self).into(),
@@ -776,18 +884,6 @@ where
self.word_type().as_basic_type_enum()
);
let storage_key_value = self.builder().build_ptr_to_int(
pointer.value,
self.word_type(),
"storage_ptr_to_int",
)?;
let storage_key_pointer = self.build_alloca(self.word_type(), "storage_key");
let storage_key_pointer_casted = self.builder().build_ptr_to_int(
storage_key_pointer.value,
self.xlen_type(),
"storage_key_pointer_casted",
)?;
let storage_value_pointer = self.build_alloca(self.word_type(), "storage_value");
let storage_value_pointer_casted = self.builder().build_ptr_to_int(
storage_value_pointer.value,
@@ -795,18 +891,16 @@ where
"storage_value_pointer_casted",
)?;
self.builder()
.build_store(storage_key_pointer.value, storage_key_value)?;
self.builder()
.build_store(storage_value_pointer.value, value)?;
let transient = pointer.address_space == AddressSpace::TransientStorage;
self.build_runtime_call(
runtime_api::imports::SET_STORAGE,
revive_runtime_api::polkavm_imports::SET_STORAGE,
&[
self.xlen_type().const_int(transient as u64, false).into(),
storage_key_pointer_casted.into(),
pointer.to_int(self).into(),
self.xlen_type().const_all_ones().into(),
storage_value_pointer_casted.into(),
self.integer_const(crate::polkavm::XLEN, 32).into(),
@@ -915,7 +1009,7 @@ where
.copied()
.map(inkwell::values::BasicMetadataValueEnum::from)
.collect::<Vec<_>>(),
&format!("runtime API call {name}"),
&format!("runtime_api_{name}_return_value"),
)
.unwrap()
.try_as_basic_value()
@@ -1038,7 +1132,7 @@ where
)?;
self.build_runtime_call(
runtime_api::imports::RETURN,
revive_runtime_api::polkavm_imports::RETURN,
&[flags.into(), offset_pointer.into(), length_pointer.into()],
);
self.build_unreachable();
@@ -1096,16 +1190,20 @@ where
Ok(truncated)
}
/// Build a call to PolkaVM `sbrk` for extending the heap by `size`.
/// Build a call to PolkaVM `sbrk` for extending the heap from offset by `size`.
/// The allocation is aligned to 32 bytes.
///
/// This emulates the EVM linear memory until the runtime supports metered memory.
pub fn build_sbrk(
&self,
offset: inkwell::values::IntValue<'ctx>,
size: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::PointerValue<'ctx>> {
Ok(self
.builder()
.build_call(
self.runtime_api_method(runtime_api::SBRK),
&[size.into()],
self.runtime_api_method(revive_runtime_api::polkavm_imports::SBRK),
&[offset.into(), size.into()],
"call_sbrk",
)?
.try_as_basic_value()
@@ -1114,14 +1212,29 @@ where
.into_pointer_value())
}
/// Call PolkaVM `sbrk` for extending the heap by `size`,
/// Build a call to PolkaVM `msize` for querying the linear memory size.
pub fn build_msize(&self) -> anyhow::Result<inkwell::values::IntValue<'ctx>> {
Ok(self
.builder()
.build_call(
self.runtime_api_method(revive_runtime_api::polkavm_imports::MEMORY_SIZE),
&[],
"call_msize",
)?
.try_as_basic_value()
.left()
.expect("sbrk returns an int")
.into_int_value())
}
/// Call PolkaVM `sbrk` for extending the heap by `offset` + `size`,
/// trapping the contract if the call failed.
/// Returns the end of memory pointer.
pub fn build_heap_alloc(
&self,
offset: inkwell::values::IntValue<'ctx>,
size: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::PointerValue<'ctx>> {
let end_of_memory = self.build_sbrk(size)?;
) -> anyhow::Result<()> {
let end_of_memory = self.build_sbrk(offset, size)?;
let return_is_nil = self.builder().build_int_compare(
inkwell::IntPredicate::EQ,
end_of_memory,
@@ -1139,7 +1252,7 @@ where
self.set_basic_block(continue_block);
Ok(end_of_memory)
Ok(())
}
/// Returns a pointer to `offset` into the heap, allocating
@@ -1154,40 +1267,12 @@ where
assert_eq!(offset.get_type(), self.xlen_type());
assert_eq!(length.get_type(), self.xlen_type());
self.build_heap_alloc(offset, length)?;
let heap_start = self
.get_global(crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER)?
.value
.as_pointer_value();
let heap_end = self.build_sbrk(self.integer_const(crate::polkavm::XLEN, 0))?;
let value_end = self.build_gep(
Pointer::new(self.byte_type(), AddressSpace::Stack, heap_start),
&[self.builder().build_int_nuw_add(offset, length, "end")?],
self.byte_type(),
"heap_end_gep",
);
let is_out_of_bounds = self.builder().build_int_compare(
inkwell::IntPredicate::UGT,
value_end.value,
heap_end,
"is_value_overflowing_heap",
)?;
let out_of_bounds_block = self.append_basic_block("heap_offset_out_of_bounds");
let heap_offset_block = self.append_basic_block("build_heap_pointer");
self.build_conditional_branch(is_out_of_bounds, out_of_bounds_block, heap_offset_block)?;
self.set_basic_block(out_of_bounds_block);
let size = self.builder().build_int_nuw_sub(
self.builder()
.build_ptr_to_int(value_end.value, self.xlen_type(), "value_end")?,
self.builder()
.build_ptr_to_int(heap_end, self.xlen_type(), "heap_end")?,
"heap_alloc_size",
)?;
self.build_heap_alloc(size)?;
self.build_unconditional_branch(heap_offset_block);
self.set_basic_block(heap_offset_block);
Ok(self.build_gep(
Pointer::new(self.byte_type(), AddressSpace::Stack, heap_start),
&[offset],
@@ -1308,12 +1393,11 @@ where
&self,
argument_types: Vec<T>,
return_values_size: usize,
is_near_call_abi: bool,
) -> inkwell::types::FunctionType<'ctx>
where
T: BasicType<'ctx>,
{
let mut argument_types: Vec<inkwell::types::BasicMetadataTypeEnum> = argument_types
let argument_types: Vec<inkwell::types::BasicMetadataTypeEnum> = argument_types
.as_slice()
.iter()
.map(T::as_basic_type_enum)
@@ -1325,11 +1409,6 @@ where
.void_type()
.fn_type(argument_types.as_slice(), false),
1 => self.word_type().fn_type(argument_types.as_slice(), false),
_size if is_near_call_abi && self.is_system_mode() => {
let return_type = self.llvm().ptr_type(AddressSpace::Stack.into());
argument_types.insert(0, return_type.as_basic_type_enum().into());
return_type.fn_type(argument_types.as_slice(), false)
}
size => self
.structure_type(vec![self.word_type().as_basic_type_enum(); size].as_slice())
.fn_type(argument_types.as_slice(), false),
@@ -1522,12 +1601,4 @@ where
anyhow::bail!("The immutable size data is not available");
}
}
/// Whether the system mode is enabled.
pub fn is_system_mode(&self) -> bool {
self.yul_data
.as_ref()
.map(|data| data.is_system_mode())
.unwrap_or_default()
}
}
@@ -17,7 +17,7 @@ impl SolidityData {
Self::default()
}
/// Returns the current number of immutables values in the contract.
/// Returns the current size of immutable values in the contract.
pub fn immutables_size(&self) -> usize {
self.immutables.len() * revive_common::BYTE_LENGTH_WORD
}
@@ -15,7 +15,7 @@ pub fn create_context(
let module = llvm.create_module("test");
let optimizer = Optimizer::new(optimizer_settings);
Context::<DummyDependency>::new(llvm, module, optimizer, None, true, None)
Context::<DummyDependency>::new(llvm, module, optimizer, None, true, Default::default())
}
#[test]
@@ -8,28 +8,12 @@ use num::Zero;
/// Describes some data that is only relevant to Yul.
#[derive(Debug, Default)]
pub struct YulData {
/// The system mode flag.
/// The call simulations only work if this mode is enabled.
is_system_mode: bool,
/// The list of constant arrays in the code section.
/// It is a temporary storage used until the finalization method is called.
const_arrays: BTreeMap<u8, Vec<num::BigUint>>,
}
impl YulData {
/// A shortcut constructor.
pub fn new(is_system_mode: bool) -> Self {
Self {
is_system_mode,
const_arrays: BTreeMap::new(),
}
}
/// Whether the system mode is enabled.
pub fn is_system_mode(&self) -> bool {
self.is_system_mode
}
/// Declares a temporary constant array representation.
pub fn const_array_declare(&mut self, index: u8, size: u16) -> anyhow::Result<()> {
if self.const_arrays.contains_key(&index) {
+80 -24
View File
@@ -5,7 +5,6 @@ use inkwell::values::BasicValue;
use crate::polkavm::context::argument::Argument;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api;
const STATIC_CALL_FLAG: u32 = 0b0001_0000;
const REENTRANT_CALL_FLAG: u32 = 0b0000_1000;
@@ -14,7 +13,7 @@ const REENTRANT_CALL_FLAG: u32 = 0b0000_1000;
#[allow(clippy::too_many_arguments)]
pub fn call<'ctx, D>(
context: &mut Context<'ctx, D>,
_gas: inkwell::values::IntValue<'ctx>,
gas: inkwell::values::IntValue<'ctx>,
address: inkwell::values::IntValue<'ctx>,
value: Option<inkwell::values::IntValue<'ctx>>,
input_offset: inkwell::values::IntValue<'ctx>,
@@ -27,14 +26,7 @@ pub fn call<'ctx, D>(
where
D: Dependency + Clone,
{
let address_type = context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS);
let address_pointer = context.build_alloca_at_entry(address_type, "address_pointer");
let address_truncated =
context
.builder()
.build_int_truncate(address, address_type, "address_truncated")?;
let address_swapped = context.build_byte_swap(address_truncated.into())?;
context.build_store(address_pointer, address_swapped)?;
let address_pointer = context.build_address_argument_store(address)?;
let value = value.unwrap_or_else(|| context.word_const(0));
let value_pointer = context.build_alloca_at_entry(context.word_type(), "value_pointer");
@@ -48,7 +40,7 @@ where
// TODO: What to supply here? Is there a weight to gas?
let _gas = context
.builder()
.build_int_truncate(_gas, context.integer_type(64), "gas")?;
.build_int_truncate(gas, context.integer_type(64), "gas")?;
let input_pointer = context.build_heap_gep(input_offset, input_length)?;
let output_pointer = context.build_heap_gep(output_offset, output_length)?;
@@ -68,8 +60,8 @@ where
let arguments = &[
flags.as_basic_value_enum(),
address_pointer.value.as_basic_value_enum(),
context.integer_const(64, 0).as_basic_value_enum(),
context.integer_const(64, 0).as_basic_value_enum(),
context.integer_const(64, u64::MAX).as_basic_value_enum(),
context.integer_const(64, u64::MAX).as_basic_value_enum(),
context.sentinel_pointer().value.as_basic_value_enum(),
value_pointer.value.as_basic_value_enum(),
input_pointer.value.as_basic_value_enum(),
@@ -84,7 +76,7 @@ where
arguments,
)?;
let name = runtime_api::imports::CALL;
let name = revive_runtime_api::polkavm_imports::CALL;
let argument_pointer = context.builder().build_ptr_to_int(
argument_pointer.value,
context.xlen_type(),
@@ -98,7 +90,7 @@ where
let is_success = context.builder().build_int_compare(
inkwell::IntPredicate::EQ,
success,
context.xlen_type().const_zero(),
context.integer_const(revive_common::BIT_LENGTH_X64, 0),
"is_success",
)?;
@@ -110,20 +102,84 @@ where
#[allow(clippy::too_many_arguments)]
pub fn delegate_call<'ctx, D>(
_context: &mut Context<'ctx, D>,
_gas: inkwell::values::IntValue<'ctx>,
_address: inkwell::values::IntValue<'ctx>,
_value: Option<inkwell::values::IntValue<'ctx>>,
_input_offset: inkwell::values::IntValue<'ctx>,
_input_length: inkwell::values::IntValue<'ctx>,
_output_offset: inkwell::values::IntValue<'ctx>,
_output_length: inkwell::values::IntValue<'ctx>,
context: &mut Context<'ctx, D>,
gas: inkwell::values::IntValue<'ctx>,
address: inkwell::values::IntValue<'ctx>,
input_offset: inkwell::values::IntValue<'ctx>,
input_length: inkwell::values::IntValue<'ctx>,
output_offset: inkwell::values::IntValue<'ctx>,
output_length: inkwell::values::IntValue<'ctx>,
_constants: Vec<Option<num::BigUint>>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
let address_pointer = context.build_address_argument_store(address)?;
let input_offset = context.safe_truncate_int_to_xlen(input_offset)?;
let input_length = context.safe_truncate_int_to_xlen(input_length)?;
let output_offset = context.safe_truncate_int_to_xlen(output_offset)?;
let output_length = context.safe_truncate_int_to_xlen(output_length)?;
// TODO: What to supply here? Is there a weight to gas?
let _gas = context
.builder()
.build_int_truncate(gas, context.integer_type(64), "gas")?;
let input_pointer = context.build_heap_gep(input_offset, input_length)?;
let output_pointer = context.build_heap_gep(output_offset, output_length)?;
let output_length_pointer = context.build_alloca_at_entry(context.xlen_type(), "output_length");
context.build_store(output_length_pointer, output_length)?;
let flags = context.xlen_type().const_int(0u64, false);
let argument_type = revive_runtime_api::calling_convention::delegate_call(context.llvm());
let argument_pointer = context.build_alloca_at_entry(argument_type, "delegate_call_arguments");
let arguments = &[
flags.as_basic_value_enum(),
address_pointer.value.as_basic_value_enum(),
context
.integer_const(revive_common::BIT_LENGTH_X64, u64::MAX)
.as_basic_value_enum(),
context
.integer_const(revive_common::BIT_LENGTH_X64, u64::MAX)
.as_basic_value_enum(),
context.sentinel_pointer().value.as_basic_value_enum(),
input_pointer.value.as_basic_value_enum(),
input_length.as_basic_value_enum(),
output_pointer.value.as_basic_value_enum(),
output_length_pointer.value.as_basic_value_enum(),
];
revive_runtime_api::calling_convention::spill(
context.builder(),
argument_pointer.value,
argument_type,
arguments,
)?;
let name = revive_runtime_api::polkavm_imports::DELEGATE_CALL;
let argument_pointer = context.builder().build_ptr_to_int(
argument_pointer.value,
context.xlen_type(),
"delegate_call_argument_pointer",
)?;
let success = context
.build_runtime_call(name, &[argument_pointer.into()])
.unwrap_or_else(|| panic!("{name} should return a value"))
.into_int_value();
let is_success = context.builder().build_int_compare(
inkwell::IntPredicate::EQ,
success,
context.integer_const(revive_common::BIT_LENGTH_X64, 0),
"is_success",
)?;
Ok(context
.builder()
.build_int_z_extend(is_success, context.word_type(), "success")?
.as_basic_value_enum())
}
/// Translates the Yul `linkersymbol` instruction.
+27 -29
View File
@@ -1,10 +1,7 @@
//! Translates the calldata instructions.
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::pointer::Pointer;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use inkwell::types::BasicType;
/// Translates the calldata load.
pub fn load<'ctx, D>(
@@ -14,19 +11,15 @@ pub fn load<'ctx, D>(
where
D: Dependency + Clone,
{
let calldata_pointer = context
.get_global(crate::polkavm::GLOBAL_CALLDATA_POINTER)?
.value
.as_pointer_value();
let offset = context.build_gep(
Pointer::new(context.byte_type(), AddressSpace::Stack, calldata_pointer),
&[offset],
context.word_type().as_basic_type_enum(),
"calldata_pointer_with_offset",
let output_pointer = context.build_alloca_at_entry(context.word_type(), "call_data_output");
let offset = context.safe_truncate_int_to_xlen(offset)?;
context.build_runtime_call(
revive_runtime_api::polkavm_imports::CALL_DATA_LOAD,
&[output_pointer.to_int(context).into(), offset.into()],
);
context
.build_load(offset, "calldata_value")
.and_then(|value| context.build_byte_swap(value))
context.build_load(output_pointer, "call_data_load_value")
}
/// Translates the calldata size.
@@ -37,8 +30,14 @@ where
D: Dependency + Clone,
{
let value = context.get_global_value(crate::polkavm::GLOBAL_CALLDATA_SIZE)?;
Ok(value)
Ok(context
.builder()
.build_int_z_extend(
value.into_int_value(),
context.word_type(),
"call_data_size_value",
)?
.into())
}
/// Translates the calldata copy.
@@ -51,20 +50,19 @@ pub fn copy<'ctx, D>(
where
D: Dependency + Clone,
{
let offset = context.safe_truncate_int_to_xlen(destination_offset)?;
let source_offset = context.safe_truncate_int_to_xlen(source_offset)?;
let size = context.safe_truncate_int_to_xlen(size)?;
let destination = context.build_heap_gep(offset, size)?;
let destination_offset = context.safe_truncate_int_to_xlen(destination_offset)?;
let output_pointer = context.build_heap_gep(destination_offset, size)?;
let calldata_pointer = context
.get_global(crate::polkavm::GLOBAL_CALLDATA_POINTER)?
.value
.as_pointer_value();
let source = context.build_gep(
Pointer::new(context.byte_type(), AddressSpace::Stack, calldata_pointer),
&[context.safe_truncate_int_to_xlen(source_offset)?],
context.byte_type(),
"calldata_pointer_with_offset",
context.build_runtime_call(
revive_runtime_api::polkavm_imports::CALL_DATA_COPY,
&[
output_pointer.to_int(context).into(),
size.into(),
source_offset.into(),
],
);
context.build_memcpy(destination, source, size, "calldata_copy_memcpy_from_child")
Ok(())
}
+49 -43
View File
@@ -4,36 +4,58 @@ use inkwell::values::BasicValue;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api;
/// Translates the `gas_limit` instruction.
pub fn gas_limit<'ctx, D>(
_context: &mut Context<'ctx, D>,
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
let gas_limit_value = context
.build_runtime_call(revive_runtime_api::polkavm_imports::GAS_LIMIT, &[])
.expect("the gas_limit syscall method should return a value")
.into_int_value();
Ok(context
.builder()
.build_int_z_extend(gas_limit_value, context.word_type(), "gas_limit")?
.into())
}
/// Translates the `gas_price` instruction.
pub fn gas_price<'ctx, D>(
_context: &mut Context<'ctx, D>,
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
let gas_price_value = context
.build_runtime_call(revive_runtime_api::polkavm_imports::GAS_PRICE, &[])
.expect("the gas_price syscall method should return a value")
.into_int_value();
Ok(context
.builder()
.build_int_z_extend(gas_price_value, context.word_type(), "gas_price")?
.into())
}
/// Translates the `tx.origin` instruction.
pub fn origin<'ctx, D>(
_context: &mut Context<'ctx, D>,
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
let address_type = context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS);
let address_pointer = context.build_alloca_at_entry(address_type, "origin_address");
context.build_store(address_pointer, address_type.const_zero())?;
context.build_runtime_call(
revive_runtime_api::polkavm_imports::ORIGIN,
&[address_pointer.to_int(context).into()],
);
context.build_load_address(address_pointer)
}
/// Translates the `chain_id` instruction.
@@ -43,7 +65,7 @@ pub fn chain_id<'ctx, D>(
where
D: Dependency + Clone,
{
context.build_runtime_call_to_getter(runtime_api::imports::CHAIN_ID)
context.build_runtime_call_to_getter(revive_runtime_api::polkavm_imports::CHAIN_ID)
}
/// Translates the `block_number` instruction.
@@ -53,7 +75,7 @@ pub fn block_number<'ctx, D>(
where
D: Dependency + Clone,
{
context.build_runtime_call_to_getter(runtime_api::imports::BLOCK_NUMBER)
context.build_runtime_call_to_getter(revive_runtime_api::polkavm_imports::BLOCK_NUMBER)
}
/// Translates the `block_timestamp` instruction.
@@ -63,18 +85,29 @@ pub fn block_timestamp<'ctx, D>(
where
D: Dependency + Clone,
{
context.build_runtime_call_to_getter(runtime_api::imports::NOW)
context.build_runtime_call_to_getter(revive_runtime_api::polkavm_imports::NOW)
}
/// Translates the `block_hash` instruction.
pub fn block_hash<'ctx, D>(
_context: &mut Context<'ctx, D>,
_index: inkwell::values::IntValue<'ctx>,
context: &mut Context<'ctx, D>,
index: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
let output_pointer = context.build_alloca_at_entry(context.word_type(), "blockhash_out_ptr");
let index_ptr = context.build_alloca_at_entry(context.word_type(), "blockhash_index_ptr");
context.build_store(index_ptr, index)?;
context.build_runtime_call(
revive_runtime_api::polkavm_imports::BLOCK_HASH,
&[
index_ptr.to_int(context).into(),
output_pointer.to_int(context).into(),
],
);
context.build_byte_swap(context.build_load(output_pointer, "block_hash")?)
}
/// Translates the `difficulty` instruction.
@@ -104,34 +137,7 @@ pub fn basefee<'ctx, D>(
where
D: Dependency + Clone,
{
Ok(context.word_const(0).as_basic_value_enum())
}
/// Translates the `msize` instruction.
pub fn msize<'ctx, D>(
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
let heap_end = context.build_sbrk(context.xlen_type().const_zero())?;
let heap_start = context
.get_global(crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER)?
.value
.as_pointer_value();
let heap_size = context.builder().build_int_nuw_sub(
context
.builder()
.build_ptr_to_int(heap_end, context.xlen_type(), "heap_end")?,
context
.builder()
.build_ptr_to_int(heap_start, context.xlen_type(), "heap_start")?,
"heap_size",
)?;
Ok(context
.builder()
.build_int_z_extend(heap_size, context.word_type(), "heap_size_extended")?
.as_basic_value_enum())
context.build_runtime_call_to_getter(revive_runtime_api::polkavm_imports::BASE_FEE)
}
/// Translates the `address` instruction.
@@ -146,7 +152,7 @@ where
"address_output",
);
context.build_runtime_call(
runtime_api::imports::ADDRESS,
revive_runtime_api::polkavm_imports::ADDRESS,
&[pointer.to_int(context).into()],
);
context.build_load_address(pointer)
@@ -164,7 +170,7 @@ where
"address_output",
);
context.build_runtime_call(
runtime_api::imports::CALLER,
revive_runtime_api::polkavm_imports::CALLER,
&[pointer.to_int(context).into()],
);
context.build_load_address(pointer)
@@ -7,7 +7,6 @@ use crate::polkavm::context::argument::Argument;
use crate::polkavm::context::code_type::CodeType;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api;
/// Translates the contract `create` and `create2` instruction.
///
@@ -82,7 +81,7 @@ where
"instantiate_argument_pointer",
)?;
context.build_runtime_call(
runtime_api::imports::INSTANTIATE,
revive_runtime_api::polkavm_imports::INSTANTIATE,
&[argument_pointer.into()],
);
@@ -2,7 +2,6 @@
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api;
/// Translates the `sha3` instruction.
pub fn sha3<'ctx, D>(
@@ -19,7 +18,7 @@ where
let output_pointer = context.build_alloca(context.word_type(), "output_pointer");
context.build_runtime_call(
runtime_api::imports::HASH_KECCAK_256,
revive_runtime_api::polkavm_imports::HASH_KECCAK_256,
&[
input_pointer.to_int(context).into(),
length_casted.into(),
@@ -1,10 +1,7 @@
//! Translates the value and balance operations.
use inkwell::values::BasicValue;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api;
/// Translates the `gas` instruction.
pub fn gas<'ctx, D>(
@@ -13,7 +10,15 @@ pub fn gas<'ctx, D>(
where
D: Dependency + Clone,
{
Ok(context.integer_const(256, 0).as_basic_value_enum())
let ref_time_left_value = context
.build_runtime_call(revive_runtime_api::polkavm_imports::REF_TIME_LEFT, &[])
.expect("the ref_time_left syscall method should return a value")
.into_int_value();
Ok(context
.builder()
.build_int_z_extend(ref_time_left_value, context.word_type(), "gas_left")?
.into())
}
/// Translates the `value` instruction.
@@ -26,7 +31,7 @@ where
let output_pointer = context.build_alloca(context.value_type(), "value_transferred");
context.build_store(output_pointer, context.word_const(0))?;
context.build_runtime_call(
runtime_api::imports::VALUE_TRANSFERRED,
revive_runtime_api::polkavm_imports::VALUE_TRANSFERRED,
&[output_pointer.to_int(context).into()],
);
context.build_load(output_pointer, "value_transferred")
@@ -40,22 +45,19 @@ pub fn balance<'ctx, D>(
where
D: Dependency + Clone,
{
let balance_pointer = context.build_alloca(context.word_type(), "balance_pointer");
let address_pointer = context.build_alloca(context.word_type(), "address_pointer");
context.build_store(address_pointer, address)?;
let address_pointer = context.build_address_argument_store(address)?;
let balance_pointer = context.build_alloca(context.word_type(), "balance_pointer");
let balance = context.builder().build_ptr_to_int(
balance_pointer.value,
context.xlen_type(),
"balance",
)?;
let _address = context.builder().build_ptr_to_int(
address_pointer.value,
context.xlen_type(),
"address",
)?;
context.build_runtime_call(runtime_api::imports::BALANCE, &[balance.into()]);
context.build_runtime_call(
revive_runtime_api::polkavm_imports::BALANCE_OF,
&[address_pointer.to_int(context).into(), balance.into()],
);
context.build_load(balance_pointer, "balance")
}
@@ -74,7 +76,10 @@ where
"balance",
)?;
context.build_runtime_call(runtime_api::imports::BALANCE, &[balance.into()]);
context.build_runtime_call(
revive_runtime_api::polkavm_imports::BALANCE,
&[balance.into()],
);
context.build_load(balance_pointer, "balance")
}
+4 -2
View File
@@ -4,7 +4,6 @@ use inkwell::values::BasicValue;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api;
/// Translates a log or event call.
///
@@ -77,7 +76,10 @@ where
]
};
let _ = context.build_runtime_call(runtime_api::imports::DEPOSIT_EVENT, &arguments);
let _ = context.build_runtime_call(
revive_runtime_api::polkavm_imports::DEPOSIT_EVENT,
&arguments,
);
Ok(())
}
+33 -24
View File
@@ -1,10 +1,7 @@
//! Translates the external code operations.
use inkwell::values::BasicValue;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api;
/// Translates the `extcodesize` instruction if `address` is `Some`.
/// Otherwise, translates the `codesize` instruction.
@@ -15,41 +12,53 @@ pub fn size<'ctx, D>(
where
D: Dependency + Clone,
{
let address_pointer = match address {
Some(address) => {
let address_pointer = context.build_alloca(context.word_type(), "value");
context.build_store(address_pointer, address)?;
address_pointer
}
None => context.sentinel_pointer(),
let address = match address {
Some(address) => address,
None => super::context::address(context)?.into_int_value(),
};
let address_pointer = context.build_address_argument_store(address)?;
let address_pointer_casted = context.builder().build_ptr_to_int(
address_pointer.value,
context.xlen_type(),
"address_pointer",
)?;
let value = context
let code_size_value = context
.build_runtime_call(
runtime_api::imports::CODE_SIZE,
&[address_pointer_casted.into()],
revive_runtime_api::polkavm_imports::CODE_SIZE,
&[address_pointer.to_int(context).into()],
)
.unwrap_or_else(|| panic!("{} should return a value", runtime_api::imports::CODE_SIZE))
.expect("the code_size syscall method should return a value")
.into_int_value();
Ok(context
.builder()
.build_int_z_extend(value, context.word_type(), "extcodesize")?
.as_basic_value_enum())
.build_int_z_extend(code_size_value, context.word_type(), "code_size")?
.into())
}
/// Translates the `extcodehash` instruction.
pub fn hash<'ctx, D>(
_context: &mut Context<'ctx, D>,
_address: inkwell::values::IntValue<'ctx>,
context: &mut Context<'ctx, D>,
address: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
let address_type = context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS);
let address_pointer = context.build_alloca_at_entry(address_type, "address_pointer");
let address_truncated =
context
.builder()
.build_int_truncate(address, address_type, "address_truncated")?;
let address_swapped = context.build_byte_swap(address_truncated.into())?;
context.build_store(address_pointer, address_swapped)?;
let extcodehash_pointer =
context.build_alloca_at_entry(context.word_type(), "extcodehash_pointer");
context.build_runtime_call(
revive_runtime_api::polkavm_imports::CODE_HASH,
&[
address_pointer.to_int(context).into(),
extcodehash_pointer.to_int(context).into(),
],
);
context.build_byte_swap(context.build_load(extcodehash_pointer, "extcodehash_value")?)
}
@@ -1,14 +1,19 @@
//! Translates the contract immutable operations.
use inkwell::types::BasicType;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::code_type::CodeType;
use crate::polkavm::context::function::runtime;
use crate::polkavm::context::pointer::Pointer;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
/// Translates the contract immutable load.
/// In the deploy code the values are read from the auxiliary heap.
/// In the runtime code they are requested from the system contract.
///
/// In deploy code the values are read from the stack.
///
/// In runtime code they are loaded lazily with the `get_immutable_data` syscall.
pub fn load<'ctx, D>(
context: &mut Context<'ctx, D>,
index: inkwell::values::IntValue<'ctx>,
@@ -20,38 +25,27 @@ where
None => {
anyhow::bail!("Immutables are not available if the contract part is undefined");
}
Some(CodeType::Deploy) => {
let index_double = context.builder().build_int_mul(
index,
context.word_const(2),
"immutable_load_index_double",
)?;
let offset_absolute = context.builder().build_int_add(
index_double,
context.word_const(
crate::polkavm::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA
+ (3 * revive_common::BYTE_LENGTH_WORD) as u64,
),
"immutable_offset_absolute",
)?;
let immutable_pointer = Pointer::new_with_offset(
context,
AddressSpace::default(),
context.word_type(),
offset_absolute,
"immutable_pointer",
);
context.build_load(immutable_pointer, "immutable_value")
}
Some(CodeType::Deploy) => load_from_memory(context, index),
Some(CodeType::Runtime) => {
todo!()
context.build_call(
context
.get_function(runtime::FUNCTION_LOAD_IMMUTABLE_DATA)
.expect("is always declared for runtime code")
.borrow()
.declaration(),
&[],
runtime::FUNCTION_LOAD_IMMUTABLE_DATA,
);
load_from_memory(context, index)
}
}
}
/// Translates the contract immutable store.
/// In the deploy code the values are written to the auxiliary heap at the predefined offset,
/// being prepared for returning to the system contract for saving.
///
/// In deploy code the values are written to the stack at the predefined offset,
/// being prepared for storing them using the `set_immutable_data` syscall.
///
/// Ignored in the runtime code.
pub fn store<'ctx, D>(
context: &mut Context<'ctx, D>,
@@ -66,46 +60,48 @@ where
anyhow::bail!("Immutables are not available if the contract part is undefined");
}
Some(CodeType::Deploy) => {
let index_double = context.builder().build_int_mul(
index,
context.word_const(2),
"immutable_load_index_double",
)?;
let index_offset_absolute = context.builder().build_int_add(
index_double,
context.word_const(
crate::polkavm::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA
+ (2 * revive_common::BYTE_LENGTH_WORD) as u64,
let immutable_data_pointer = context
.get_global(revive_runtime_api::immutable_data::GLOBAL_IMMUTABLE_DATA_POINTER)?
.value
.as_pointer_value();
let immutable_pointer = context.build_gep(
Pointer::new(
context.word_type(),
AddressSpace::Stack,
immutable_data_pointer,
),
"index_offset_absolute",
)?;
let index_offset_pointer = Pointer::new_with_offset(
context,
AddressSpace::default(),
context.word_type(),
index_offset_absolute,
"immutable_index_pointer",
&[index],
context.word_type().as_basic_type_enum(),
"immutable_variable_pointer",
);
context.build_store(index_offset_pointer, index)?;
let value_offset_absolute = context.builder().build_int_add(
index_offset_absolute,
context.word_const(revive_common::BYTE_LENGTH_WORD as u64),
"value_offset_absolute",
)?;
let value_offset_pointer = Pointer::new_with_offset(
context,
AddressSpace::default(),
context.word_type(),
value_offset_absolute,
"immutable_value_pointer",
);
context.build_store(value_offset_pointer, value)?;
Ok(())
context.build_store(immutable_pointer, value)
}
Some(CodeType::Runtime) => {
anyhow::bail!("Immutable writes are not available in the runtime code");
}
}
}
pub fn load_from_memory<'ctx, D>(
context: &mut Context<'ctx, D>,
index: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
let immutable_data_pointer = context
.get_global(revive_runtime_api::immutable_data::GLOBAL_IMMUTABLE_DATA_POINTER)?
.value
.as_pointer_value();
let immutable_pointer = context.build_gep(
Pointer::new(
context.word_type(),
AddressSpace::Stack,
immutable_data_pointer,
),
&[index],
context.word_type().as_basic_type_enum(),
"immutable_variable_pointer",
);
context.build_load(immutable_pointer, "immutable_value")
}
@@ -1,10 +1,29 @@
//! Translates the heap memory operations.
use inkwell::values::BasicValue;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::pointer::Pointer;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
/// Translates the `msize` instruction.
pub fn msize<'ctx, D>(
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
Ok(context
.builder()
.build_int_z_extend(
context.build_msize()?,
context.word_type(),
"heap_size_extended",
)?
.as_basic_value_enum())
}
/// Translates the `mload` instruction.
/// Uses the main heap.
pub fn load<'ctx, D>(
+57 -2
View File
@@ -1,5 +1,8 @@
//! Translates the transaction return operations.
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::code_type::CodeType;
use crate::polkavm::context::pointer::Pointer;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
@@ -12,8 +15,60 @@ pub fn r#return<'ctx, D>(
where
D: Dependency + Clone,
{
if context.code_type().is_none() {
anyhow::bail!("Return is not available if the contract part is undefined");
match context.code_type() {
None => anyhow::bail!("Return is not available if the contract part is undefined"),
Some(CodeType::Deploy) => {
let immutable_data_size_pointer = context
.get_global(revive_runtime_api::immutable_data::GLOBAL_IMMUTABLE_DATA_SIZE)?
.value
.as_pointer_value();
let immutable_data_size = context.build_load(
Pointer::new(
context.xlen_type(),
AddressSpace::Stack,
immutable_data_size_pointer,
),
"immutable_data_size_load",
)?;
let write_immutable_data_block = context.append_basic_block("write_immutables_block");
let join_return_block = context.append_basic_block("join_return_block");
let immutable_data_size_is_zero = context.builder().build_int_compare(
inkwell::IntPredicate::EQ,
context.xlen_type().const_zero(),
immutable_data_size.into_int_value(),
"immutable_data_size_is_zero",
)?;
context.build_conditional_branch(
immutable_data_size_is_zero,
join_return_block,
write_immutable_data_block,
)?;
context.set_basic_block(write_immutable_data_block);
let immutable_data_pointer = context
.get_global(revive_runtime_api::immutable_data::GLOBAL_IMMUTABLE_DATA_POINTER)?
.value
.as_pointer_value();
context.build_runtime_call(
revive_runtime_api::polkavm_imports::SET_IMMUTABLE_DATA,
&[
context
.builder()
.build_ptr_to_int(
immutable_data_pointer,
context.xlen_type(),
"immutable_data_pointer_to_xlen",
)?
.into(),
immutable_data_size,
],
);
context.build_unconditional_branch(join_return_block);
context.set_basic_block(join_return_block);
}
Some(CodeType::Runtime) => {}
}
context.build_exit(
@@ -2,7 +2,6 @@
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api;
/// Translates the return data size.
pub fn size<'ctx, D>(
@@ -11,17 +10,19 @@ pub fn size<'ctx, D>(
where
D: Dependency + Clone,
{
let output_pointer = context.build_alloca_at_entry(context.word_type(), "return_data_size");
let output_pointer_parameter = context.builder().build_ptr_to_int(
output_pointer.value,
context.xlen_type(),
"return_data_copy_output_pointer",
)?;
context.build_runtime_call(
runtime_api::imports::RETURNDATASIZE,
&[output_pointer_parameter.into()],
);
context.build_load(output_pointer, "return_data_size_load")
let return_data_size_value = context
.build_runtime_call(revive_runtime_api::polkavm_imports::RETURNDATASIZE, &[])
.expect("the return_data_size syscall method should return a value")
.into_int_value();
Ok(context
.builder()
.build_int_z_extend(
return_data_size_value,
context.word_type(),
"return_data_size",
)?
.into())
}
/// Translates the return data copy, trapping if
@@ -59,7 +60,7 @@ where
)?;
context.build_runtime_call(
runtime_api::imports::RETURNDATACOPY,
revive_runtime_api::polkavm_imports::RETURNDATACOPY,
&[
output_pointer.into(),
output_length_pointer_int.into(),
+16 -33
View File
@@ -1,7 +1,6 @@
//! Translates the storage operations.
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::pointer::Pointer;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
@@ -13,14 +12,10 @@ pub fn load<'ctx, D>(
where
D: Dependency + Clone,
{
let position_pointer = Pointer::new_with_offset(
context,
AddressSpace::Storage,
context.word_type(),
position,
"storage_load_position_pointer",
);
context.build_load(position_pointer, "storage_load_value")
let mut slot_ptr = context.build_alloca_at_entry(context.word_type(), "slot_pointer");
slot_ptr.address_space = AddressSpace::Storage;
context.builder().build_store(slot_ptr.value, position)?;
context.build_load(slot_ptr, "storage_load_value")
}
/// Translates the storage store.
@@ -32,14 +27,10 @@ pub fn store<'ctx, D>(
where
D: Dependency + Clone,
{
let position_pointer = Pointer::new_with_offset(
context,
AddressSpace::Storage,
context.word_type(),
position,
"storage_store_position_pointer",
);
context.build_store(position_pointer, value)?;
let mut slot_ptr = context.build_alloca_at_entry(context.word_type(), "slot_pointer");
slot_ptr.address_space = AddressSpace::Storage;
context.builder().build_store(slot_ptr.value, position)?;
context.build_store(slot_ptr, value)?;
Ok(())
}
@@ -51,14 +42,10 @@ pub fn transient_load<'ctx, D>(
where
D: Dependency + Clone,
{
let position_pointer = Pointer::new_with_offset(
context,
AddressSpace::TransientStorage,
context.word_type(),
position,
"transient_storage_load_position_pointer",
);
context.build_load(position_pointer, "transient_storage_load_value")
let mut slot_ptr = context.build_alloca_at_entry(context.word_type(), "slot_pointer");
slot_ptr.address_space = AddressSpace::TransientStorage;
context.builder().build_store(slot_ptr.value, position)?;
context.build_load(slot_ptr, "transient_storage_load_value")
}
/// Translates the transient storage store.
@@ -70,13 +57,9 @@ pub fn transient_store<'ctx, D>(
where
D: Dependency + Clone,
{
let position_pointer = Pointer::new_with_offset(
context,
AddressSpace::TransientStorage,
context.word_type(),
position,
"transient_storage_store_position_pointer",
);
context.build_store(position_pointer, value)?;
let mut slot_ptr = context.build_alloca_at_entry(context.word_type(), "slot_pointer");
slot_ptr.address_space = AddressSpace::TransientStorage;
context.builder().build_store(slot_ptr.value, position)?;
context.build_store(slot_ptr, value)?;
Ok(())
}
+6 -11
View File
@@ -4,10 +4,8 @@ pub mod r#const;
pub mod context;
pub mod evm;
pub mod metadata_hash;
pub mod utils;
pub use self::r#const::*;
use self::utils::keccak256;
use crate::debug_config::DebugConfig;
use crate::optimizer::settings::Settings as OptimizerSettings;
@@ -15,6 +13,7 @@ use crate::optimizer::settings::Settings as OptimizerSettings;
use anyhow::Context as AnyhowContext;
use polkavm_common::program::ProgramBlob;
use polkavm_disassembler::{Disassembler, DisassemblyFormat};
use sha3::Digest;
use self::context::build::Build;
use self::context::Context;
@@ -29,7 +28,7 @@ pub fn build_assembly_text(
contract_path: &str,
bytecode: &[u8],
metadata_hash: Option<[u8; revive_common::BYTE_LENGTH_WORD]>,
debug_config: Option<&DebugConfig>,
debug_config: &DebugConfig,
) -> anyhow::Result<Build> {
let program_blob = ProgramBlob::parse(bytecode.into())
.map_err(anyhow::Error::msg)
@@ -49,15 +48,13 @@ pub fn build_assembly_text(
format!("Failed to convert disassembled code to string for contract: {contract_path}")
})?;
if let Some(debug_config) = debug_config {
debug_config.dump_assembly(contract_path, &assembly_text)?;
}
debug_config.dump_assembly(contract_path, &assembly_text)?;
Ok(Build::new(
assembly_text.to_owned(),
metadata_hash,
bytecode.to_owned(),
keccak256(bytecode),
hex::encode(sha3::Keccak256::digest(bytecode)),
))
}
@@ -97,9 +94,8 @@ pub trait Dependency {
dependency: Self,
path: &str,
optimizer_settings: OptimizerSettings,
is_system_mode: bool,
include_metadata_hash: bool,
debug_config: Option<DebugConfig>,
debug_config: DebugConfig,
) -> anyhow::Result<String>;
/// Resolves a full contract path.
@@ -118,9 +114,8 @@ impl Dependency for DummyDependency {
_dependency: Self,
_path: &str,
_optimizer_settings: OptimizerSettings,
_is_system_mode: bool,
_include_metadata_hash: bool,
_debug_config: Option<DebugConfig>,
_debug_config: DebugConfig,
) -> anyhow::Result<String> {
Ok(String::new())
}
-60
View File
@@ -1,60 +0,0 @@
//! Some LLVM IR generator utilies.
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
/// Clamps `value` to `max_value`, if `value` is bigger than `max_value`.
pub fn clamp<'ctx, D>(
context: &mut Context<'ctx, D>,
value: inkwell::values::IntValue<'ctx>,
max_value: inkwell::values::IntValue<'ctx>,
name: &str,
) -> anyhow::Result<inkwell::values::IntValue<'ctx>>
where
D: Dependency + Clone,
{
let in_bounds_block = context.append_basic_block(format!("{name}_is_bounds_block").as_str());
let join_block = context.append_basic_block(format!("{name}_join_block").as_str());
let pointer = context.build_alloca(context.word_type(), format!("{name}_pointer").as_str());
context.build_store(pointer, max_value)?;
let is_in_bounds = context.builder().build_int_compare(
inkwell::IntPredicate::ULE,
value,
max_value,
format!("{name}_is_in_bounds").as_str(),
)?;
context.build_conditional_branch(is_in_bounds, in_bounds_block, join_block)?;
context.set_basic_block(in_bounds_block);
context.build_store(pointer, value)?;
context.build_unconditional_branch(join_block);
context.set_basic_block(join_block);
let result = context.build_load(pointer, name)?;
Ok(result.into_int_value())
}
/// Computes the `keccak256` hash for `preimage`.
pub fn keccak256(preimage: &[u8]) -> String {
use sha3::Digest;
let hash_bytes = sha3::Keccak256::digest(preimage);
hash_bytes
.into_iter()
.map(|byte| format!("{byte:02x}"))
.collect::<Vec<String>>()
.join("")
}
#[cfg(test)]
mod tests {
#[test]
fn keccak256() {
assert_eq!(
super::keccak256("zksync".as_bytes()),
"0238fb1ab06c28c32885f9a4842207ac480c2467df26b6c58e201679628c5a5b"
);
}
}
+2 -20
View File
@@ -20,28 +20,16 @@ pub struct TargetMachine {
impl TargetMachine {
/// The LLVM target name.
#[cfg(not(feature = "riscv-64"))]
pub const VM_TARGET_NAME: &'static str = "riscv32";
#[cfg(feature = "riscv-64")]
pub const VM_TARGET_NAME: &'static str = "riscv64";
/// The LLVM target triple.
#[cfg(not(feature = "riscv-64"))]
pub const VM_TARGET_TRIPLE: &'static str = "riscv32-unknown-unknown-elf";
#[cfg(feature = "riscv-64")]
pub const VM_TARGET_TRIPLE: &'static str = "riscv64-unknown-unknown-elf";
/// The LLVM target cpu
#[cfg(not(feature = "riscv-64"))]
pub const VM_TARGET_CPU: &'static str = "generic-rv32";
#[cfg(feature = "riscv-64")]
pub const VM_TARGET_CPU: &'static str = "generic-rv64";
/// LLVM target features.
#[cfg(feature = "riscv-zbb")]
pub const VM_FEATURES: &'static str = "+zbb,+e,+m,+c";
#[cfg(not(feature = "riscv-zbb"))]
pub const VM_FEATURES: &'static str = "+e,+m,+c";
pub const VM_FEATURES: &'static str =
"+e,+m,+a,+c,+zbb,+auipc-addi-fusion,+ld-add-fusion,+lui-addi-fusion,+xtheadcondmov";
/// A shortcut constructor.
/// A separate instance for every optimization level is created.
@@ -70,12 +58,6 @@ impl TargetMachine {
})
}
/// Sets the target-specific data in the module.
pub fn set_target_data(&self, module: &inkwell::module::Module) {
module.set_triple(&self.target_machine.get_triple());
module.set_data_layout(&self.target_machine.get_target_data().get_data_layout());
}
/// Writes the LLVM module to a memory buffer.
pub fn write_to_memory_buffer(
&self,
@@ -13,9 +13,6 @@ impl Target {
/// Returns the target name.
pub fn name(&self) -> &str {
match self {
#[cfg(not(feature = "riscv-64"))]
Self::PVM => "riscv32",
#[cfg(feature = "riscv-64")]
Self::PVM => "riscv64",
}
}
@@ -23,9 +20,6 @@ impl Target {
/// Returns the target triple.
pub fn triple(&self) -> &str {
match self {
#[cfg(not(feature = "riscv-64"))]
Self::PVM => "riscv32-unknown-unknown-elf",
#[cfg(feature = "riscv-64")]
Self::PVM => "riscv64-unknown-unknown-elf",
}
}
@@ -43,9 +37,6 @@ impl FromStr for Target {
fn from_str(string: &str) -> Result<Self, Self::Err> {
match string {
#[cfg(not(feature = "riscv-64"))]
"riscv32" => Ok(Self::PVM),
#[cfg(feature = "riscv-64")]
"riscv64" => Ok(Self::PVM),
_ => Err(anyhow::anyhow!(
"Unknown target `{}`. Supported targets: {:?}",
@@ -59,9 +50,6 @@ impl FromStr for Target {
impl std::fmt::Display for Target {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
#[cfg(not(feature = "riscv-64"))]
Target::PVM => write!(f, "riscv32"),
#[cfg(feature = "riscv-64")]
Target::PVM => write!(f, "riscv64"),
}
}
Binary file not shown.
+23 -12
View File
@@ -26,13 +26,13 @@
use std::time::Duration;
use hex::{FromHex, ToHex};
use pallet_revive::AddressMapper;
use pallet_revive::{AddressMapper, ExecReturnValue, InstantiateReturnValue};
use polkadot_sdk::*;
use polkadot_sdk::{
pallet_revive::{CollectEvents, ContractExecResult, ContractInstantiateResult, DebugInfo},
pallet_revive::{CollectEvents, ContractResult, DebugInfo},
polkadot_runtime_common::BuildStorage,
polkadot_sdk_frame::testing_prelude::*,
sp_core::H160,
sp_core::{H160, H256},
sp_keystore::{testing::MemoryKeystore, KeystoreExt},
sp_runtime::AccountId32,
};
@@ -55,7 +55,7 @@ pub const BOB: H160 = H160([2u8; 20]);
/// The charlie test account
pub const CHARLIE: H160 = H160([3u8; 20]);
/// Default gas limit
pub const GAS_LIMIT: Weight = Weight::from_parts(100_000_000_000, 3 * 1024 * 1024);
pub const GAS_LIMIT: Weight = Weight::from_parts(100_000_000_000_000, 3 * 1024 * 1024 * 1024);
/// Default deposit limit
pub const DEPOSIT_LIMIT: Balance = 10_000_000;
@@ -72,7 +72,7 @@ impl ExtBuilder {
Self {
balance_genesis_config: value
.iter()
.map(|(address, balance)| (AccountId::to_account_id(address), *balance))
.map(|(address, balance)| (AccountId::to_fallback_account_id(address), *balance))
.collect(),
}
}
@@ -161,7 +161,7 @@ impl VerifyCallExpectation {
fn verify(self, result: &CallResult) {
assert_eq!(
self.success,
result.is_ok(),
!result.did_revert(),
"contract execution result mismatch: {result:?}"
);
@@ -179,23 +179,33 @@ impl VerifyCallExpectation {
#[derive(Clone, Debug)]
pub enum CallResult {
Exec {
result: ContractExecResult<Balance, EventRecord>,
result: ContractResult<ExecReturnValue, Balance, EventRecord>,
wall_time: Duration,
},
Instantiate {
result: ContractInstantiateResult<Balance, EventRecord>,
result: ContractResult<InstantiateReturnValue, Balance, EventRecord>,
wall_time: Duration,
code_hash: H256,
},
}
impl CallResult {
/// Check if the call was successful
fn is_ok(&self) -> bool {
fn did_revert(&self) -> bool {
match self {
Self::Exec { result, .. } => result.result.is_ok(),
Self::Instantiate { result, .. } => result.result.is_ok(),
Self::Exec { result, .. } => result
.result
.as_ref()
.map(|r| r.did_revert())
.unwrap_or(true),
Self::Instantiate { result, .. } => result
.result
.as_ref()
.map(|r| r.result.did_revert())
.unwrap_or(true),
}
}
/// Get the output of the call
fn output(&self) -> Vec<u8> {
match self {
@@ -211,6 +221,7 @@ impl CallResult {
.unwrap_or_default(),
}
}
/// Get the gas consumed by the call
fn gas_consumed(&self) -> Weight {
match self {
@@ -235,7 +246,7 @@ pub enum Code {
/// A contract blob
Bytes(Vec<u8>),
/// Pre-existing contract hash
Hash(Hash),
Hash(crate::runtime::Hash),
}
impl Default for Code {
+9 -3
View File
@@ -1,5 +1,6 @@
use frame_support::runtime;
use frame_support::{runtime, weights::constants::WEIGHT_REF_TIME_PER_SECOND};
use pallet_revive::AccountId32Mapper;
use polkadot_sdk::*;
use polkadot_sdk::{
polkadot_sdk_frame::{log, runtime::prelude::*},
@@ -7,7 +8,7 @@ use polkadot_sdk::{
};
pub type Balance = u128;
pub type AccountId = pallet_revive::DefaultAddressMapper;
pub type AccountId = pallet_revive::AccountId32Mapper<Runtime>;
pub type Block = frame_system::mocking::MockBlock<Runtime>;
pub type Hash = <Runtime as frame_system::Config>::Hash;
pub type EventRecord =
@@ -45,6 +46,7 @@ mod runtime {
#[derive_impl(frame_system::config_preludes::SolochainDefaultConfig)]
impl frame_system::Config for Runtime {
type Block = Block;
type BlockWeights = BlockWeights;
type AccountId = AccountId32;
type AccountData = pallet_balances::AccountData<<Runtime as pallet_balances::Config>::Balance>;
}
@@ -64,6 +66,10 @@ parameter_types! {
pub const DepositPerByte: Balance = 1;
pub const DepositPerItem: Balance = 2;
pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0);
pub BlockWeights: frame_system::limits::BlockWeights =
frame_system::limits::BlockWeights::simple_max(
Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX),
);
}
#[derive_impl(pallet_revive::config_preludes::TestDefaultConfig)]
@@ -74,7 +80,7 @@ impl pallet_revive::Config for Runtime {
type ChainExtension = ();
type DepositPerByte = DepositPerByte;
type DepositPerItem = DepositPerItem;
type AddressMapper = AccountId;
type AddressMapper = AccountId32Mapper<Self>;
type RuntimeMemory = ConstU32<{ 512 * 1024 * 1024 }>;
type PVFMemory = ConstU32<{ 1024 * 1024 * 1024 }>;
type UnsafeUnstableInterface = UnstableInterface;
+18 -15
View File
@@ -1,10 +1,9 @@
use std::time::Instant;
use pallet_revive::AddressMapper;
use serde::{Deserialize, Serialize};
use crate::*;
use alloy_primitives::Address;
use alloy_primitives::{keccak256, Address};
#[cfg(feature = "revive-solidity")]
use revive_differential::{Evm, EvmLog};
#[cfg(feature = "revive-solidity")]
@@ -289,16 +288,10 @@ impl Specs {
else {
panic!("the differential runner requires Code::Solidity source");
};
assert_ne!(
solc_optimizer,
Some(false),
"solc_optimizer must be enabled in differntial mode"
);
assert_ne!(
pipeline,
Some(revive_solidity::SolcPipeline::EVMLA),
"yul pipeline must be enabled in differntial mode"
"yul pipeline must be enabled in differential mode"
);
assert!(
salt.0.is_none(),
@@ -311,9 +304,11 @@ impl Specs {
);
let deploy_code = match std::fs::read_to_string(&path) {
Ok(solidity_source) => {
hex::encode(compile_evm_deploy_code(&contract, &solidity_source))
}
Ok(solidity_source) => hex::encode(compile_evm_deploy_code(
&contract,
&solidity_source,
solc_optimizer.unwrap_or(true),
)),
Err(err) => panic!(
"failed to read solidity source\n . path: '{}'\n . error: {:?}",
path.display(),
@@ -413,14 +408,21 @@ impl Specs {
data,
salt,
} => {
let code: pallet_revive::Code = code.into();
let code_hash = match code.clone() {
pallet_revive::Code::Existing(code_hash) => code_hash,
pallet_revive::Code::Upload(bytes) => {
H256::from_slice(keccak256(&bytes).as_slice())
}
};
let origin = RuntimeOrigin::signed(origin.to_account_id(&results));
let time_start = Instant::now();
let result = Contracts::bare_instantiate(
origin,
value,
gas_limit.unwrap_or(GAS_LIMIT),
storage_deposit_limit.unwrap_or(DEPOSIT_LIMIT),
code.into(),
storage_deposit_limit.unwrap_or(DEPOSIT_LIMIT).into(),
code,
data,
salt.0,
DebugInfo::Skip,
@@ -429,6 +431,7 @@ impl Specs {
results.push(CallResult::Instantiate {
result,
wall_time: time_start.elapsed(),
code_hash,
})
}
Upload {
@@ -458,7 +461,7 @@ impl Specs {
dest.to_eth_addr(&results),
value,
gas_limit.unwrap_or(GAS_LIMIT),
storage_deposit_limit.unwrap_or(DEPOSIT_LIMIT),
storage_deposit_limit.unwrap_or(DEPOSIT_LIMIT).into(),
data,
DebugInfo::Skip,
CollectEvents::Skip,
+3 -4
View File
@@ -7,9 +7,8 @@ repository.workspace = true
authors.workspace = true
description = "Implements the low level runtime API bindings with pallet contracts"
[features]
riscv-64 = []
[dependencies]
anyhow = { workspace = true }
inkwell = { workspace = true, features = ["target-riscv", "no-libffi-linking", "llvm18-0"] }
inkwell = { workspace = true, features = ["target-riscv", "no-libffi-linking", "llvm18-0"] }
revive-common = { workspace = true }
+5 -16
View File
@@ -1,23 +1,8 @@
use std::{env, fs, path::Path, process::Command};
#[cfg(not(feature = "riscv-64"))]
const TARGET_TRIPLE_FLAG: &str = "-triple=riscv32-unknown-unknown-elf";
#[cfg(feature = "riscv-64")]
const TARGET_TRIPLE_FLAG: &str = "-triple=riscv64-unknown-unknown-elf";
#[cfg(not(feature = "riscv-64"))]
const TARGET_FLAG: &str = "--target=riscv32";
#[cfg(feature = "riscv-64")]
const TARGET_FLAG: &str = "--target=riscv64";
#[cfg(not(feature = "riscv-64"))]
const TARGET_ARCH_FLAG: &str = "-march=rv32em";
#[cfg(feature = "riscv-64")]
const TARGET_ARCH_FLAG: &str = "-march=rv64em";
#[cfg(not(feature = "riscv-64"))]
const TARGET_ABI_FLAG: &str = "-mabi=ilp32e";
#[cfg(feature = "riscv-64")]
const TARGET_ARCH_FLAG: &str = "-march=rv64emac";
const TARGET_ABI_FLAG: &str = "-mabi=lp64e";
const IMPORTS_SOUCE: &str = "src/polkavm_imports.c";
@@ -36,6 +21,10 @@ fn compile(source_path: &str, bitcode_path: &str) {
TARGET_TRIPLE_FLAG,
TARGET_ARCH_FLAG,
TARGET_ABI_FLAG,
"-Xclang",
"-target-feature",
"-Xclang",
"+fast-unaligned-access,+xtheadcondmov",
"-fno-exceptions",
"-ffreestanding",
"-Wall",
+43 -16
View File
@@ -52,29 +52,29 @@ pub fn instantiate(context: &Context) -> StructType {
context.struct_type(
&[
// code_hash_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(),
context.i32_type().as_basic_type_enum(),
// ref_time_limit: u64,
context.i64_type().as_basic_type_enum(),
// proof_size_limit: u64,
context.i64_type().as_basic_type_enum(),
// deposit_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(),
context.i32_type().as_basic_type_enum(),
// value_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(),
context.i32_type().as_basic_type_enum(),
// input_data_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(),
context.i32_type().as_basic_type_enum(),
// input_data_len: u32,
context.i32_type().as_basic_type_enum(),
// address_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(),
context.i32_type().as_basic_type_enum(),
// output_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(),
context.i32_type().as_basic_type_enum(),
// output_len_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(),
context.i32_type().as_basic_type_enum(),
// salt_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(),
context.i32_type().as_basic_type_enum(),
],
true,
false,
)
}
@@ -85,24 +85,51 @@ pub fn call(context: &Context) -> StructType {
// flags: u32,
context.i32_type().as_basic_type_enum(),
// address_ptr:
context.ptr_type(Default::default()).as_basic_type_enum(),
context.i32_type().as_basic_type_enum(),
// ref_time_limit: u64,
context.i64_type().as_basic_type_enum(),
// proof_size_limit: u64,
context.i64_type().as_basic_type_enum(),
// deposit_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(),
context.i32_type().as_basic_type_enum(),
// value_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(),
context.i32_type().as_basic_type_enum(),
// input_data_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(),
context.i32_type().as_basic_type_enum(),
// input_data_len: u32,
context.i32_type().as_basic_type_enum(),
// output_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(),
context.i32_type().as_basic_type_enum(),
// output_len_ptr: u32,
context.ptr_type(Default::default()).as_basic_type_enum(),
context.i32_type().as_basic_type_enum(),
],
true,
false,
)
}
/// Returns a packed struct argument type for the `delegate_call` API.
pub fn delegate_call(context: &Context) -> StructType {
context.struct_type(
&[
// flags: u32,
context.i32_type().as_basic_type_enum(),
// address_ptr:
context.i32_type().as_basic_type_enum(),
// ref_time_limit: u64,
context.i64_type().as_basic_type_enum(),
// proof_size_limit: u64,
context.i64_type().as_basic_type_enum(),
// deposit_ptr: u32,
context.i32_type().as_basic_type_enum(),
// input_data_ptr: u32,
context.i32_type().as_basic_type_enum(),
// input_data_len: u32,
context.i32_type().as_basic_type_enum(),
// output_ptr: u32,
context.i32_type().as_basic_type_enum(),
// output_len_ptr: u32,
context.i32_type().as_basic_type_enum(),
],
false,
)
}
+89
View File
@@ -0,0 +1,89 @@
//! Allocates memory for the immutable data in a separate module.
//!
//! Because we only know how many immutable variables were set after
//! translating the whole contract code, we want to set the size at
//! last. However, array types need a size upon declaration.
//!
//! A simple work around is to replace it during link time.
//! To quote the [LLVM docs][0]:
//!
//! > For global variable declarations [..] the allocation size and
//! > alignment of the definition it resolves to must be greater than
//! > or equal to that of the declaration [..]
//!
//! To adhere to this we initially declare a length of 0 in
//! `revive-llvm-context`.
//!
//! [0]: https://llvm.org/docs/LangRef.html#global-variables
/// The immutable data module name.
pub static MODULE_NAME: &str = "__evm_immutables";
/// The immutable data global pointer.
pub static GLOBAL_IMMUTABLE_DATA_POINTER: &str = "__immutable_data_ptr";
/// The immutable data global size.
pub static GLOBAL_IMMUTABLE_DATA_SIZE: &str = "__immutable_data_size";
/// The immutable data maximum size in bytes.
pub static IMMUTABLE_DATA_MAX_SIZE: u32 = 4 * 1024;
/// Returns the immutable data global type.
pub fn data_type(context: &inkwell::context::Context, size: u32) -> inkwell::types::ArrayType {
context
.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.array_type(size)
}
/// Returns the immutable data size global type.
pub fn size_type(context: &inkwell::context::Context) -> inkwell::types::IntType {
context.custom_width_int_type(revive_common::BIT_LENGTH_X32 as u32)
}
/// Creates a LLVM module with the immutable data and its `size` in bytes.
pub fn module(context: &inkwell::context::Context, size: u32) -> inkwell::module::Module {
let module = context.create_module(MODULE_NAME);
let length = size / revive_common::BYTE_LENGTH_WORD as u32;
let immutable_data = module.add_global(
data_type(context, length),
Default::default(),
GLOBAL_IMMUTABLE_DATA_POINTER,
);
immutable_data.set_linkage(inkwell::module::Linkage::External);
immutable_data.set_visibility(inkwell::GlobalVisibility::Default);
immutable_data.set_initializer(&data_type(context, length).get_undef());
let immutable_data_size = module.add_global(
size_type(context),
Default::default(),
GLOBAL_IMMUTABLE_DATA_SIZE,
);
immutable_data_size.set_linkage(inkwell::module::Linkage::External);
immutable_data_size.set_visibility(inkwell::GlobalVisibility::Default);
immutable_data_size.set_initializer(&size_type(context).const_int(size as u64, false));
module
}
#[cfg(test)]
mod tests {
use crate::immutable_data::*;
#[test]
fn it_works() {
inkwell::targets::Target::initialize_riscv(&Default::default());
let context = inkwell::context::Context::create();
let size = 512;
let module = crate::immutable_data::module(&context, size);
let immutable_data_pointer = module.get_global(GLOBAL_IMMUTABLE_DATA_POINTER).unwrap();
assert_eq!(
immutable_data_pointer.get_initializer().unwrap(),
data_type(&context, size / 32).get_undef()
);
let immutable_data_size = module.get_global(GLOBAL_IMMUTABLE_DATA_SIZE).unwrap();
assert_eq!(
immutable_data_size.get_initializer().unwrap(),
size_type(&context).const_int(size as u64, false)
);
}
}
+1
View File
@@ -7,5 +7,6 @@
//! [1]: [https://docs.rs/pallet-contracts/26.0.0/pallet_contracts/api_doc/index.html]
pub mod calling_convention;
pub mod immutable_data;
pub mod polkavm_exports;
pub mod polkavm_imports;
+12 -2
View File
@@ -2,6 +2,16 @@ use inkwell::{context::Context, memory_buffer::MemoryBuffer, module::Module, sup
include!(concat!(env!("OUT_DIR"), "/polkavm_exports.rs"));
/// The contract deploy export.
pub static CALL: &str = "call";
/// The contract call export.
pub static DEPLOY: &str = "deploy";
/// All exported symbols.
/// Useful for configuring common attributes and linkage.
pub static EXPORTS: [&str; 2] = [CALL, DEPLOY];
/// Creates a LLVM module from the [BITCODE].
/// The module exports `call` and `deploy` functions (which are named thereafter).
/// Returns `Error` if the bitcode fails to parse, which should never happen.
@@ -23,7 +33,7 @@ mod tests {
let context = inkwell::context::Context::create();
let module = polkavm_exports::module(&context, "polkavm_exports").unwrap();
assert!(module.get_function("call").is_some());
assert!(module.get_function("deploy").is_some());
assert!(module.get_function(polkavm_exports::CALL).is_some());
assert!(module.get_function(polkavm_exports::DEPLOY).is_some());
}
}
+28 -6
View File
@@ -9,7 +9,11 @@
#define POLKAVM_REGS_FOR_TY_void 0
#define POLKAVM_REGS_FOR_TY_i32 1
#define POLKAVM_REGS_FOR_TY_i64 2
#ifdef _LP64
#define POLKAVM_REGS_FOR_TY_i64 1
#else
#define POLKAVM_REGS_FOR_TY_i64 2
#endif
#define POLKAVM_REGS_FOR_TY_int8_t POLKAVM_REGS_FOR_TY_i32
#define POLKAVM_REGS_FOR_TY_uint8_t POLKAVM_REGS_FOR_TY_i32
@@ -107,6 +111,26 @@ struct PolkaVM_Metadata {
unsigned char output_regs;
} __attribute__ ((packed));
#ifdef _LP64
#define POLKAVM_EXPORT_DEF() \
".quad %[metadata]\n" \
".quad %[function]\n"
#else
#define POLKAVM_EXPORT_DEF() \
".word %[metadata]\n" \
".word %[function]\n"
#endif
#ifdef _LP64
#define POLKAVM_IMPORT_DEF() \
".word 0x0000000b\n" \
".quad %[metadata]\n"
#else
#define POLKAVM_IMPORT_DEF() \
".word 0x0000000b\n" \
".word %[metadata]\n"
#endif
#define POLKAVM_EXPORT(arg_return_ty, fn_name, ...) \
static struct PolkaVM_Metadata POLKAVM_JOIN(fn_name, __EXPORT_METADATA) __attribute__ ((section(".polkavm_metadata"))) = { \
1, 0, sizeof(#fn_name) - 1, #fn_name, POLKAVM_COUNT_REGS(__VA_ARGS__), POLKAVM_COUNT_REGS(arg_return_ty) \
@@ -115,8 +139,7 @@ static void __attribute__ ((naked, used)) POLKAVM_UNIQUE(polkavm_export_dummy)()
__asm__( \
".pushsection .polkavm_exports,\"R\",@note\n" \
".byte 1\n" \
".word %[metadata]\n" \
".word %[function]\n" \
POLKAVM_EXPORT_DEF() \
".popsection\n" \
: \
: \
@@ -130,10 +153,9 @@ static void __attribute__ ((naked, used)) POLKAVM_UNIQUE(polkavm_export_dummy)()
static struct PolkaVM_Metadata POLKAVM_JOIN(fn_name, __IMPORT_METADATA) __attribute__ ((section(".polkavm_metadata"))) = { \
1, 0, sizeof(#fn_name) - 1, #fn_name, POLKAVM_COUNT_REGS(__VA_ARGS__), POLKAVM_COUNT_REGS(arg_return_ty) \
}; \
static arg_return_ty __attribute__ ((naked, used)) fn_name(POLKAVM_IMPORT_ARGS_IMPL(__VA_ARGS__)) { \
static arg_return_ty __attribute__ ((used, naked)) fn_name(POLKAVM_IMPORT_ARGS_IMPL(__VA_ARGS__)) { \
__asm__( \
".word 0x0000000b\n" \
".word %[metadata]\n" \
POLKAVM_IMPORT_DEF() \
"ret\n" \
: \
: \
+71 -72
View File
@@ -3,9 +3,34 @@
#include "polkavm_guest.h"
// Missing builtins
#define EVM_WORD_SIZE 32
#define ALIGN(size) ((size + EVM_WORD_SIZE - 1) & ~(EVM_WORD_SIZE - 1))
#define MAX_MEMORY_SIZE (64 * 1024)
static char __memory[MAX_MEMORY_SIZE];
static uint32_t __memory_size = 0;
void * __sbrk_internal(uint32_t offset, uint32_t size) {
if (offset >= MAX_MEMORY_SIZE || size > MAX_MEMORY_SIZE) {
return NULL;
}
uint32_t new_size = ALIGN(offset + size);
if (new_size > MAX_MEMORY_SIZE) {
return NULL;
}
if (new_size > __memory_size) {
__memory_size = new_size;
}
return (void *)&__memory[__memory_size];
}
uint32_t __msize() {
return __memory_size;
}
void * memset(void *b, int c, size_t len) {
uint8_t *dest = b;
while (len-- > 0) *dest++ = c;
@@ -37,94 +62,68 @@ void * memmove(void *dst, const void *src, size_t n) {
return dst;
}
void * __sbrk(uint32_t size) {
uint32_t address;
__asm__ __volatile__(
".insn r 0xb, 1, 0, %[dst], %[sz], zero"
: [dst] "=r" (address)
: [sz] "ir" (size)
:
);
return (void *)address;
}
// Imports
POLKAVM_IMPORT(void, input, uint32_t, uint32_t)
POLKAVM_IMPORT(void, seal_return, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(void, return_data_copy, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(void, return_data_size, uint32_t)
POLKAVM_IMPORT(void, value_transferred, uint32_t)
POLKAVM_IMPORT(uint32_t, set_storage, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(uint32_t, get_storage, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(uint32_t, clear_storage, uint32_t, uint32_t)
POLKAVM_IMPORT(uint32_t, contains_storage, uint32_t, uint32_t)
POLKAVM_IMPORT(uint32_t, take_storage, uint32_t, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(uint32_t, call, uint32_t)
POLKAVM_IMPORT(uint32_t, delegate_call, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(uint32_t, instantiate, uint32_t)
POLKAVM_IMPORT(void, terminate, uint32_t)
POLKAVM_IMPORT(void, caller, uint32_t)
POLKAVM_IMPORT(uint32_t, is_contract, uint32_t)
POLKAVM_IMPORT(uint32_t, code_hash, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(uint32_t, code_size, uint32_t)
POLKAVM_IMPORT(void, own_code_hash, uint32_t, uint32_t)
POLKAVM_IMPORT(uint32_t, caller_is_origin)
POLKAVM_IMPORT(uint32_t, caller_is_root)
POLKAVM_IMPORT(void, address, uint32_t)
POLKAVM_IMPORT(void, weight_to_fee, uint64_t, uint32_t, uint32_t)
POLKAVM_IMPORT(void, gas_left, uint32_t, uint32_t)
POLKAVM_IMPORT(void, balance, uint32_t)
POLKAVM_IMPORT(void, chain_id, uint32_t)
POLKAVM_IMPORT(void, balance_of, uint32_t, uint32_t)
POLKAVM_IMPORT(void, now, uint32_t)
POLKAVM_IMPORT(void, base_fee, uint32_t)
POLKAVM_IMPORT(void, minimum_balance, uint32_t, uint32_t)
POLKAVM_IMPORT(void, deposit_event, uint32_t, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(void, block_hash, uint32_t, uint32_t)
POLKAVM_IMPORT(void, block_number, uint32_t)
POLKAVM_IMPORT(void, hash_sha2_256, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(uint64_t, call, uint32_t)
POLKAVM_IMPORT(uint64_t, call_data_copy, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(uint64_t, call_data_load, uint32_t, uint32_t)
POLKAVM_IMPORT(uint64_t, call_data_size)
POLKAVM_IMPORT(void, caller, uint32_t)
POLKAVM_IMPORT(void, chain_id, uint32_t)
POLKAVM_IMPORT(uint64_t, code_size, uint32_t)
POLKAVM_IMPORT(void, code_hash, uint32_t, uint32_t)
POLKAVM_IMPORT(uint64_t, delegate_call, uint32_t)
POLKAVM_IMPORT(void, deposit_event, uint32_t, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(uint64_t, gas_limit);
POLKAVM_IMPORT(uint64_t, gas_price);
POLKAVM_IMPORT(void, get_immutable_data, uint32_t, uint32_t);
POLKAVM_IMPORT(uint64_t, get_storage, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(void, hash_keccak_256, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(void, hash_blake2_256, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(uint64_t, instantiate, uint32_t)
POLKAVM_IMPORT(void, hash_blake2_128, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(void, now, uint32_t)
POLKAVM_IMPORT(uint32_t, call_chain_extension, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(void, origin, uint32_t)
POLKAVM_IMPORT(uint32_t, debug_message, uint32_t, uint32_t)
POLKAVM_IMPORT(void, seal_return, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(uint32_t, set_code_hash, uint32_t)
POLKAVM_IMPORT(uint64_t, ref_time_left)
POLKAVM_IMPORT(uint64_t, instantiation_nonce,)
POLKAVM_IMPORT(void, return_data_copy, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(uint32_t, transfer, uint32_t, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(uint64_t, return_data_size)
POLKAVM_IMPORT(void, set_immutable_data, uint32_t, uint32_t);
POLKAVM_IMPORT(uint64_t, set_storage, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t)
POLKAVM_IMPORT(void, value_transferred, uint32_t)
POLKAVM_IMPORT(void, weight_to_fee, uint64_t, uint64_t, uint32_t);
+107
View File
@@ -10,6 +10,113 @@ use inkwell::{context::Context, memory_buffer::MemoryBuffer, module::Module, sup
include!(concat!(env!("OUT_DIR"), "/polkavm_imports.rs"));
pub static SBRK: &str = "__sbrk_internal";
pub static MEMORY_SIZE: &str = "__msize";
pub static ADDRESS: &str = "address";
pub static BALANCE: &str = "balance";
pub static BALANCE_OF: &str = "balance_of";
pub static BASE_FEE: &str = "base_fee";
pub static BLOCK_HASH: &str = "block_hash";
pub static BLOCK_NUMBER: &str = "block_number";
pub static CALL: &str = "call";
pub static CALL_DATA_COPY: &str = "call_data_copy";
pub static CALL_DATA_LOAD: &str = "call_data_load";
pub static CALL_DATA_SIZE: &str = "call_data_size";
pub static CALLER: &str = "caller";
pub static CHAIN_ID: &str = "chain_id";
pub static CODE_SIZE: &str = "code_size";
pub static CODE_HASH: &str = "code_hash";
pub static DELEGATE_CALL: &str = "delegate_call";
pub static DEPOSIT_EVENT: &str = "deposit_event";
pub static GAS_LIMIT: &str = "gas_limit";
pub static GAS_PRICE: &str = "gas_price";
pub static GET_IMMUTABLE_DATA: &str = "get_immutable_data";
pub static GET_STORAGE: &str = "get_storage";
pub static HASH_KECCAK_256: &str = "hash_keccak_256";
pub static INSTANTIATE: &str = "instantiate";
pub static NOW: &str = "now";
pub static ORIGIN: &str = "origin";
pub static REF_TIME_LEFT: &str = "ref_time_left";
pub static RETURN: &str = "seal_return";
pub static RETURNDATACOPY: &str = "return_data_copy";
pub static RETURNDATASIZE: &str = "return_data_size";
pub static SET_IMMUTABLE_DATA: &str = "set_immutable_data";
pub static SET_STORAGE: &str = "set_storage";
pub static VALUE_TRANSFERRED: &str = "value_transferred";
pub static WEIGHT_TO_FEE: &str = "weight_to_fee";
/// All imported runtime API symbols.
/// Useful for configuring common attributes and linkage.
pub static IMPORTS: [&str; 34] = [
SBRK,
MEMORY_SIZE,
ADDRESS,
BALANCE,
BALANCE_OF,
BASE_FEE,
BLOCK_HASH,
BLOCK_NUMBER,
CALL,
CALL_DATA_COPY,
CALL_DATA_LOAD,
CALL_DATA_SIZE,
CALLER,
CHAIN_ID,
CODE_SIZE,
CODE_HASH,
DELEGATE_CALL,
DEPOSIT_EVENT,
GAS_LIMIT,
GAS_PRICE,
GET_IMMUTABLE_DATA,
GET_STORAGE,
HASH_KECCAK_256,
INSTANTIATE,
NOW,
ORIGIN,
REF_TIME_LEFT,
RETURN,
RETURNDATACOPY,
RETURNDATASIZE,
SET_IMMUTABLE_DATA,
SET_STORAGE,
VALUE_TRANSFERRED,
WEIGHT_TO_FEE,
];
/// Creates a LLVM module from the [BITCODE].
/// The module imports `pallet-revive` runtime API functions.
/// Returns `Error` if the bitcode fails to parse, which should never happen.
+12 -2
View File
@@ -24,7 +24,7 @@ thiserror = { workspace = true }
anyhow = { workspace = true }
which = { workspace = true }
path-slash = { workspace = true }
rayon = { workspace = true }
rayon = { workspace = true, optional = true }
serde = { workspace = true }
serde_json = { workspace = true }
@@ -41,6 +41,16 @@ inkwell = { workspace = true }
revive-common = { workspace = true }
revive-llvm-context = { workspace = true }
[target.'cfg(target_env = "musl")'.dependencies]
mimalloc = { version = "*", default-features = false }
[target.'cfg(target_os = "emscripten")'.dependencies]
libc = { workspace = true }
inkwell = { workspace = true, features = ["target-riscv", "llvm18-0-no-llvm-linking"]}
[build-dependencies]
git2 = { workspace = true }
[features]
parallel = ["rayon"]
default = ["parallel"]
+7
View File
@@ -0,0 +1,7 @@
fn main() {
let repo = git2::Repository::open("../..").expect("should be a repository");
let head = repo.head().expect("should have head");
let commit = head.peel_to_commit().expect("should have commit");
let id = &commit.id().to_string()[..7];
println!("cargo:rustc-env=GIT_COMMIT_HASH={id}");
}
+4 -8
View File
@@ -8,6 +8,7 @@ use std::path::Path;
use crate::solc::combined_json::CombinedJson;
use crate::solc::standard_json::output::Output as StandardJsonOutput;
use crate::solc::version::Version as SolcVersion;
use crate::ResolcVersion;
use self::contract::Contract;
@@ -40,11 +41,7 @@ impl Build {
}
/// Writes all contracts assembly and bytecode to the combined JSON.
pub fn write_to_combined_json(
self,
combined_json: &mut CombinedJson,
resolc_version: &semver::Version,
) -> anyhow::Result<()> {
pub fn write_to_combined_json(self, combined_json: &mut CombinedJson) -> anyhow::Result<()> {
for (path, contract) in self.contracts.into_iter() {
let combined_json_contract = combined_json
.contracts
@@ -61,7 +58,7 @@ impl Build {
contract.write_to_combined_json(combined_json_contract)?;
}
combined_json.zk_version = Some(resolc_version.to_string());
combined_json.revive_version = Some(ResolcVersion::default().long);
Ok(())
}
@@ -71,7 +68,6 @@ impl Build {
mut self,
standard_json: &mut StandardJsonOutput,
solc_version: &SolcVersion,
resolc_version: &semver::Version,
) -> anyhow::Result<()> {
let contracts = match standard_json.contracts.as_mut() {
Some(contracts) => contracts,
@@ -90,7 +86,7 @@ impl Build {
standard_json.version = Some(solc_version.default.to_string());
standard_json.long_version = Some(solc_version.long.to_owned());
standard_json.zk_version = Some(resolc_version.to_string());
standard_json.revive_version = Some(ResolcVersion::default().long);
Ok(())
}
+18 -10
View File
@@ -8,6 +8,7 @@ use std::collections::HashSet;
use serde::Deserialize;
use serde::Serialize;
use sha3::Digest;
use crate::evmla::ethereal_ir::entry_link::EntryLink;
use crate::evmla::ethereal_ir::EtherealIR;
@@ -45,7 +46,7 @@ impl Assembly {
/// Gets the contract `keccak256` hash.
pub fn keccak256(&self) -> String {
let json = serde_json::to_vec(self).expect("Always valid");
revive_llvm_context::polkavm_utils::keccak256(json.as_slice())
hex::encode(sha3::Keccak256::digest(json.as_slice()))
}
/// Sets the full contract path.
@@ -199,6 +200,7 @@ where
revive_llvm_context::PolkaVMDummyLLVMWritable::default(),
)
.declare(context)?;
revive_llvm_context::PolkaVMImmutableDataLoadFunction.declare(context)?;
entry.into_llvm(context)?;
@@ -211,9 +213,10 @@ where
) -> anyhow::Result<()> {
let full_path = self.full_path().to_owned();
if let Some(debug_config) = context.debug_config() {
debug_config.dump_evmla(full_path.as_str(), self.to_string().as_str())?;
}
context
.debug_config()
.dump_evmla(full_path.as_str(), self.to_string().as_str())?;
let deploy_code_blocks = EtherealIR::get_blocks(
context.evmla().version.to_owned(),
revive_llvm_context::PolkaVMCodeType::Deploy,
@@ -227,9 +230,11 @@ where
.ok_or_else(|| anyhow::anyhow!("Runtime code data not found"))?
.remove("0")
.expect("Always exists");
if let Some(debug_config) = context.debug_config() {
debug_config.dump_evmla(full_path.as_str(), data.to_string().as_str())?;
}
context
.debug_config()
.dump_evmla(full_path.as_str(), data.to_string().as_str())?;
let runtime_code_instructions = match data {
Data::Assembly(assembly) => assembly
.code
@@ -252,9 +257,11 @@ where
blocks.extend(runtime_code_blocks);
let mut ethereal_ir =
EtherealIR::new(context.evmla().version.to_owned(), extra_metadata, blocks)?;
if let Some(debug_config) = context.debug_config() {
debug_config.dump_ethir(full_path.as_str(), ethereal_ir.to_string().as_str())?;
}
context
.debug_config()
.dump_ethir(full_path.as_str(), ethereal_ir.to_string().as_str())?;
ethereal_ir.declare(context)?;
ethereal_ir.into_llvm(context)?;
@@ -266,6 +273,7 @@ where
revive_llvm_context::PolkaVMCodeType::Runtime,
))
.into_llvm(context)?;
revive_llvm_context::PolkaVMImmutableDataLoadFunction.into_llvm(context)?;
Ok(())
}
@@ -45,7 +45,7 @@ impl Element {
fn pop_arguments_llvm<'ctx, D>(
&mut self,
context: &mut revive_llvm_context::PolkaVMContext<'ctx, D>,
) -> Vec<inkwell::values::BasicValueEnum<'ctx>>
) -> anyhow::Result<Vec<inkwell::values::BasicValueEnum<'ctx>>>
where
D: revive_llvm_context::PolkaVMDependency + Clone,
{
@@ -57,15 +57,13 @@ impl Element {
[self.stack.elements.len() + input_size - output_size - 1 - index]
.to_llvm()
.into_pointer_value();
let value = context
.build_load(
revive_llvm_context::PolkaVMPointer::new_stack_field(context, pointer),
format!("argument_{index}").as_str(),
)
.unwrap();
let value = context.build_load(
revive_llvm_context::PolkaVMPointer::new_stack_field(context, pointer),
format!("argument_{index}").as_str(),
)?;
arguments.push(value);
}
arguments
Ok(arguments)
}
}
@@ -426,7 +424,7 @@ where
InstructionName::JUMPDEST => Ok(None),
InstructionName::ADD => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_arithmetic::addition(
context,
arguments[0].into_int_value(),
@@ -435,7 +433,7 @@ where
.map(Some)
}
InstructionName::SUB => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_arithmetic::subtraction(
context,
arguments[0].into_int_value(),
@@ -444,7 +442,7 @@ where
.map(Some)
}
InstructionName::MUL => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_arithmetic::multiplication(
context,
arguments[0].into_int_value(),
@@ -453,7 +451,7 @@ where
.map(Some)
}
InstructionName::DIV => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_arithmetic::division(
context,
arguments[0].into_int_value(),
@@ -462,7 +460,7 @@ where
.map(Some)
}
InstructionName::MOD => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_arithmetic::remainder(
context,
arguments[0].into_int_value(),
@@ -471,7 +469,7 @@ where
.map(Some)
}
InstructionName::SDIV => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_arithmetic::division_signed(
context,
arguments[0].into_int_value(),
@@ -480,7 +478,7 @@ where
.map(Some)
}
InstructionName::SMOD => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_arithmetic::remainder_signed(
context,
arguments[0].into_int_value(),
@@ -490,7 +488,7 @@ where
}
InstructionName::LT => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_comparison::compare(
context,
arguments[0].into_int_value(),
@@ -500,7 +498,7 @@ where
.map(Some)
}
InstructionName::GT => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_comparison::compare(
context,
arguments[0].into_int_value(),
@@ -510,7 +508,7 @@ where
.map(Some)
}
InstructionName::EQ => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_comparison::compare(
context,
arguments[0].into_int_value(),
@@ -520,7 +518,7 @@ where
.map(Some)
}
InstructionName::ISZERO => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_comparison::compare(
context,
arguments[0].into_int_value(),
@@ -530,7 +528,7 @@ where
.map(Some)
}
InstructionName::SLT => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_comparison::compare(
context,
arguments[0].into_int_value(),
@@ -540,7 +538,7 @@ where
.map(Some)
}
InstructionName::SGT => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_comparison::compare(
context,
arguments[0].into_int_value(),
@@ -551,7 +549,7 @@ where
}
InstructionName::OR => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_bitwise::or(
context,
arguments[0].into_int_value(),
@@ -560,7 +558,7 @@ where
.map(Some)
}
InstructionName::XOR => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_bitwise::xor(
context,
arguments[0].into_int_value(),
@@ -569,7 +567,7 @@ where
.map(Some)
}
InstructionName::NOT => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_bitwise::xor(
context,
arguments[0].into_int_value(),
@@ -578,7 +576,7 @@ where
.map(Some)
}
InstructionName::AND => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_bitwise::and(
context,
arguments[0].into_int_value(),
@@ -587,7 +585,7 @@ where
.map(Some)
}
InstructionName::SHL => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_bitwise::shift_left(
context,
arguments[0].into_int_value(),
@@ -596,7 +594,7 @@ where
.map(Some)
}
InstructionName::SHR => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_bitwise::shift_right(
context,
arguments[0].into_int_value(),
@@ -605,7 +603,7 @@ where
.map(Some)
}
InstructionName::SAR => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_bitwise::shift_right_arithmetic(
context,
arguments[0].into_int_value(),
@@ -614,7 +612,7 @@ where
.map(Some)
}
InstructionName::BYTE => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_bitwise::byte(
context,
arguments[0].into_int_value(),
@@ -624,7 +622,7 @@ where
}
InstructionName::ADDMOD => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_math::add_mod(
context,
arguments[0].into_int_value(),
@@ -634,7 +632,7 @@ where
.map(Some)
}
InstructionName::MULMOD => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_math::mul_mod(
context,
arguments[0].into_int_value(),
@@ -644,7 +642,7 @@ where
.map(Some)
}
InstructionName::EXP => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_math::exponent(
context,
arguments[0].into_int_value(),
@@ -653,7 +651,7 @@ where
.map(Some)
}
InstructionName::SIGNEXTEND => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_math::sign_extend(
context,
arguments[0].into_int_value(),
@@ -663,7 +661,7 @@ where
}
InstructionName::SHA3 | InstructionName::KECCAK256 => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_crypto::sha3(
context,
arguments[0].into_int_value(),
@@ -673,7 +671,7 @@ where
}
InstructionName::MLOAD => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_memory::load(
context,
arguments[0].into_int_value(),
@@ -681,7 +679,7 @@ where
.map(Some)
}
InstructionName::MSTORE => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_memory::store(
context,
arguments[0].into_int_value(),
@@ -690,7 +688,7 @@ where
.map(|_| None)
}
InstructionName::MSTORE8 => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_memory::store_byte(
context,
arguments[0].into_int_value(),
@@ -699,7 +697,7 @@ where
.map(|_| None)
}
InstructionName::MCOPY => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
let destination = revive_llvm_context::PolkaVMPointer::new_with_offset(
context,
revive_llvm_context::PolkaVMAddressSpace::Heap,
@@ -725,7 +723,7 @@ where
}
InstructionName::SLOAD => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_storage::load(
context,
arguments[0].into_int_value(),
@@ -733,7 +731,7 @@ where
.map(Some)
}
InstructionName::SSTORE => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_storage::store(
context,
arguments[0].into_int_value(),
@@ -742,7 +740,7 @@ where
.map(|_| None)
}
InstructionName::TLOAD => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_storage::transient_load(
context,
arguments[0].into_int_value(),
@@ -750,7 +748,7 @@ where
.map(Some)
}
InstructionName::TSTORE => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_storage::transient_store(
context,
arguments[0].into_int_value(),
@@ -766,27 +764,28 @@ where
let offset = context
.solidity_mut()
.get_or_allocate_immutable(key.as_str());
.get_or_allocate_immutable(key.as_str())
/ revive_common::BYTE_LENGTH_WORD;
let index = context.word_const(offset as u64);
let index = context.xlen_type().const_int(offset as u64, false);
revive_llvm_context::polkavm_evm_immutable::load(context, index).map(Some)
}
InstructionName::ASSIGNIMMUTABLE => {
let mut arguments = self.pop_arguments_llvm(context);
let mut arguments = self.pop_arguments_llvm(context)?;
let key = self
.instruction
.value
.ok_or_else(|| anyhow::anyhow!("Instruction value missing"))?;
let offset = context.solidity_mut().allocate_immutable(key.as_str());
let offset = context.solidity_mut().allocate_immutable(key.as_str())
/ revive_common::BYTE_LENGTH_WORD;
let index = context.word_const(offset as u64);
let index = context.xlen_type().const_int(offset as u64, false);
let value = arguments.pop().expect("Always exists").into_int_value();
revive_llvm_context::polkavm_evm_immutable::store(context, index, value)
.map(|_| None)
}
InstructionName::CALLDATALOAD => {
match context
.code_type()
@@ -796,7 +795,7 @@ where
Ok(Some(context.word_const(0).as_basic_value_enum()))
}
revive_llvm_context::PolkaVMCodeType::Runtime => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_calldata::load(
context,
arguments[0].into_int_value(),
@@ -819,7 +818,7 @@ where
}
}
InstructionName::CALLDATACOPY => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
match context
.code_type()
@@ -862,7 +861,7 @@ where
}
}
InstructionName::CODECOPY => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
let parent = context.module().get_name().to_str().expect("Always valid");
let source = &self.stack_input.elements[1];
@@ -917,7 +916,7 @@ where
revive_llvm_context::polkavm_evm_return_data::size(context).map(Some)
}
InstructionName::RETURNDATACOPY => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_return_data::copy(
context,
arguments[0].into_int_value(),
@@ -927,7 +926,7 @@ where
.map(|_| None)
}
InstructionName::EXTCODESIZE => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_ext_code::size(
context,
Some(arguments[0].into_int_value()),
@@ -935,7 +934,7 @@ where
.map(Some)
}
InstructionName::EXTCODEHASH => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_ext_code::hash(
context,
arguments[0].into_int_value(),
@@ -944,7 +943,7 @@ where
}
InstructionName::RETURN => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_return::r#return(
context,
arguments[0].into_int_value(),
@@ -953,7 +952,7 @@ where
.map(|_| None)
}
InstructionName::REVERT => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_return::revert(
context,
arguments[0].into_int_value(),
@@ -969,7 +968,7 @@ where
}
InstructionName::LOG0 => {
let mut arguments = self.pop_arguments_llvm(context);
let mut arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_event::log(
context,
arguments.remove(0).into_int_value(),
@@ -982,7 +981,7 @@ where
.map(|_| None)
}
InstructionName::LOG1 => {
let mut arguments = self.pop_arguments_llvm(context);
let mut arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_event::log(
context,
arguments.remove(0).into_int_value(),
@@ -995,7 +994,7 @@ where
.map(|_| None)
}
InstructionName::LOG2 => {
let mut arguments = self.pop_arguments_llvm(context);
let mut arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_event::log(
context,
arguments.remove(0).into_int_value(),
@@ -1008,7 +1007,7 @@ where
.map(|_| None)
}
InstructionName::LOG3 => {
let mut arguments = self.pop_arguments_llvm(context);
let mut arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_event::log(
context,
arguments.remove(0).into_int_value(),
@@ -1021,7 +1020,7 @@ where
.map(|_| None)
}
InstructionName::LOG4 => {
let mut arguments = self.pop_arguments_llvm(context);
let mut arguments = self.pop_arguments_llvm(context)?;
revive_llvm_context::polkavm_evm_event::log(
context,
arguments.remove(0).into_int_value(),
@@ -1035,7 +1034,7 @@ where
}
InstructionName::CALL => {
let mut arguments = self.pop_arguments_llvm(context);
let mut arguments = self.pop_arguments_llvm(context)?;
let gas = arguments.remove(0).into_int_value();
let address = arguments.remove(0).into_int_value();
@@ -1060,7 +1059,7 @@ where
.map(Some)
}
InstructionName::STATICCALL => {
let mut arguments = self.pop_arguments_llvm(context);
let mut arguments = self.pop_arguments_llvm(context)?;
let gas = arguments.remove(0).into_int_value();
let address = arguments.remove(0).into_int_value();
@@ -1084,7 +1083,7 @@ where
.map(Some)
}
InstructionName::DELEGATECALL => {
let mut arguments = self.pop_arguments_llvm(context);
let mut arguments = self.pop_arguments_llvm(context)?;
let gas = arguments.remove(0).into_int_value();
let address = arguments.remove(0).into_int_value();
@@ -1097,7 +1096,6 @@ where
context,
gas,
address,
None,
input_offset,
input_size,
output_offset,
@@ -1108,7 +1106,7 @@ where
}
InstructionName::CREATE | InstructionName::ZK_CREATE => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
let value = arguments[0].into_int_value();
let input_offset = arguments[1].into_int_value();
@@ -1124,7 +1122,7 @@ where
.map(Some)
}
InstructionName::CREATE2 | InstructionName::ZK_CREATE2 => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
let value = arguments[0].into_int_value();
let input_offset = arguments[1].into_int_value();
@@ -1155,7 +1153,7 @@ where
revive_llvm_context::polkavm_evm_ether_gas::gas(context).map(Some)
}
InstructionName::BALANCE => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
let address = arguments[0].into_int_value();
revive_llvm_context::polkavm_evm_ether_gas::balance(context, address).map(Some)
@@ -1184,7 +1182,7 @@ where
revive_llvm_context::polkavm_evm_contract_context::block_number(context).map(Some)
}
InstructionName::BLOCKHASH => {
let arguments = self.pop_arguments_llvm(context);
let arguments = self.pop_arguments_llvm(context)?;
let index = arguments[0].into_int_value();
revive_llvm_context::polkavm_evm_contract_context::block_hash(context, index)
@@ -1207,7 +1205,7 @@ where
anyhow::bail!("The `BLOBBASEFEE` instruction is not supported until zkVM v1.5.0");
}
InstructionName::MSIZE => {
revive_llvm_context::polkavm_evm_contract_context::msize(context).map(Some)
revive_llvm_context::polkavm_evm_memory::msize(context).map(Some)
}
InstructionName::CALLCODE => {
@@ -1222,7 +1220,7 @@ where
anyhow::bail!("The `EXTCODECOPY` instruction is not supported");
}
InstructionName::SELFDESTRUCT => {
let _arguments = self.pop_arguments_llvm(context);
let _arguments = self.pop_arguments_llvm(context)?;
anyhow::bail!("The `SELFDESTRUCT` instruction is not supported");
}
@@ -1234,7 +1232,7 @@ where
return_address,
..
} => {
let mut arguments = self.pop_arguments_llvm(context);
let mut arguments = self.pop_arguments_llvm(context)?;
arguments.pop();
arguments.reverse();
arguments.pop();
@@ -1297,7 +1295,7 @@ where
return Ok(());
}
InstructionName::RecursiveReturn { .. } => {
let mut arguments = self.pop_arguments_llvm(context);
let mut arguments = self.pop_arguments_llvm(context)?;
arguments.reverse();
arguments.pop();
@@ -1139,7 +1139,6 @@ where
.integer_type(revive_common::BIT_LENGTH_BOOLEAN)
.as_basic_type_enum()],
output_size,
false,
);
(r#type, output_size)
}
@@ -1156,7 +1155,6 @@ where
input_size
],
output_size,
false,
);
(r#type, output_size)
}
@@ -1165,7 +1163,7 @@ where
self.name.as_str(),
function_type,
output_size,
Some(inkwell::module::Linkage::Private),
Some(inkwell::module::Linkage::External),
)?;
function
.borrow_mut()
@@ -1177,7 +1175,7 @@ where
}
fn into_llvm(self, context: &mut revive_llvm_context::PolkaVMContext<D>) -> anyhow::Result<()> {
context.set_current_function(self.name.as_str())?;
context.set_current_function(self.name.as_str(), None)?;
for (key, blocks) in self.blocks.iter() {
for (index, block) in blocks.iter().enumerate() {
@@ -1299,6 +1297,8 @@ where
}
}
context.pop_debug_scope();
Ok(())
}
}
+54 -76
View File
@@ -7,6 +7,7 @@ pub(crate) mod missing_libraries;
pub(crate) mod process;
pub(crate) mod project;
pub(crate) mod solc;
pub(crate) mod version;
pub(crate) mod warning;
pub(crate) mod yul;
@@ -14,15 +15,22 @@ pub use self::build::contract::Contract as ContractBuild;
pub use self::build::Build;
pub use self::missing_libraries::MissingLibraries;
pub use self::process::input::Input as ProcessInput;
#[cfg(not(target_os = "emscripten"))]
pub use self::process::native_process::NativeProcess;
pub use self::process::output::Output as ProcessOutput;
pub use self::process::run as run_process;
pub use self::process::EXECUTABLE;
#[cfg(target_os = "emscripten")]
pub use self::process::worker_process::WorkerProcess;
pub use self::process::Process;
pub use self::project::contract::Contract as ProjectContract;
pub use self::project::Project;
pub use self::r#const::*;
pub use self::solc::combined_json::contract::Contract as SolcCombinedJsonContract;
pub use self::solc::combined_json::CombinedJson as SolcCombinedJson;
pub use self::solc::pipeline::Pipeline as SolcPipeline;
#[cfg(not(target_os = "emscripten"))]
pub use self::solc::solc_compiler::SolcCompiler;
#[cfg(target_os = "emscripten")]
pub use self::solc::soljson_compiler::SoljsonCompiler;
pub use self::solc::standard_json::input::language::Language as SolcStandardJsonInputLanguage;
pub use self::solc::standard_json::input::settings::metadata::Metadata as SolcStandardJsonInputSettingsMetadata;
pub use self::solc::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer;
@@ -37,23 +45,23 @@ pub use self::solc::standard_json::output::contract::evm::EVM as SolcStandardJso
pub use self::solc::standard_json::output::contract::Contract as SolcStandardJsonOutputContract;
pub use self::solc::standard_json::output::Output as SolcStandardJsonOutput;
pub use self::solc::version::Version as SolcVersion;
pub use self::solc::Compiler as SolcCompiler;
pub use self::solc::Compiler;
pub use self::version::Version as ResolcVersion;
pub use self::warning::Warning;
#[cfg(not(target_os = "emscripten"))]
pub mod test_utils;
pub mod tests;
use std::collections::BTreeSet;
use std::collections::{BTreeMap, BTreeSet};
use std::path::PathBuf;
/// Runs the Yul mode.
pub fn yul(
pub fn yul<T: Compiler>(
input_files: &[PathBuf],
solc: &mut SolcCompiler,
solc: &mut T,
optimizer_settings: revive_llvm_context::OptimizerSettings,
is_system_mode: bool,
include_metadata_hash: bool,
debug_config: Option<revive_llvm_context::DebugConfig>,
debug_config: revive_llvm_context::DebugConfig,
) -> anyhow::Result<Build> {
let path = match input_files.len() {
1 => input_files.first().expect("Always exists"),
@@ -64,28 +72,17 @@ pub fn yul(
),
};
let solc_validator = if is_system_mode {
None
} else {
if solc.version()?.default != SolcCompiler::LAST_SUPPORTED_VERSION {
anyhow::bail!(
if solc.version()?.default != solc::LAST_SUPPORTED_VERSION {
anyhow::bail!(
"The Yul mode is only supported with the most recent version of the Solidity compiler: {}",
SolcCompiler::LAST_SUPPORTED_VERSION,
solc::LAST_SUPPORTED_VERSION,
);
}
Some(&*solc)
};
}
let solc_validator = Some(&*solc);
let project = Project::try_from_yul_path(path, solc_validator)?;
let build = project.compile(
optimizer_settings,
is_system_mode,
include_metadata_hash,
false,
debug_config,
)?;
let build = project.compile(optimizer_settings, include_metadata_hash, debug_config)?;
Ok(build)
}
@@ -94,9 +91,8 @@ pub fn yul(
pub fn llvm_ir(
input_files: &[PathBuf],
optimizer_settings: revive_llvm_context::OptimizerSettings,
is_system_mode: bool,
include_metadata_hash: bool,
debug_config: Option<revive_llvm_context::DebugConfig>,
debug_config: revive_llvm_context::DebugConfig,
) -> anyhow::Result<Build> {
let path = match input_files.len() {
1 => input_files.first().expect("Always exists"),
@@ -109,35 +105,28 @@ pub fn llvm_ir(
let project = Project::try_from_llvm_ir_path(path)?;
let build = project.compile(
optimizer_settings,
is_system_mode,
include_metadata_hash,
false,
debug_config,
)?;
let build = project.compile(optimizer_settings, include_metadata_hash, debug_config)?;
Ok(build)
}
/// Runs the standard output mode.
#[allow(clippy::too_many_arguments)]
pub fn standard_output(
pub fn standard_output<T: Compiler>(
input_files: &[PathBuf],
libraries: Vec<String>,
solc: &mut SolcCompiler,
solc: &mut T,
evm_version: Option<revive_common::EVMVersion>,
solc_optimizer_enabled: bool,
optimizer_settings: revive_llvm_context::OptimizerSettings,
force_evmla: bool,
is_system_mode: bool,
include_metadata_hash: bool,
base_path: Option<String>,
include_paths: Vec<String>,
allow_paths: Option<String>,
remappings: Option<BTreeSet<String>>,
suppressed_warnings: Option<Vec<Warning>>,
debug_config: Option<revive_llvm_context::DebugConfig>,
debug_config: revive_llvm_context::DebugConfig,
) -> anyhow::Result<Build> {
let solc_version = solc.version()?;
let solc_pipeline = SolcPipeline::new(&solc_version, force_evmla);
@@ -154,14 +143,13 @@ pub fn standard_output(
None,
&solc_version.default,
optimizer_settings.is_fallback_to_size_enabled(),
optimizer_settings.is_system_request_memoization_disabled(),
),
None,
solc_pipeline == SolcPipeline::Yul,
suppressed_warnings,
)?;
let source_code_files = solc_input
let mut source_code_files: BTreeMap<String, String> = solc_input
.sources
.iter()
.map(|(path, source)| (path.to_owned(), source.content.to_owned()))
@@ -192,40 +180,44 @@ pub fn standard_output(
}
}
// load import callbacks unspecified in input
if let Some(sources) = &solc_output.sources {
for source_path in sources.keys() {
if !source_code_files.contains_key(source_path) {
let source = std::fs::read_to_string(source_path).map_err(|e| {
anyhow::anyhow!("Can't read source file at `{}`: {}", source_path, e)
})?;
let _ = source_code_files.insert(source_path.clone(), source);
}
}
}
let project = solc_output.try_to_project(
source_code_files,
libraries,
solc_pipeline,
&solc_version,
debug_config.as_ref(),
&debug_config,
)?;
let build = project.compile(
optimizer_settings,
is_system_mode,
include_metadata_hash,
false,
debug_config,
)?;
let build = project.compile(optimizer_settings, include_metadata_hash, debug_config)?;
Ok(build)
}
/// Runs the standard JSON mode.
#[allow(clippy::too_many_arguments)]
pub fn standard_json(
solc: &mut SolcCompiler,
pub fn standard_json<T: Compiler>(
solc: &mut T,
detect_missing_libraries: bool,
force_evmla: bool,
is_system_mode: bool,
base_path: Option<String>,
include_paths: Vec<String>,
allow_paths: Option<String>,
debug_config: Option<revive_llvm_context::DebugConfig>,
debug_config: revive_llvm_context::DebugConfig,
) -> anyhow::Result<()> {
let solc_version = solc.version()?;
let solc_pipeline = SolcPipeline::new(&solc_version, force_evmla);
let resolc_version = semver::Version::parse(env!("CARGO_PKG_VERSION")).expect("Always valid");
let solc_input = SolcStandardJsonInput::try_from_stdin(solc_pipeline)?;
let source_code_files = solc_input
@@ -267,25 +259,15 @@ pub fn standard_json(
libraries,
solc_pipeline,
&solc_version,
debug_config.as_ref(),
&debug_config,
)?;
if detect_missing_libraries {
let missing_libraries = project.get_missing_libraries();
missing_libraries.write_to_standard_json(
&mut solc_output,
&solc_version,
&resolc_version,
)?;
missing_libraries.write_to_standard_json(&mut solc_output, &solc_version)?;
} else {
let build = project.compile(
optimizer_settings,
is_system_mode,
include_metadata_hash,
false,
debug_config,
)?;
build.write_to_standard_json(&mut solc_output, &solc_version, &resolc_version)?;
let build = project.compile(optimizer_settings, include_metadata_hash, debug_config)?;
build.write_to_standard_json(&mut solc_output, &solc_version)?;
}
serde_json::to_writer(std::io::stdout(), &solc_output)?;
std::process::exit(0);
@@ -293,28 +275,25 @@ pub fn standard_json(
/// Runs the combined JSON mode.
#[allow(clippy::too_many_arguments)]
pub fn combined_json(
pub fn combined_json<T: Compiler>(
format: String,
input_files: &[PathBuf],
libraries: Vec<String>,
solc: &mut SolcCompiler,
solc: &mut T,
evm_version: Option<revive_common::EVMVersion>,
solc_optimizer_enabled: bool,
optimizer_settings: revive_llvm_context::OptimizerSettings,
force_evmla: bool,
is_system_mode: bool,
include_metadata_hash: bool,
base_path: Option<String>,
include_paths: Vec<String>,
allow_paths: Option<String>,
remappings: Option<BTreeSet<String>>,
suppressed_warnings: Option<Vec<Warning>>,
debug_config: Option<revive_llvm_context::DebugConfig>,
debug_config: revive_llvm_context::DebugConfig,
output_directory: Option<PathBuf>,
overwrite: bool,
) -> anyhow::Result<()> {
let resolc_version = semver::Version::parse(env!("CARGO_PKG_VERSION")).expect("Always valid");
let build = standard_output(
input_files,
libraries,
@@ -323,7 +302,6 @@ pub fn combined_json(
solc_optimizer_enabled,
optimizer_settings,
force_evmla,
is_system_mode,
include_metadata_hash,
base_path,
include_paths,
@@ -334,7 +312,7 @@ pub fn combined_json(
)?;
let mut combined_json = solc.combined_json(input_files, format.as_str())?;
build.write_to_combined_json(&mut combined_json, &resolc_version)?;
build.write_to_combined_json(&mut combined_json)?;
match output_directory {
Some(output_directory) => {
+2 -2
View File
@@ -5,6 +5,7 @@ use std::collections::HashSet;
use crate::solc::standard_json::output::Output as StandardJsonOutput;
use crate::solc::version::Version as SolcVersion;
use crate::ResolcVersion;
/// The missing Solidity libraries.
pub struct MissingLibraries {
@@ -23,7 +24,6 @@ impl MissingLibraries {
mut self,
standard_json: &mut StandardJsonOutput,
solc_version: &SolcVersion,
resolc_version: &semver::Version,
) -> anyhow::Result<()> {
let contracts = match standard_json.contracts.as_mut() {
Some(contracts) => contracts,
@@ -43,7 +43,7 @@ impl MissingLibraries {
standard_json.version = Some(solc_version.default.to_string());
standard_json.long_version = Some(solc_version.long.to_owned());
standard_json.zk_version = Some(resolc_version.to_string());
standard_json.revive_version = Some(ResolcVersion::default().long);
Ok(())
}

Some files were not shown because too many files have changed in this diff Show More