Compare commits

...

119 Commits

Author SHA1 Message Date
xermicus bec5d60b7c release resolc-0.1.0-dev.9 (#178)
Signed-off-by: xermicus <cyrill@parity.io>
2025-01-29 12:49:54 +01:00
xermicus 3608a5a143 runtime-api: pass call arguments in registers instead of spilling to stack (#174)
Companion to paritytech/polkadot-sdk#7319

Signed-off-by: xermicus <cyrill@parity.io>
2025-01-28 12:45:54 +01:00
Sebastian Miasojed 888723eb0d Fix issue with relative path in web worker (#169) 2025-01-22 23:58:47 +01:00
Cyrill Leutwiler fe1b3258d2 bump crate versions (#171)
- bump crate versions
- point out the supported polkadot sdk version in the changelog
2025-01-17 17:29:38 +01:00
Cyrill Leutwiler d8a72e580b release resolc-0.1.0-dev.8 (#170)
Signed-off-by: xermicus <cyrill@parity.io>
2025-01-17 17:00:25 +01:00
Cyrill Leutwiler bd1a22a702 update polkavm to 0.19 (#167)
paritytech/polkadot-sdk#7203 companion:

- Update the polkavm dependency to the latest version.
- Update the polkadot-sdk to the latest version.

Signed-off-by: xermicus <cyrill@parity.io>
2025-01-17 16:59:45 +01:00
Cyrill Leutwiler cec283986f llvm-builder: do not build clang by default (#168)
We only need LLD for cross compilation. This significantly reduces the LLVM build times in a cross compilation scenario. Update the README as a drive-by.
2025-01-17 16:03:04 +01:00
Siphamandla Mjoli 06f43083c3 multiple resolc fixes and improvements (#151)
- Error out early on compiler invocations with invalid base path or include path flags.
- Do not error out if no files and no errors were produced. This aligns resolc closer to sloc.
- Add a CLI test with an involved fixture containing multiple contract remappings to properly testing the standard JSON path.
- Fixes input normalization in the Wasm version.



Co-authored-by: Cyrill Leutwiler <cyrill@parity.io>
2025-01-17 10:10:47 +01:00
Sebastian Miasojed b78b2b2af9 Add test fixtures for the revive WASM version (#160) 2025-01-16 16:01:34 +01:00
Cyrill Leutwiler 8ffe072fee pin the LLVM version (#166)
Signed-off-by: xermicus <cyrill@parity.io>
2025-01-16 10:45:43 +01:00
Cyrill Leutwiler 71fb3ab8d6 vet the Makefile (#165)
- Move the emscripten target flags into the target configuration.
- Improve the readability of the Makefile.

Signed-off-by: xermicus <cyrill@parity.io>
2025-01-15 22:26:56 +01:00
Cyrill Leutwiler 3e7579580b vet workspace dependencies (#163)
- Update the used workspace dependencies.
- Remove the unused workspace dependencies.
- Add the machete CI workflow.
2025-01-15 20:14:54 +01:00
Cyrill Leutwiler ad805543b3 call and create set uncapped resource limits (#161)
- polkadot-sdk#6890 companion
- Adjust the gas price constant for the required polkadot-sdk version as a drive-by
2025-01-15 17:32:31 +01:00
Cyrill Leutwiler 4fbfb97b9e Fix the Wasm build cache (#159)
Follow-ups for https://github.com/paritytech/revive/pull/154
- Fix the cache in the Wasm build
- Remove a no longer needed script
- The Wasm build job uses parity-large
2025-01-15 11:36:59 +01:00
Sebastian Miasojed 939138d0cd Add the revive tests in the browsers (#158) 2025-01-14 22:29:02 +01:00
Cyrill Leutwiler 7f81f37e0c revive llvm builder utility (#154)
Pre-eliminary support for LLVM releases and resolc binary releases by streamlining the build process for all supported hosts platforms.

- Introduce the revive-llvm-builder crate with the revive-llvm builder utilty.
- Do not rely on the LLVM dependency in $PATH to decouple the system LLVM installation from the LLVM host dependency.
- Fix the emscripten build by decoupling the host and native LLVM dependencies. Thus allowing a single LLVM emscripten release that can be used on any host platform.
- An example Dockerfile building an alpine container with a fully statically linked resolc ELF binary.
- Remove the Debian builder utilities and workflow.
2025-01-13 15:58:27 +01:00
Cyrill Leutwiler fde9edab10 Remove the docs Makefile targets (#152)
Documentation of the contracts stack was moved into a dedicated repository.
2025-01-10 11:36:51 +01:00
Sebastian Miasojed d7d60da6f1 Add tests for the Revive WASM version (#147) 2025-01-10 09:12:43 +01: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
Sebastian Miasojed 5ac67bdc0d Initial wasm support 2024-08-29 17:28:31 +02:00
202 changed files with 9746 additions and 3226 deletions
+18
View File
@@ -0,0 +1,18 @@
[target.wasm32-unknown-emscripten]
rustflags = [
"-Clink-arg=-sEXPORTED_FUNCTIONS=_main,_free,_malloc",
"-Clink-arg=-sNO_INVOKE_RUN=1",
"-Clink-arg=-sEXIT_RUNTIME=1",
"-Clink-arg=-sALLOW_MEMORY_GROWTH=1",
"-Clink-arg=-sEXPORTED_RUNTIME_METHODS=FS,callMain,stringToNewUTF8",
"-Clink-arg=-sMODULARIZE=1",
"-Clink-arg=-sEXPORT_NAME=createRevive",
"-Clink-arg=-sWASM_ASYNC_COMPILATION=0",
"-Clink-arg=-sDYNAMIC_EXECUTION=0",
"-Clink-arg=-sALLOW_TABLE_GROWTH=1",
"-Clink-arg=--js-library=js/embed/soljson_interface.js",
"-Clink-arg=--pre-js=js/embed/pre.js",
"-Clink-arg=-sNODEJS_CATCH_EXIT=0",
"-Clink-arg=-sDISABLE_EXCEPTION_CATCHING=0",
"-Copt-level=3"
]
+7
View File
@@ -0,0 +1,7 @@
emsdk
llvm*/
target-llvm/
target/
node_modules/
utils/
build/
-38
View File
@@ -1,38 +0,0 @@
name: Build revive-debian
on:
workflow_dispatch:
env:
REVIVE_DEBIAN_PACKAGE: revive-debian-x86
DEBIAN_CONTAINER: revive-builder-debian-x86
DEBIAN_CONTAINER_BUILDER: build-debian-builder.sh
DEBIAN_CONTAINER_RUNNER: run-debian-builder.sh
REVIVE_DEBIAN_INSTALL: ${{ github.workspace }}/target/release
REVIVE_DEBIAN_BINARY: resolc
RUST_VERSION: "1.80"
jobs:
build-revive-debian-x86:
name: debian-container-x86
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: build-container
run: |
(cd utils && ./${{ env.DEBIAN_CONTAINER_BUILDER}} --build-arg RUST_VERSION=${{ env.RUST_VERSION}} . )
- name: build-revive-debian
run: |
rustup show
cargo --version
rustup +nightly show
cargo +nightly --version
bash --version
utils/${{ env.DEBIAN_CONTAINER_RUNNER }} utils/build-revive.sh -o ${{ env.REVIVE_DEBIAN_INSTALL}}
- uses: actions/upload-artifact@v4
with:
name: ${{ env.REVIVE_DEBIAN_PACKAGE }}
path: ${{ env.REVIVE_DEBIAN_INSTALL }}/${{ env.REVIVE_DEBIAN_BINARY }}
retention-days: 1
+142
View File
@@ -0,0 +1,142 @@
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
BUN_VERSION: 1.1.43
jobs:
build-revive-wasm:
runs-on: parity-large
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
- name: Cache LLVM build
id: cache-llvm
uses: actions/cache@v3
with:
path: |
target-llvm/emscripten/target-final
target-llvm/gnu/target-final
# Use a unique key based on LLVM version or configuration files to avoid cache invalidation
key: llvm-build-${{ runner.os }}-${{ hashFiles('LLVM.lock', 'Cargo.toml', 'Cargo.lock', 'crates/llvm-builder/**', '.github/workflows/build-revive-wasm.yml') }}
- name: Install system dependencies
run: |
sudo apt-get update && sudo apt-get install -y cmake ninja-build curl git libssl-dev pkg-config clang lld
- name: Install Rust stable toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
components: rust-src
target: wasm32-unknown-emscripten
- name: Install LLVM build dependencies
run: |
make install-llvm-builder
revive-llvm --target-env emscripten clone
- name: Setup revive environment variables
run: |
echo "LLVM_SYS_181_PREFIX=$(pwd)/target-llvm/gnu/target-final" >> $GITHUB_ENV
echo "REVIVE_LLVM_TARGET_PREFIX=$(pwd)/target-llvm/emscripten/target-final" >> $GITHUB_ENV
- name: Build host LLVM
if: steps.cache-llvm.outputs.cache-hit != 'true'
run: |
revive-llvm build --llvm-projects lld --llvm-projects clang
- name: Build target LLVM
if: steps.cache-llvm.outputs.cache-hit != 'true'
run: |
source emsdk/emsdk_env.sh
revive-llvm --target-env emscripten build --llvm-projects lld
- run: |
rustup show
cargo --version
rustup +nightly show
cargo +nightly --version
cmake --version
bash --version
- name: Use Cached LLVM
if: steps.cache-llvm.outputs.cache-hit == 'true'
run: |
echo "Using cached LLVM"
- name: Build revive
run: |
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
test-revive-wasm:
needs: build-revive-wasm
strategy:
matrix:
os: ["ubuntu-24.04", "macos-14", "windows-2022"]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Create Target Directory
run: mkdir -p ${{ env.REVIVE_WASM_INSTALL_DIR }}
- name: Download Artifact
uses: actions/download-artifact@v4
with:
name: revive-wasm
path: ${{ env.REVIVE_WASM_INSTALL_DIR }}
- name: Set Up Node.js
uses: actions/setup-node@v3
with:
node-version: "20"
- name: Install Bun on Windows
if: runner.os == 'Windows'
run: |
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
iex (new-object net.webclient).downloadstring('https://get.scoop.sh')
scoop install bun@${{ env.BUN_VERSION }}
scoop install wget
Join-Path (Resolve-Path ~).Path "scoop\shims" >> $Env:GITHUB_PATH
- name: Install Bun on macOS and Linux
if: runner.os != 'Windows'
run: |
curl -fsSL https://bun.sh/install | bash -s bun-v${{ env.BUN_VERSION }}
echo "$HOME/.bun/bin" >> $GITHUB_PATH
- name: Install packages
run: npm install
- name: Run Playwright tests
run: |
cd js
npx playwright install --with-deps
npx playwright test
- name: Test revive
run: |
echo "Running tests for ${{ matrix.os }}"
npm run test:wasm
+9 -6
View File
@@ -11,7 +11,7 @@ env:
jobs:
build-ubuntu-x86:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
@@ -25,16 +25,19 @@ jobs:
- name: Install LLVM
run: |
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
curl -sSL --output llvm.tar.xz https://github.com/paritytech/revive/releases/download/v0.1.0-dev.7/clang+llvm-18.1.8-x86_64-linux-gnu-ubuntu-24.04.tar.xz
mkdir llvm18
tar Jxf llvm.tar.xz -C llvm18/
echo "LLVM_SYS_181_PREFIX=$(pwd)/llvm18" >> $GITHUB_ENV
- name: Install apt dependencies
run: |
sudo add-apt-repository -y ppa:ethereum/ethereum
sudo apt update
sudo apt install -y libtinfo5 ethereum
sudo apt install -y ethereum
- name: Machete
uses: bnjbvr/cargo-machete@main
- name: Format
run: make format
+7 -2
View File
@@ -1,4 +1,5 @@
/target
target-llvm
*.dot
.vscode/
.DS_Store
@@ -6,11 +7,15 @@
/*.yul
/*.ll
/*.s
/llvm-project
/llvm18.0
/llvm*
node_modules
artifacts
tmp
package-lock.json
/*.html
/build
soljson.js
test-results
playwright-report
.cache
emsdk
+97
View File
@@ -0,0 +1,97 @@
# Changelog
## Unreleased
## v0.1.0-dev.9
This is a development pre-release.
### Added
### Changed
- Syscalls with more than 6 arguments now pack them into registers.
### Fixed
- Remove reloading of the resolc.js file (fix issue with relative path in web worker)
## v0.1.0-dev.8
This is a development pre-release.
### Added
- The `revive-llvm-builder` crate with the `revive-llvm` helper utility for streamlined management of the LLVM framework dependency.
- Initial support for running `resolc` in the browser.
### Changed
- Suported contracts runtime is polkadot-sdk git version `d62a90c8c729acd98c7e9a5cab9803b8b211ffc5`.
- The minimum supported Rust version is `1.81.0`.
- Error out early instead of invoking `solc` with invalid base or include path flags.
### Fixed
- Decouple the LLVM target dependency from the LLVM host dependency.
- Do not error out if no files and no errors were produced. This aligns resolc closer to solc.
- Fixes input normalization in the Wasm version.
## 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
+2213 -1684
View File
File diff suppressed because it is too large Load Diff
+48 -42
View File
@@ -3,7 +3,7 @@ resolver = "2"
members = ["crates/*"]
[workspace.package]
version = "0.1.0"
version = "0.1.0-dev.9"
authors = [
"Cyrill Leutwiler <cyrill@parity.io>",
"Parity Technologies <admin@parity.io>",
@@ -11,63 +11,69 @@ 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.9", path = "crates/benchmarks" }
revive-builtins = { version = "0.1.0-dev.9", path = "crates/builtins" }
revive-common = { version = "0.1.0-dev.9", path = "crates/common" }
revive-differential = { version = "0.1.0-dev.9", path = "crates/differential" }
revive-integration = { version = "0.1.0-dev.9", path = "crates/integration" }
revive-linker = { version = "0.1.0-dev.9", path = "crates/linker" }
lld-sys = { version = "0.1.0-dev.9", path = "crates/lld-sys" }
revive-llvm-context = { version = "0.1.0-dev.9", path = "crates/llvm-context" }
revive-runtime-api = { version = "0.1.0-dev.9", path = "crates/runtime-api" }
revive-runner = { version = "0.1.0-dev.9", path = "crates/runner" }
revive-solidity = { version = "0.1.0-dev.9", path = "crates/solidity" }
revive-stdlib = { version = "0.1.0-dev.9", path = "crates/stdlib" }
revive-build-utils = { version = "0.1.0-dev.9", path = "crates/build-utils" }
hex = "0.4"
petgraph = "0.6"
hex = "0.4.3"
cc = "1.0"
libc = "0.2"
libc = "0.2.169"
tempfile = "3.8"
anyhow = "1.0"
semver = { version = "1.0", features = [ "serde" ] }
itertools = "0.12"
serde = { version = "1.0", features = [ "derive" ] }
serde_json = { version = "1.0", features = [ "arbitrary_precision" ] }
semver = { version = "1.0", features = ["serde"] }
itertools = "0.14"
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["arbitrary_precision"] }
regex = "1.10"
once_cell = "1.19"
num = "0.4"
num = "0.4.3"
sha1 = "0.10"
sha2 = "0.10"
sha3 = "0.10"
md5 = "0.7"
colored = "2.1"
thiserror = "1.0"
which = "5.0"
md5 = "0.7.0"
thiserror = "2.0"
which = "7.0"
path-slash = "0.2"
rayon = "1.8"
clap = { version = "4", default-features = false, features = ["derive"] }
rand = "0.8"
polkavm-common = "0.14"
polkavm-linker = "0.14"
polkavm-disassembler = "0.14"
polkavm = "0.14"
alloy-primitives = { version = "0.8", features = ["serde"] }
alloy-sol-types = "0.8"
alloy-genesis = "0.3"
alloy-serde = "0.3"
env_logger = { version = "0.10.0", default-features = false }
serde_stacker = "0.1"
criterion = { version = "0.5", features = ["html_reports"] }
log = { version = "0.4" }
polkavm-common = "0.19.0"
polkavm-linker = "0.19.0"
polkavm-disassembler = "0.19.0"
polkavm = "0.19.0"
alloy-primitives = { version = "0.8.19", features = ["serde"] }
alloy-sol-types = "0.8.19"
alloy-genesis = "0.9.2"
alloy-serde = "0.9.2"
env_logger = { version = "0.11.6", default-features = false }
serde_stacker = "0.1.11"
criterion = { version = "0.5.1", features = ["html_reports"] }
log = { version = "0.4.25" }
git2 = { version = "0.20.0", default-features = false }
downloader = "0.2.8"
flate2 = "1.0.35"
fs_extra = "1.3.0"
num_cpus = "1"
tar = "0.4.43"
toml = "0.8.19"
assert_cmd = "2.0.16"
assert_fs = "1.1.2"
# 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 = "db40a66db71e8e7fe943dda5cd0e28078efa2a19" }
scale-info = { version = "2.11.6", default-features = false }
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk", rev = "4302f74f7874e6a894578731142a7b310a1449b0" }
# llvm
[workspace.dependencies.inkwell]
+32
View File
@@ -0,0 +1,32 @@
FROM rust:1.84.0 AS llvm-builder
WORKDIR /opt/revive
RUN apt update && \
apt upgrade -y && \
apt install -y cmake ninja-build curl git libssl-dev pkg-config clang lld musl
COPY . .
RUN make install-llvm-builder
RUN revive-llvm --target-env musl clone
RUN revive-llvm --target-env musl build --llvm-projects lld --llvm-projects clang
FROM messense/rust-musl-cross:x86_64-musl AS resolc-builder
WORKDIR /opt/revive
RUN apt update && \
apt upgrade -y && \
apt install -y pkg-config
COPY . .
COPY --from=llvm-builder /opt/revive/target-llvm /opt/revive/target-llvm
ENV LLVM_SYS_181_PREFIX=/opt/revive/target-llvm/musl/target-final
RUN make install-bin
FROM alpine:latest
ADD https://github.com/ethereum/solidity/releases/download/v0.8.28/solc-static-linux /usr/bin/solc
COPY --from=resolc-builder /root/.cargo/bin/resolc /usr/bin/resolc
RUN apk add --no-cache libc6-compat
RUN chmod +x /usr/bin/solc
+3
View File
@@ -0,0 +1,3 @@
url = "https://github.com/llvm/llvm-project.git"
branch = "release/18.x"
ref = "3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff"
+52 -20
View File
@@ -1,4 +1,24 @@
.PHONY: install format test test-solidity test-cli test-integration test-workspace clean docs docs-build
.PHONY: \
install \
install-bin \
install-npm \
install-wasm \
install-llvm-builder \
install-llvm \
format \
clippy \
machete \
test \
test-integration \
test-solidity \
test-workspace \
test-cli \
test-wasm \
test-llvm-builder
bench \
bench-pvm \
bench-evm \
clean
install: install-bin install-npm
@@ -8,12 +28,15 @@ install-bin:
install-npm:
npm install && npm fund
# 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
endif
install-revive:
cargo install --path crates/solidity --root $(REVIVE_INSTALL_DIR)
install-wasm: install-npm
cargo build --target wasm32-unknown-emscripten -p revive-solidity --release --no-default-features
install-llvm-builder:
cargo install --path crates/llvm-builder
install-llvm: install-llvm-builder
revive-llvm clone
revive-llvm build --llvm-projects lld --llvm-projects clang
format:
cargo fmt --all --check
@@ -21,7 +44,11 @@ format:
clippy:
cargo clippy --all-features --workspace --tests --benches -- --deny warnings --allow dead_code
test: format clippy test-cli test-workspace
machete:
cargo install cargo-machete
cargo machete
test: format clippy machete test-cli test-workspace
test-integration: install-bin
cargo test --package revive-integration
@@ -30,11 +57,22 @@ test-solidity: install
cargo test --package revive-solidity
test-workspace: install
cargo test --workspace
cargo test --workspace --exclude revive-llvm-builder
test-cli: install
npm run test:cli
test-wasm: install-wasm
npm run test:wasm
test-llvm-builder:
@echo "warning: the llvm-builder tests will take many hours"
cargo test --package revive-llvm-builder -- --test-threads=1
bench: install-bin
cargo criterion --all --all-features --message-format=json \
| criterion-table > crates/benchmarks/BENCHMARKS.md
bench-pvm: install-bin
cargo criterion --bench execute --features bench-pvm-interpreter --message-format=json \
| criterion-table > crates/benchmarks/PVM.md
@@ -43,19 +81,13 @@ bench-evm: install-bin
cargo criterion --bench execute --features bench-evm --message-format=json \
| criterion-table > crates/benchmarks/EVM.md
bench: install-bin
cargo criterion --all --all-features --message-format=json \
| criterion-table > crates/benchmarks/BENCHMARKS.md
docs: docs-build
mdbook serve --open docs/
docs-build:
mdbook test docs/ && mdbook build docs/
clean:
cargo clean ; \
revive-llvm clean ; \
rm -rf node_modules ; \
rm -rf crates/solidity/src/tests/cli-tests/artifacts ; \
cargo uninstall revive-solidity ; \
rm -f package-lock.json
cargo uninstall revive-llvm-builder ; \
rm -f package-lock.json ; \
rm -rf js/dist ; \
rm -f js/src/resolc.{wasm,js}
+53 -6
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,18 +17,53 @@ 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.
Download and install the `resolc` frontend executable for your platform from our [releases](https://github.com/paritytech/revive/releases).
## Building from source
Building revive requires a [stable Rust installation](https://rustup.rs/) and a C++ toolchain for building [LLVM](https://github.com/llvm/llvm-project) on your system.
### LLVM
`revive` depends on a custom build of LLVM `v18.1.8` with the RISC-V _embedded_ target, including the `compiler-rt` builtins. Use the provided [revive-llvm](crates/llvm-builder/README.md) utility to compile a compatible LLVM build locally and point `$LLVM_SYS_181_PREFIX` to the installation afterwards.
The `Makefile` provides a shortcut target to obtain a compatible LLVM build:
```bash
make install-llvm
export LLVM_SYS_181_PREFIX=${PWD}/target-llvm/gnu/target-final
```
### The `resolc` Solidity frontend
To build the `resolc` Solidity frontend executable, make sure you have obtained a compatible LLVM build using [revive-llvm](crates/llvm-builder/README.md) and did export the `LLVM_SYS_181_PREFIX` environment variable pointing to it (see [above](#LLVM)).
To install the `resolc` Solidity frontend executable:
```bash
bash build-llvm.sh
export PATH=${PWD}/llvm18.0/bin:$PATH
make install-bin
resolc --version
```
### LLVM
### Cross-compilation to Wasm
`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).
Cross-compile the `resolc.js` frontend executable to Wasm for running it in a Node.js or browser environment. The `REVIVE_LLVM_TARGET_PREFIX` environment variable is used to control the target environment LLVM dependency.
```bash
# Build the host LLVM dependency with PolkaVM target support
make install-llvm
export LLVM_SYS_181_PREFIX=${PWD}/target-llvm/gnu/target-final
# Build the target LLVM dependency with PolkaVM target support
revive-llvm --target-env emscripten clone
source emsdk/emsdk_env.sh
revive-llvm --target-env emscripten build --llvm-projects lld
export REVIVE_LLVM_TARGET_PREFIX=${PWD}/target-llvm/emscripten/target-final
# Build the resolc frontend executable
make install-wasm
make test-wasm
```
### Development
@@ -35,4 +71,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
```
+11
View File
@@ -0,0 +1,11 @@
# 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. Create a __pre-release__ from the tag and manually upload the `resolc` binary from docker image
4. Manually upload `resolc.js` and `resolc.wasm` from the `build-revive-wasm` action artifacts.
5. Update the [contract-docs](https://github.com/paritytech/contract-docs/) accordingly
+1
View File
@@ -0,0 +1 @@
https://github.com/paritytech/polkadot-sdk/blob/master/docs/contributor/SECURITY.md
+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.
-92
View File
@@ -1,92 +0,0 @@
#!/usr/bin/env bash
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
if [ ! -d ${LLVM_BUILD_DIR} ] ; then
mkdir -p ${LLVM_BUILD_DIR}
fi
cmake -G Ninja \
-S ${LLVM_SRC_DIR} \
-B ${LLVM_BUILD_DIR} \
-DLLVM_ENABLE_ASSERTIONS=On \
-DLLVM_ENABLE_TERMINFO=Off \
-DLLVM_ENABLE_LIBXML2=Off \
-DLLVM_ENABLE_ZLIB=Off \
-DLLVM_ENABLE_PROJECTS='clang;lld' \
-DLLVM_TARGETS_TO_BUILD='RISCV' \
-DLLVM_ENABLE_ZSTD=Off \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}
cmake --build ${LLVM_BUILD_DIR}
cmake --install ${LLVM_BUILD_DIR}
# Build compiler builtins
COMPILER_RT_SRC_DIR=${LLVM_SRC_PREFIX}/compiler-rt
COMPILER_RT_BUILD_DIR=${PWD}/build/compiler-rt
if [ ! -d ${COMPILER_RT_BUILD_DIR} ] ; then
mkdir -p ${COMPILER_RT_BUILD_DIR}
fi
build_compiler_rt() {
case "$1" in
64) TARGET_ABI=lp64e ;;
32) TARGET_ABI=ilp32e ;;
*) exit -1
esac
CFLAGS="--target=riscv${1} -march=rv${1}em -mabi=${TARGET_ABI} -mcpu=generic-rv${1} -nostdlib -nodefaultlibs"
cmake -G Ninja \
-S ${COMPILER_RT_SRC_DIR} \
-B ${COMPILER_RT_BUILD_DIR} \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} \
-DCOMPILER_RT_BUILD_BUILTINS=ON \
-DCOMPILER_RT_BUILD_LIBFUZZER=OFF \
-DCOMPILER_RT_BUILD_MEMPROF=OFF \
-DCOMPILER_RT_BUILD_PROFILE=OFF \
-DCOMPILER_RT_BUILD_SANITIZERS=OFF \
-DCOMPILER_RT_BUILD_XRAY=OFF \
-DCMAKE_C_COMPILER=${INSTALL_DIR}/bin/clang \
-DCMAKE_C_COMPILER_TARGET=riscv${1} \
-DCMAKE_ASM_COMPILER_TARGET=riscv${1} \
-DCMAKE_CXX_COMPILER_TARGET=riscv${1} \
-DCMAKE_C_TARGET_BITS=riscv${1} \
-DCMAKE_ASM_TARGET_BITS=riscv${1} \
-DCMAKE_AR=${INSTALL_DIR}/bin/llvm-ar \
-DCMAKE_NM=${INSTALL_DIR}/bin/llvm-nm \
-DCMAKE_RANLIB=${INSTALL_DIR}/bin/llvm-ranlib \
-DCOMPILER_RT_BAREMETAL_BUILD=ON \
-DLLVM_CONFIG_PATH=${INSTALL_DIR}/bin/llvm-config \
-DCMAKE_C_FLAGS="${CFLAGS}" \
-DCMAKE_ASM_FLAGS="${CFLAGS}" \
-DCOMPILER_RT_TEST_COMPILER=${INSTALL_DIR}/bin/clang \
-DCMAKE_CXX_FLAGS="${CFLAGS}" \
-DCMAKE_SYSTEM_NAME=unknown \
-DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON
cmake --build ${COMPILER_RT_BUILD_DIR}
cmake --install ${COMPILER_RT_BUILD_DIR}
}
build_compiler_rt 32
build_compiler_rt 64
echo ""
echo "success"
echo "add this directory to your PATH: ${INSTALL_DIR}/bin/"
+24 -24
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**) |
|:-------------|:-------------------------|:-------------------------------- |
| **`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**) |
|:-------------|:-------------------------|:-------------------------------- |
| **`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*) |
|:----------|:-------------------------|:-------------------------------- |
| **`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*) |
|:----------|:-------------------------|:-------------------------------- |
| **`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) {
+14
View File
@@ -0,0 +1,14 @@
# Generated by Cargo
# will have compiled files and executables
/target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# IDE
/.idea/
/.vscode/
+15
View File
@@ -0,0 +1,15 @@
[package]
name = "revive-build-utils"
version.workspace = true
license.workspace = true
edition.workspace = true
repository.workspace = true
authors = [
"Cyrill Leutwiler <cyrill@parity.io>",
]
description = "Shared build utilities of the revive compiler"
[lib]
doctest = false
[dependencies]
+10
View File
@@ -0,0 +1,10 @@
# revive: Compiler Common
## License
This library is distributed under the terms of either
- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
at your option.
+48
View File
@@ -0,0 +1,48 @@
//! The compiler build utilities library.
/// The revive LLVM host dependency directory prefix environment variable.
pub const REVIVE_LLVM_HOST_PREFIX: &str = "LLVM_SYS_181_PREFIX";
/// The revive LLVM target dependency directory prefix environment variable.
pub const REVIVE_LLVM_TARGET_PREFIX: &str = "REVIVE_LLVM_TARGET_PREFIX";
/// Constructs a path to the LLVM tool `name`.
///
/// Respects the [`REVIVE_LLVM_HOST_PREFIX`] environment variable.
pub fn llvm_host_tool(name: &str) -> std::path::PathBuf {
std::env::var_os(REVIVE_LLVM_HOST_PREFIX)
.map(Into::<std::path::PathBuf>::into)
.unwrap_or_else(|| {
panic!(
"install LLVM using the revive-llvm builder and export {REVIVE_LLVM_HOST_PREFIX}",
)
})
.join("bin")
.join(name)
}
/// Returns the LLVM lib dir.
///
/// Respects the [`REVIVE_LLVM_HOST_PREFIX`] environment variable.
pub fn llvm_lib_dir() -> std::path::PathBuf {
std::path::PathBuf::from(llvm_config("--libdir").trim())
}
/// Returns the LLVM CXX compiler flags.
///
/// Respects the [`REVIVE_LLVM_HOST_PREFIX`] environment variable.
pub fn llvm_cxx_flags() -> String {
llvm_config("--cxxflags")
}
/// Execute the `llvm-config` utility respecting the [`REVIVE_LLVM_HOST_PREFIX`] environment variable.
fn llvm_config(arg: &str) -> String {
let llvm_config = llvm_host_tool("llvm-config");
let output = std::process::Command::new(&llvm_config)
.arg(arg)
.output()
.unwrap_or_else(|error| panic!("`{} {arg}` failed: {error}", llvm_config.display()));
String::from_utf8(output.stdout)
.unwrap_or_else(|_| panic!("output of `{} {arg}` should be utf8", llvm_config.display()))
}
+2 -4
View File
@@ -8,7 +8,5 @@ authors.workspace = true
build = "build.rs"
description = "compiler builtins for the revive compiler"
[features]
riscv-64 = []
[dependencies]
[build-dependencies]
revive-build-utils = { workspace = true }
+18 -14
View File
@@ -1,30 +1,34 @@
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() {
let mut llvm_lib_dir = String::new();
println!(
"cargo:rerun-if-env-changed={}",
revive_build_utils::REVIVE_LLVM_HOST_PREFIX
);
Command::new("llvm-config")
.args(["--libdir"])
let llvm_config = revive_build_utils::llvm_host_tool("llvm-config");
let mut llvm_lib_dir = String::new();
Command::new(&llvm_config)
.arg("--libdir")
.output()
.expect("llvm-config should be able to provide LD path")
.unwrap_or_else(|_| {
panic!(
"{} should be able to provide LD path",
llvm_config.display()
)
})
.stdout
.as_slice()
.read_to_string(&mut llvm_lib_dir)
.expect("llvm-config output should be utf8");
let mut lib_path = std::path::PathBuf::from(llvm_lib_dir.trim())
.join("linux")
let lib_path = revive_build_utils::llvm_lib_dir()
.join("unknown")
.join(BUILTINS_ARCHIVE_FILE);
if !lib_path.exists() {
lib_path = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap())
.join(BUILTINS_ARCHIVE_FILE);
}
let archive = fs::read(lib_path).expect("clang builtins not found");
let archive = fs::read(&lib_path).expect("clang builtins not found");
println!("cargo:rerun-if-env-changed={}", lib_path.display());
let out_dir = env::var_os("OUT_DIR").expect("has OUT_DIR");
let archive_path = Path::new(&out_dir).join(BUILTINS_ARCHIVE_FILE);
Binary file not shown.
Binary file not shown.
-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";
+1
View File
@@ -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())
-6
View File
@@ -8,18 +8,12 @@ authors.workspace = true
description = "revive compiler integration test cases"
[dependencies]
polkavm = { workspace = true }
alloy-primitives = { workspace = true }
alloy-sol-types = { workspace = true }
hex = { workspace = true }
env_logger = { workspace = true }
log = { workspace = true }
serde_json = { workspace = true }
revive-solidity = { workspace = true }
revive-differential = { workspace = true }
revive-llvm-context = { workspace = true }
revive-common = { workspace = true }
revive-runner = { workspace = true }
[dev-dependencies]
+8 -8
View File
@@ -1,10 +1,10 @@
{
"Baseline": 967,
"Computation": 4022,
"DivisionArithmetics": 31787,
"ERC20": 44233,
"Events": 1743,
"FibonacciIterative": 2927,
"Flipper": 3408,
"SHA1": 26009
"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
);
}
}
+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);
}
}
+2 -4
View File
@@ -105,9 +105,7 @@ contract ERC20Tester {
ERC20 token = new ERC20();
assert(token.decimals() == 18);
// use call directly when code_size is implemented on pallet-revive
address(token).call(abi.encodeWithSignature("mint(uint256)", 300));
token.mint(300);
assert(token.balanceOf(address(this)) == 300);
token.transfer(BOB, 100);
assert(token.balanceOf(address(this)) == 200);
@@ -119,7 +117,7 @@ contract ERC20Tester {
assert(token.balanceOf(BOB) == 200);
assert(token.balanceOf(address(this)) == 100);
address(token).call(abi.encodeWithSignature("burn(uint256)", 100));
token.burn(100);
assert(token.balanceOf(address(this)) == 0);
}
}
+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 == 1000);
}
}
+114 -24
View File
@@ -44,6 +44,12 @@ 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 {
@@ -321,6 +327,114 @@ fn ext_code_hash() {
.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();
}
/*
// These test were implement for the mock-runtime and need to be ported yet.
@@ -349,28 +463,4 @@ fn create2_failure() {
assert_eq!(output.flags, ReturnFlags::Revert);
}
#[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);
}
*/
-5
View File
@@ -7,14 +7,9 @@ repository.workspace = true
authors.workspace = true
description = "revive compiler linker utils"
[features]
riscv-64 = []
[dependencies]
inkwell = { workspace = true }
tempfile = { workspace = true }
polkavm-linker = { workspace = true }
polkavm-common = { workspace = true }
libc = { workspace = true }
anyhow = { 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)?)
}
+1
View File
@@ -13,3 +13,4 @@ libc = { workspace = true }
[build-dependencies]
cc = { workspace = true }
revive-build-utils = { workspace = true }
+74 -15
View File
@@ -1,17 +1,16 @@
fn llvm_config(arg: &str) -> String {
let output = std::process::Command::new("llvm-config")
.args([arg])
.output()
.unwrap_or_else(|_| panic!("`llvm-config {arg}` failed"));
String::from_utf8(output.stdout)
.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"));
let llvm_lib_path = match std::env::var(revive_build_utils::REVIVE_LLVM_TARGET_PREFIX) {
Ok(path) => std::path::PathBuf::from(path).join("lib"),
_ => revive_build_utils::llvm_lib_dir(),
};
println!(
"cargo:rustc-link-search=native={}",
llvm_lib_path.to_string_lossy()
);
for lib in [
// These are required by ld.lld
"lldELF",
"lldCommon",
"lldMachO",
@@ -22,19 +21,79 @@ 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();
let target_env = std::env::var("CARGO_CFG_TARGET_ENV").unwrap_or_default();
if target_os == "linux" {
if target_env == "musl" {
println!("cargo:rustc-link-lib=static=c++");
} else {
println!("cargo:rustc-link-lib=dylib=stdc++");
println!("cargo:rustc-link-lib=tinfo");
}
}
}
fn main() {
llvm_config("--cxxflags")
println!(
"cargo:rerun-if-env-changed={}",
revive_build_utils::REVIVE_LLVM_HOST_PREFIX
);
println!(
"cargo:rerun-if-env-changed={}",
revive_build_utils::REVIVE_LLVM_TARGET_PREFIX
);
revive_build_utils::llvm_cxx_flags()
.split_whitespace()
.fold(&mut cc::Build::new(), |builder, flag| builder.flag(flag))
.flag("-Wno-unused-parameter")
+38
View File
@@ -0,0 +1,38 @@
[package]
name = "revive-llvm-builder"
description = "revive LLVM compiler framework builder"
authors = [
"Oleksandr Zarudnyi <a.zarudnyy@matterlabs.dev>",
"Anton Baliasnikov <aba@matterlabs.dev>",
"Cyrill Leutwiler <cyrill@parity.io>",
]
version.workspace = true
license.workspace = true
edition.workspace = true
repository.workspace = true
[[bin]]
name = "revive-llvm"
path = "src/revive_llvm/main.rs"
[lib]
doctest = false
[dependencies]
clap = { workspace = true, features = ["help", "std", "derive"] }
anyhow = { workspace = true }
serde = { workspace = true, features = [ "derive" ] }
toml = { workspace = true }
num_cpus = { workspace = true }
fs_extra = { workspace = true }
path-slash = { workspace = true }
regex = { workspace = true }
downloader = { workspace = true }
tar = { workspace = true }
flate2 = { workspace = true }
env_logger = { workspace = true }
log = { workspace = true }
[dev-dependencies]
assert_cmd = { workspace = true }
assert_fs = { workspace = true }
+132
View File
@@ -0,0 +1,132 @@
# revive LLVM builder
Parity fork of the [Matter Labs zksync LLVM builder](https://github.com/matter-labs/era-compiler-llvm-builder) helper utility for compiling [revive](https://github.com/paritytech/revive) compatible LLVM builds.
## Installation and usage
The LLVM compiler framework for revive must be built with our tool called `revive-llvm`.
This is because the revive compiler has requirements not fullfilled in upstream builds:
- Special builds for compiling the frontend into statically linked ELF binaries and also Wasm executables
- The RISC-V target (the PolkaVM target)
- The compiler-rt builtins for the PolkaVM target
- We want to leave the assertions always on
- Various other specific configurations and optimization may be applied
Obtain a compatible build for your host platform from the release section of this repository (TODO). Alternatively follow below steps to get a custom build:
<details>
<summary>1. Install the system prerequisites.</summary>
* Linux (Debian):
Install the following packages:
```shell
apt install cmake ninja-build curl git libssl-dev pkg-config clang lld
```
* Linux (Arch):
Install the following packages:
```shell
pacman -Syu which cmake ninja curl git pkg-config clang lld
```
* MacOS:
* Install the [HomeBrew](https://brew.sh) package manager.
* Install the following packages:
```shell
brew install cmake ninja coreutils
```
* Install your choice of a recent LLVM/[Clang](https://clang.llvm.org) compiler, e.g. via [Xcode](https://developer.apple.com/xcode/), [Apples Command Line Tools](https://developer.apple.com/library/archive/technotes/tn2339/_index.html), or your preferred package manager.
</details>
<details>
<summary>2. Install Rust.</summary>
* Follow the latest [official instructions](https://www.rust-lang.org/tools/install:
```shell
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
. ${HOME}/.cargo/env
```
> Currently we are not pinned to any specific version of Rust, so just install the latest stable build for your platform.
</details>
<details>
<summary>3. Install the revive LLVM framework builder.</summary>
* Install the builder using `cargo`:
```shell
cargo install --git https://github.com/paritytech/revive-llvm-builder --force --locked
```
> The builder is not the LLVM framework itself, but a tool that clones its repository and runs a sequence of build commands. By default it is installed in `~/.cargo/bin/`, which is recommended to be added to your `$PATH`.
</details>
<details>
<summary>4. (Optional) Create the `LLVM.lock` file.</summary>
* The `LLVM.lock` dictates the LLVM source tree being used.
A default `./LLVM.lock` pointing to the release used for development is already provided.
</details>
<details>
<summary>5. Build LLVM.</summary>
* Clone and build the LLVM framework using the `revive-llvm` tool.
The clang and lld projects are required for the `resolc` Solidity frontend executable; they are enabled by default. LLVM assertions are also enabled by default.
```shell
revive-llvm clone
revive-llvm build --llvm-projects lld --llvm-projects clang
```
Build artifacts end up in the `./target-llvm/gnu/target-final/` directory by default.
The `gnu` directory depends on the supported archticture and will either be `gnu`, `musl` or `emscripten`.
You now need to export the final target directory `$LLVM_SYS_181_PREFIX`: `export LLVM_SYS_181_PREFIX=${PWD}/target-llvm/gnu/target-final`
If built with the `--enable-tests` option, test tools will be in the `./target-llvm/gnu/build-final/` directory, along with copies of the build artifacts. For all supported build options, run `revive-llvm build --help`.
</details>
## Supported target architectures
The following target platforms are supported:
- Linux GNU (x86)
- Linux MUSL (x86)
- MacOS (aarch64)
- Windows GNU (x86)
- Emscripten (wasm32)
<details>
<summary>Building for MUSL</summary>
* Via a musl build we can build revive into fully static ELF binaries.
Which is desirable for reproducible Solidity contracts builds.
The resulting binary is also very portable, akin to the`solc` frontend binary distribution.
Clone and build the LLVM framework using the `revive-llvm` tool:
```shell
revive-llvm --target-env musl clone
revive-llvm --target-env musl build --enable-assertions --llvm-projects clang --llvm-projects lld
```
</details>
<details>
<summary>Building for Emscripten</summary>
* Via an emsdk build we can run revive in the browser and on node.js.
Clone and build the LLVM framework using the `revive-llvm` tool:
```shell
revive-llvm --target-env emscripten clone
revive-llvm --target-env emscripten build --enable-assertions --llvm-projects clang --llvm-projects lld
```
</details>
+39
View File
@@ -0,0 +1,39 @@
//! The revive LLVM build type.
/// The revive LLVM build type.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BuildType {
/// The debug build.
Debug,
/// The release build.
Release,
/// The release with debug info build.
RelWithDebInfo,
/// The minimal size release build.
MinSizeRel,
}
impl std::str::FromStr for BuildType {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"Debug" => Ok(Self::Debug),
"Release" => Ok(Self::Release),
"RelWithDebInfo" => Ok(Self::RelWithDebInfo),
"MinSizeRel" => Ok(Self::MinSizeRel),
value => Err(format!("Unsupported build type: `{}`", value)),
}
}
}
impl std::fmt::Display for BuildType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Debug => write!(f, "Debug"),
Self::Release => write!(f, "Release"),
Self::RelWithDebInfo => write!(f, "RelWithDebInfo"),
Self::MinSizeRel => write!(f, "MinSizeRel"),
}
}
}
+137
View File
@@ -0,0 +1,137 @@
//! Utilities for compiling the LLVM compiler-rt builtins.
/// Static CFLAGS variable passed to the compiler building the compiler-rt builtins.
const C_FLAGS: [&str; 6] = [
"--target=riscv64",
"-march=rv64emac",
"-mabi=lp64e",
"-mcpu=generic-rv64",
"-nostdlib",
"-nodefaultlibs",
];
/// Static CMAKE arguments for building the compiler-rt builtins.
const CMAKE_STATIC_ARGS: [&str; 14] = [
"-DCOMPILER_RT_BUILD_BUILTINS='On'",
"-DCOMPILER_RT_BUILD_LIBFUZZER='Off'",
"-DCOMPILER_RT_BUILD_MEMPROF='Off'",
"-DCOMPILER_RT_BUILD_PROFILE='Off'",
"-DCOMPILER_RT_BUILD_SANITIZERS='Off'",
"-DCOMPILER_RT_BUILD_XRAY='Off'",
"-DCOMPILER_RT_DEFAULT_TARGET_ONLY='On'",
"-DCOMPILER_RT_BAREMETAL_BUILD='On'",
"-DCMAKE_BUILD_WITH_INSTALL_RPATH=1",
"-DCMAKE_EXPORT_COMPILE_COMMANDS='On'",
"-DCMAKE_SYSTEM_NAME='unknown'",
"-DCMAKE_C_COMPILER_TARGET='riscv64'",
"-DCMAKE_ASM_COMPILER_TARGET='riscv64'",
"-DCMAKE_CXX_COMPILER_TARGET='riscv64'",
];
/// Dynamic cmake arguments for building the compiler-rt builtins.
fn cmake_dynamic_args(
build_type: crate::BuildType,
target_env: crate::target_env::TargetEnv,
) -> anyhow::Result<[String; 13]> {
let llvm_compiler_rt_target = crate::LLVMPath::llvm_target_compiler_rt()?;
// The Emscripten target needs to use the host LLVM tools.
let llvm_target_host = if target_env == crate::target_env::TargetEnv::Emscripten {
crate::LLVMPath::llvm_build_host()?
} else {
crate::LLVMPath::llvm_target_final()?
};
let mut clang_path = llvm_target_host.to_path_buf();
clang_path.push("bin/clang");
let mut clangxx_path = llvm_target_host.to_path_buf();
clangxx_path.push("bin/clang++");
let mut llvm_config_path = llvm_target_host.to_path_buf();
llvm_config_path.push("bin/llvm-config");
let mut ar_path = llvm_target_host.to_path_buf();
ar_path.push("bin/llvm-ar");
let mut nm_path = llvm_target_host.to_path_buf();
nm_path.push("bin/llvm-nm");
let mut ranlib_path = llvm_target_host.to_path_buf();
ranlib_path.push("bin/llvm-ranlib");
let mut linker_path = llvm_target_host.to_path_buf();
linker_path.push("bin/ld.lld");
Ok([
format!(
"-DCMAKE_INSTALL_PREFIX='{}'",
llvm_compiler_rt_target.to_string_lossy().as_ref(),
),
format!("-DCMAKE_BUILD_TYPE='{build_type}'"),
format!(
"-DCOMPILER_RT_TEST_COMPILER='{}'",
clang_path.to_string_lossy()
),
format!("-DCMAKE_C_FLAGS='{}'", C_FLAGS.join(" ")),
format!("-DCMAKE_ASM_FLAGS='{}'", C_FLAGS.join(" ")),
format!("-DCMAKE_CXX_FLAGS='{}'", C_FLAGS.join(" ")),
format!("-DCMAKE_C_COMPILER='{}'", clang_path.to_string_lossy()),
format!("-DCMAKE_ASM_COMPILER='{}'", clang_path.to_string_lossy()),
format!("-DCMAKE_CXX_COMPILER='{}'", clangxx_path.to_string_lossy()),
format!("-DCMAKE_AR='{}'", ar_path.to_string_lossy()),
format!("-DCMAKE_NM='{}'", nm_path.to_string_lossy()),
format!("-DCMAKE_RANLIB='{}'", ranlib_path.to_string_lossy()),
format!(
"-DLLVM_CONFIG_PATH='{}'",
llvm_config_path.to_string_lossy()
),
])
}
/// Build the compiler-rt builtins library.
pub fn build(
build_type: crate::BuildType,
target_env: crate::target_env::TargetEnv,
default_target: Option<crate::TargetTriple>,
extra_args: &[String],
ccache_variant: Option<crate::ccache_variant::CcacheVariant>,
sanitizer: Option<crate::sanitizer::Sanitizer>,
) -> anyhow::Result<()> {
log::info!("building compiler-rt for rv64emac");
crate::utils::check_presence("cmake")?;
crate::utils::check_presence("ninja")?;
let llvm_module_compiler_rt = crate::LLVMPath::llvm_module_compiler_rt()?;
let llvm_compiler_rt_build = crate::LLVMPath::llvm_build_compiler_rt()?;
crate::utils::command(
std::process::Command::new("cmake")
.args([
"-S",
llvm_module_compiler_rt.to_string_lossy().as_ref(),
"-B",
llvm_compiler_rt_build.to_string_lossy().as_ref(),
"-G",
"Ninja",
])
.args(CMAKE_STATIC_ARGS)
.args(cmake_dynamic_args(build_type, target_env)?)
.args(extra_args)
.args(crate::platforms::shared::shared_build_opts_ccache(
ccache_variant,
))
.args(crate::platforms::shared::shared_build_opts_default_target(
default_target,
))
.args(crate::platforms::shared::shared_build_opts_sanitizers(
sanitizer,
)),
"LLVM compiler-rt building cmake",
)?;
crate::utils::ninja(&llvm_compiler_rt_build)?;
Ok(())
}
+31
View File
@@ -0,0 +1,31 @@
//! Compiler cache variants.
/// The list compiler cache variants to be used as constants.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CcacheVariant {
/// Standard ccache.
Ccache,
/// Mozilla's sccache.
Sccache,
}
impl std::str::FromStr for CcacheVariant {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"ccache" => Ok(Self::Ccache),
"sccache" => Ok(Self::Sccache),
value => Err(format!("Unsupported ccache variant: `{}`", value)),
}
}
}
impl std::fmt::Display for CcacheVariant {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Ccache => write!(f, "ccache"),
Self::Sccache => write!(f, "sccache"),
}
}
}
+340
View File
@@ -0,0 +1,340 @@
//! The revive LLVM builder library.
pub mod build_type;
pub mod builtins;
pub mod ccache_variant;
pub mod llvm_path;
pub mod llvm_project;
pub mod lock;
pub mod platforms;
pub mod sanitizer;
pub mod target_env;
pub mod target_triple;
pub mod utils;
pub use self::build_type::BuildType;
pub use self::llvm_path::LLVMPath;
pub use self::lock::Lock;
pub use self::platforms::Platform;
use std::collections::HashSet;
use std::path::{Path, PathBuf};
use std::process::Command;
pub use target_env::TargetEnv;
pub use target_triple::TargetTriple;
/// Executes the LLVM repository cloning.
pub fn clone(lock: Lock, deep: bool, target_env: TargetEnv) -> anyhow::Result<()> {
utils::check_presence("git")?;
if target_env == TargetEnv::Emscripten {
utils::install_emsdk()?;
}
let destination_path = PathBuf::from(LLVMPath::DIRECTORY_LLVM_SOURCE);
if destination_path.exists() {
log::warn!(
"LLVM repository directory {} already exists, falling back to checkout",
destination_path.display()
);
return checkout(lock, false);
}
let mut clone_args = vec!["clone", "--branch", lock.branch.as_str()];
if !deep {
clone_args.push("--depth");
clone_args.push("1");
}
utils::command(
Command::new("git")
.args(clone_args)
.arg(lock.url.as_str())
.arg(destination_path.to_string_lossy().as_ref()),
"LLVM repository cloning",
)?;
if let Some(r#ref) = lock.r#ref {
utils::command(
Command::new("git")
.args(["checkout", r#ref.as_str()])
.current_dir(destination_path.to_string_lossy().as_ref()),
"LLVM repository commit checking out",
)?;
}
Ok(())
}
/// Executes the checkout of the specified branch.
pub fn checkout(lock: Lock, force: bool) -> anyhow::Result<()> {
let destination_path = PathBuf::from(LLVMPath::DIRECTORY_LLVM_SOURCE);
utils::command(
Command::new("git")
.current_dir(destination_path.as_path())
.args(["fetch", "--all", "--tags"]),
"LLVM repository data fetching",
)?;
if force {
utils::command(
Command::new("git")
.current_dir(destination_path.as_path())
.args(["clean", "-d", "-x", "--force"]),
"LLVM repository cleaning",
)?;
}
utils::command(
Command::new("git")
.current_dir(destination_path.as_path())
.args(["checkout", "--force", lock.branch.as_str()]),
"LLVM repository data pulling",
)?;
if let Some(r#ref) = lock.r#ref {
let mut checkout_command = Command::new("git");
checkout_command.current_dir(destination_path.as_path());
checkout_command.arg("checkout");
if force {
checkout_command.arg("--force");
}
checkout_command.arg(r#ref);
utils::command(&mut checkout_command, "LLVM repository checking out")?;
}
Ok(())
}
/// Executes the building of the LLVM framework for the platform determined by the cfg macro.
/// Since cfg is evaluated at compile time, overriding the platform with a command-line
/// argument is not possible. So for cross-platform testing, comment out all but the
/// line to be tested, and perhaps also checks in the platform-specific build method.
#[allow(clippy::too_many_arguments)]
pub fn build(
build_type: BuildType,
target_env: TargetEnv,
targets: HashSet<Platform>,
llvm_projects: HashSet<llvm_project::LLVMProject>,
enable_rtti: bool,
default_target: Option<TargetTriple>,
enable_tests: bool,
enable_coverage: bool,
extra_args: &[String],
ccache_variant: Option<ccache_variant::CcacheVariant>,
enable_assertions: bool,
sanitizer: Option<sanitizer::Sanitizer>,
enable_valgrind: bool,
) -> anyhow::Result<()> {
log::trace!("build type: {:?}", build_type);
log::trace!("target env: {:?}", target_env);
log::trace!("targets: {:?}", targets);
log::trace!("llvm projects: {:?}", llvm_projects);
log::trace!("enable rtti: {:?}", enable_rtti);
log::trace!("default target: {:?}", default_target);
log::trace!("eneable tests: {:?}", enable_tests);
log::trace!("enable_coverage: {:?}", enable_coverage);
log::trace!("extra args: {:?}", extra_args);
log::trace!("sanitzer: {:?}", sanitizer);
log::trace!("enable valgrind: {:?}", enable_valgrind);
if !PathBuf::from(LLVMPath::DIRECTORY_LLVM_SOURCE).exists() {
log::error!(
"LLVM project source directory {} does not exist (run `revive-llvm --target-env {} clone`)",
LLVMPath::DIRECTORY_LLVM_SOURCE,
target_env
)
}
std::fs::create_dir_all(llvm_path::DIRECTORY_LLVM_TARGET.get().unwrap())?;
if cfg!(target_arch = "x86_64") {
if cfg!(target_os = "linux") {
if target_env == TargetEnv::MUSL {
platforms::x86_64_linux_musl::build(
build_type,
targets,
llvm_projects,
enable_rtti,
default_target,
enable_tests,
enable_coverage,
extra_args,
ccache_variant,
enable_assertions,
sanitizer,
enable_valgrind,
)?;
} else if target_env == TargetEnv::GNU {
platforms::x86_64_linux_gnu::build(
build_type,
targets,
llvm_projects,
enable_rtti,
default_target,
enable_tests,
enable_coverage,
extra_args,
ccache_variant,
enable_assertions,
sanitizer,
enable_valgrind,
)?;
} else if target_env == TargetEnv::Emscripten {
platforms::wasm32_emscripten::build(
build_type,
targets,
llvm_projects,
enable_rtti,
default_target,
enable_tests,
enable_coverage,
extra_args,
ccache_variant,
enable_assertions,
sanitizer,
enable_valgrind,
)?;
} else {
anyhow::bail!("Unsupported target environment for x86_64 and Linux");
}
} else if cfg!(target_os = "macos") {
platforms::x86_64_macos::build(
build_type,
targets,
llvm_projects,
enable_rtti,
default_target,
enable_tests,
enable_coverage,
extra_args,
ccache_variant,
enable_assertions,
sanitizer,
)?;
} else if cfg!(target_os = "windows") {
platforms::x86_64_windows_gnu::build(
build_type,
targets,
llvm_projects,
enable_rtti,
default_target,
enable_tests,
enable_coverage,
extra_args,
ccache_variant,
enable_assertions,
sanitizer,
)?;
} else {
anyhow::bail!("Unsupported target OS for x86_64");
}
} else if cfg!(target_arch = "aarch64") {
if cfg!(target_os = "linux") {
if target_env == TargetEnv::MUSL {
platforms::aarch64_linux_musl::build(
build_type,
targets,
llvm_projects,
enable_rtti,
default_target,
enable_tests,
enable_coverage,
extra_args,
ccache_variant,
enable_assertions,
sanitizer,
enable_valgrind,
)?;
} else if target_env == TargetEnv::GNU {
platforms::aarch64_linux_gnu::build(
build_type,
targets,
llvm_projects,
enable_rtti,
default_target,
enable_tests,
enable_coverage,
extra_args,
ccache_variant,
enable_assertions,
sanitizer,
enable_valgrind,
)?;
} else {
anyhow::bail!("Unsupported target environment for aarch64 and Linux");
}
} else if cfg!(target_os = "macos") {
if target_env == TargetEnv::Emscripten {
platforms::wasm32_emscripten::build(
build_type,
targets,
llvm_projects,
enable_rtti,
default_target,
enable_tests,
enable_coverage,
extra_args,
ccache_variant,
enable_assertions,
sanitizer,
enable_valgrind,
)?;
} else {
platforms::aarch64_macos::build(
build_type,
targets,
llvm_projects,
enable_rtti,
default_target,
enable_tests,
enable_coverage,
extra_args,
ccache_variant,
enable_assertions,
sanitizer,
)?;
}
} else {
anyhow::bail!("Unsupported target OS for aarch64");
}
} else {
anyhow::bail!("Unsupported target architecture");
}
crate::builtins::build(
build_type,
target_env,
default_target,
extra_args,
ccache_variant,
sanitizer,
)?;
Ok(())
}
/// Executes the build artifacts cleaning.
pub fn clean() -> anyhow::Result<()> {
let remove_if_exists = |path: &Path| {
if !path.exists() {
return Ok(());
}
log::info!("deleting {}", path.display());
std::fs::remove_dir_all(path)
};
remove_if_exists(
llvm_path::DIRECTORY_LLVM_TARGET
.get()
.expect("target_env is always set because of the default value")
.parent()
.expect("target_env parent directory is target-llvm"),
)?;
remove_if_exists(&PathBuf::from(LLVMPath::DIRECTORY_EMSDK_SOURCE))?;
remove_if_exists(&PathBuf::from(LLVMPath::DIRECTORY_LLVM_SOURCE))?;
remove_if_exists(&PathBuf::from(LLVMPath::DIRECTORY_LLVM_HOST_SOURCE))?;
Ok(())
}
+151
View File
@@ -0,0 +1,151 @@
//! The revive LLVM builder constants.
use std::path::PathBuf;
use std::sync::OnceLock;
pub static DIRECTORY_LLVM_TARGET: OnceLock<PathBuf> = OnceLock::new();
/// The LLVM path resolver.
pub struct LLVMPath {}
impl LLVMPath {
/// The LLVM source directory.
pub const DIRECTORY_LLVM_SOURCE: &'static str = "./llvm/";
/// The LLVM host source directory for stage 1 of multistage MUSL and Emscripten builds.
///
/// We use upstream LLVM anyways; re-use the same tree for host and target builds.
pub const DIRECTORY_LLVM_HOST_SOURCE: &'static str = Self::DIRECTORY_LLVM_SOURCE;
/// The Emscripten SDK source directory.
pub const DIRECTORY_EMSDK_SOURCE: &'static str = "./emsdk/";
/// Returns the path to the `llvm` stage 1 host LLVM source module directory.
pub fn llvm_host_module_llvm() -> anyhow::Result<PathBuf> {
let mut path = PathBuf::from(Self::DIRECTORY_LLVM_HOST_SOURCE);
path.push("llvm");
crate::utils::absolute_path(path).inspect(|absolute_path| {
log::debug!(
"llvm stage 1 host llvm source module: {}",
absolute_path.display()
)
})
}
/// Returns the path to the `llvm` LLVM source module directory.
pub fn llvm_module_llvm() -> anyhow::Result<PathBuf> {
let mut path = PathBuf::from(Self::DIRECTORY_LLVM_SOURCE);
path.push("llvm");
crate::utils::absolute_path(path)
.inspect(|absolute_path| log::debug!("llvm source module: {}", absolute_path.display()))
}
/// Returns the path to the MUSL source.
pub fn musl_source(name: &str) -> anyhow::Result<PathBuf> {
let mut path = PathBuf::from(DIRECTORY_LLVM_TARGET.get().unwrap());
path.push(name);
crate::utils::absolute_path(path)
.inspect(|absolute_path| log::debug!("musl source: {}", absolute_path.display()))
}
/// Returns the path to the MUSL build directory.
pub fn musl_build(source_directory: &str) -> anyhow::Result<PathBuf> {
let mut path = PathBuf::from(DIRECTORY_LLVM_TARGET.get().unwrap());
path.push(source_directory);
path.push("build");
crate::utils::absolute_path(path)
.inspect(|absolute_path| log::debug!("musl build: '{}'", absolute_path.display()))
}
/// Returns the path to the LLVM CRT build directory.
pub fn llvm_build_crt() -> anyhow::Result<PathBuf> {
let mut path = PathBuf::from(DIRECTORY_LLVM_TARGET.get().unwrap());
path.push("build-crt");
crate::utils::absolute_path(path)
.inspect(|absolute_path| log::debug!("llvm build crt: {}", absolute_path.display()))
}
/// Returns the path to the LLVM host build directory.
pub fn llvm_build_host() -> anyhow::Result<PathBuf> {
let mut path = PathBuf::from(DIRECTORY_LLVM_TARGET.get().unwrap());
path.push("build-host");
crate::utils::absolute_path(path)
.inspect(|absolute_path| log::debug!("llvm build host: {}", absolute_path.display()))
}
/// Returns the path to the LLVM final build directory.
pub fn llvm_build_final() -> anyhow::Result<PathBuf> {
let mut path = PathBuf::from(DIRECTORY_LLVM_TARGET.get().unwrap());
path.push("build-final");
crate::utils::absolute_path(path)
.inspect(|absolute_path| log::debug!("llvm build final: {}", absolute_path.display()))
}
/// Returns the path to the MUSL target directory.
pub fn musl_target() -> anyhow::Result<PathBuf> {
let mut path = PathBuf::from(DIRECTORY_LLVM_TARGET.get().unwrap());
path.push("target-musl");
crate::utils::absolute_path(path)
.inspect(|absolute_path| log::debug!("musl target: {}", absolute_path.display()))
}
/// Returns the path to the LLVM CRT target directory.
pub fn llvm_target_crt() -> anyhow::Result<PathBuf> {
let mut path = PathBuf::from(DIRECTORY_LLVM_TARGET.get().unwrap());
path.push("target-crt");
crate::utils::absolute_path(path)
.inspect(|absolute_path| log::debug!("llvm crt target: {}", absolute_path.display()))
}
/// Returns the path to the LLVM host target directory.
pub fn llvm_target_host() -> anyhow::Result<PathBuf> {
let mut path = PathBuf::from(DIRECTORY_LLVM_TARGET.get().unwrap());
path.push("target-host");
crate::utils::absolute_path(path)
.inspect(|absolute_path| log::debug!("llvm host target: {}", absolute_path.display()))
}
/// Returns the path to the LLVM final target directory.
pub fn llvm_target_final() -> anyhow::Result<PathBuf> {
let mut path = PathBuf::from(DIRECTORY_LLVM_TARGET.get().unwrap());
path.push("target-final");
crate::utils::absolute_path(path)
.inspect(|absolute_path| log::debug!("llvm final target: {}", absolute_path.display()))
}
/// Returns the path to the LLVM compiler builtin target directory.
pub fn llvm_module_compiler_rt() -> anyhow::Result<PathBuf> {
let mut path = PathBuf::from(Self::DIRECTORY_LLVM_SOURCE);
path.push("compiler-rt");
crate::utils::absolute_path(path).inspect(|absolute_path| {
log::debug!("compiler-rt source dir: {}", absolute_path.display())
})
}
/// Returns the path to the LLVM compiler-rt target directory.
pub fn llvm_target_compiler_rt() -> anyhow::Result<PathBuf> {
Self::llvm_target_final()
}
/// Returns the path to the LLVM compiler-rt build directory.
pub fn llvm_build_compiler_rt() -> anyhow::Result<PathBuf> {
let mut path = PathBuf::from(DIRECTORY_LLVM_TARGET.get().unwrap());
path.push("build-compiler-rt");
crate::utils::absolute_path(path).inspect(|absolute_path| {
log::debug!("llvm compiler-rt build: {}", absolute_path.display())
})
}
/// Returns the path to the LLVM target final bin path.
///
pub fn llvm_target_final_bin(
target_env: crate::target_env::TargetEnv,
) -> anyhow::Result<PathBuf> {
let mut path = Self::llvm_target_final()?;
path.push("bin");
path.push(format!("{target_env}"));
crate::utils::absolute_path(path).inspect(|absolute_path| {
log::debug!("llvm target final bin: {}", absolute_path.display())
})
}
}
+39
View File
@@ -0,0 +1,39 @@
//! The LLVM projects to enable during the build.
/// The list of LLVM projects used as constants.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LLVMProject {
/// The Clang compiler.
CLANG,
/// LLD, the LLVM linker.
LLD,
/// The LLVM debugger.
LLDB,
/// The MLIR compiler.
MLIR,
}
impl std::str::FromStr for LLVMProject {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value.to_lowercase().as_str() {
"clang" => Ok(Self::CLANG),
"lld" => Ok(Self::LLD),
"lldb" => Ok(Self::LLDB),
"mlir" => Ok(Self::MLIR),
value => Err(format!("Unsupported LLVM project to enable: `{}`", value)),
}
}
}
impl std::fmt::Display for LLVMProject {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::CLANG => write!(f, "clang"),
Self::LLD => write!(f, "lld"),
Self::LLDB => write!(f, "lldb"),
Self::MLIR => write!(f, "mlir"),
}
}
}
+37
View File
@@ -0,0 +1,37 @@
//! The revive LLVM builder lock file.
use anyhow::Context;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use serde::Deserialize;
use serde::Serialize;
/// The default lock file location.
pub const LLVM_LOCK_DEFAULT_PATH: &str = "LLVM.lock";
/// The lock file data.
///
/// This file describes the exact reference of the LLVM framework.
#[derive(Debug, Deserialize, Serialize)]
pub struct Lock {
/// The LLVM repository URL.
pub url: String,
/// The LLVM repository branch.
pub branch: String,
/// The LLVM repository commit reference.
pub r#ref: Option<String>,
}
impl TryFrom<&PathBuf> for Lock {
type Error = anyhow::Error;
fn try_from(path: &PathBuf) -> Result<Self, Self::Error> {
let mut config_str = String::new();
let mut config_file =
File::open(path).with_context(|| format!("Error opening {:?} file", path))?;
config_file.read_to_string(&mut config_str)?;
Ok(toml::from_str(&config_str)?)
}
}
@@ -0,0 +1,111 @@
//! The revive LLVM arm64 `linux-gnu` builder.
use std::collections::HashSet;
use std::process::Command;
use crate::build_type::BuildType;
use crate::ccache_variant::CcacheVariant;
use crate::llvm_path::LLVMPath;
use crate::llvm_project::LLVMProject;
use crate::platforms::Platform;
use crate::sanitizer::Sanitizer;
use crate::target_triple::TargetTriple;
/// The building sequence.
#[allow(clippy::too_many_arguments)]
pub fn build(
build_type: BuildType,
targets: HashSet<Platform>,
llvm_projects: HashSet<LLVMProject>,
enable_rtti: bool,
default_target: Option<TargetTriple>,
enable_tests: bool,
enable_coverage: bool,
extra_args: &[String],
ccache_variant: Option<CcacheVariant>,
enable_assertions: bool,
sanitizer: Option<Sanitizer>,
enable_valgrind: bool,
) -> anyhow::Result<()> {
crate::utils::check_presence("cmake")?;
crate::utils::check_presence("clang")?;
crate::utils::check_presence("clang++")?;
crate::utils::check_presence("lld")?;
crate::utils::check_presence("ninja")?;
let llvm_module_llvm = LLVMPath::llvm_module_llvm()?;
let llvm_build_final = LLVMPath::llvm_build_final()?;
let llvm_target_final = LLVMPath::llvm_target_final()?;
crate::utils::command(
Command::new("cmake")
.args([
"-S",
llvm_module_llvm.to_string_lossy().as_ref(),
"-B",
llvm_build_final.to_string_lossy().as_ref(),
"-G",
"Ninja",
format!(
"-DCMAKE_INSTALL_PREFIX='{}'",
llvm_target_final.to_string_lossy().as_ref(),
)
.as_str(),
format!("-DCMAKE_BUILD_TYPE='{build_type}'").as_str(),
"-DCMAKE_C_COMPILER='clang'",
"-DCMAKE_CXX_COMPILER='clang++'",
format!(
"-DLLVM_TARGETS_TO_BUILD='{}'",
targets
.into_iter()
.map(|platform| platform.to_string())
.collect::<Vec<String>>()
.join(";")
)
.as_str(),
format!(
"-DLLVM_ENABLE_PROJECTS='{}'",
llvm_projects
.into_iter()
.map(|project| project.to_string())
.collect::<Vec<String>>()
.join(";")
)
.as_str(),
"-DLLVM_USE_LINKER='lld'",
])
.args(crate::platforms::shared::shared_build_opts_default_target(
default_target,
))
.args(crate::platforms::shared::shared_build_opts_tests(
enable_tests,
))
.args(crate::platforms::shared::shared_build_opts_coverage(
enable_coverage,
))
.args(crate::platforms::shared::SHARED_BUILD_OPTS)
.args(crate::platforms::shared::SHARED_BUILD_OPTS_NOT_MUSL)
.args(crate::platforms::shared::shared_build_opts_werror(
crate::target_env::TargetEnv::GNU,
))
.args(extra_args)
.args(crate::platforms::shared::shared_build_opts_ccache(
ccache_variant,
))
.args(crate::platforms::shared::shared_build_opts_assertions(
enable_assertions,
))
.args(crate::platforms::shared::shared_build_opts_rtti(
enable_rtti,
))
.args(crate::platforms::shared::shared_build_opts_sanitizers(
sanitizer,
))
.args(crate::platforms::shared::shared_build_opts_valgrind(
enable_valgrind,
)),
"LLVM building cmake",
)?;
crate::utils::ninja(llvm_build_final.as_ref())?;
Ok(())
}
@@ -0,0 +1,394 @@
//! The revive LLVM arm64 `linux-musl` builder.
use crate::build_type::BuildType;
use crate::ccache_variant::CcacheVariant;
use crate::llvm_path::LLVMPath;
use crate::llvm_project::LLVMProject;
use crate::platforms::Platform;
use crate::sanitizer::Sanitizer;
use crate::target_triple::TargetTriple;
use std::collections::HashSet;
use std::path::Path;
use std::process::Command;
/// The building sequence.
#[allow(clippy::too_many_arguments)]
pub fn build(
build_type: BuildType,
targets: HashSet<Platform>,
llvm_projects: HashSet<LLVMProject>,
enable_rtti: bool,
default_target: Option<TargetTriple>,
enable_tests: bool,
enable_coverage: bool,
extra_args: &[String],
ccache_variant: Option<CcacheVariant>,
enable_assertions: bool,
sanitizer: Option<Sanitizer>,
enable_valgrind: bool,
) -> anyhow::Result<()> {
crate::utils::check_presence("cmake")?;
crate::utils::check_presence("clang")?;
crate::utils::check_presence("clang++")?;
crate::utils::check_presence("lld")?;
crate::utils::check_presence("ninja")?;
let musl_name = "musl-1.2.3";
let musl_build = LLVMPath::musl_build(musl_name)?;
let musl_target = LLVMPath::musl_target()?;
let llvm_module_llvm = LLVMPath::llvm_module_llvm()?;
let llvm_host_module_llvm = LLVMPath::llvm_host_module_llvm()?;
let llvm_build_crt = LLVMPath::llvm_build_crt()?;
let llvm_target_crt = LLVMPath::llvm_target_crt()?;
let llvm_build_host = LLVMPath::llvm_build_host()?;
let llvm_target_host = LLVMPath::llvm_target_host()?;
let llvm_build_final = LLVMPath::llvm_build_final()?;
let llvm_target_final = LLVMPath::llvm_target_final()?;
if !LLVMPath::musl_source(musl_name)?.exists() {
crate::utils::download_musl(musl_name)?;
}
crate::platforms::shared::build_musl(musl_build.as_path(), musl_target.as_path())?;
build_crt(
targets.clone(),
llvm_host_module_llvm.as_path(),
llvm_build_crt.as_path(),
llvm_target_crt.as_path(),
ccache_variant,
)?;
build_host(
llvm_host_module_llvm.as_path(),
llvm_build_host.as_path(),
llvm_target_host.as_path(),
musl_target.as_path(),
llvm_target_crt.as_path(),
ccache_variant,
)?;
build_target(
build_type,
targets,
llvm_projects,
enable_rtti,
default_target,
llvm_module_llvm.as_path(),
llvm_build_final.as_path(),
llvm_target_final.as_path(),
musl_target.as_path(),
llvm_target_host.as_path(),
enable_tests,
enable_coverage,
extra_args,
ccache_variant,
enable_assertions,
sanitizer,
enable_valgrind,
)?;
Ok(())
}
///
/// The `crt` building sequence.
///
fn build_crt(
mut targets: HashSet<Platform>,
source_directory: &Path,
build_directory: &Path,
target_directory: &Path,
ccache_variant: Option<CcacheVariant>,
) -> anyhow::Result<()> {
targets.insert(Platform::AArch64);
crate::utils::command(
Command::new("cmake")
.args([
"-S",
source_directory.to_string_lossy().as_ref(),
"-B",
build_directory.to_string_lossy().as_ref(),
"-G",
"Ninja",
format!(
"-DCMAKE_INSTALL_PREFIX='{}'",
target_directory.to_string_lossy()
)
.as_str(),
"-DCMAKE_BUILD_TYPE='Release'",
"-DCMAKE_C_COMPILER='clang'",
"-DCMAKE_CXX_COMPILER='clang++'",
"-DLLVM_ENABLE_PROJECTS='compiler-rt'",
format!("-DLLVM_TARGETS_TO_BUILD='{}'", Platform::AArch64).as_str(),
"-DLLVM_DEFAULT_TARGET_TRIPLE='aarch64-unknown-linux-musl'",
"-DLLVM_BUILD_TESTS='Off'",
"-DLLVM_BUILD_RUNTIMES='Off'",
"-DLLVM_BUILD_UTILS='Off'",
"-DLLVM_INCLUDE_TESTS='Off'",
"-DLLVM_INCLUDE_RUNTIMES='Off'",
"-DLLVM_INCLUDE_UTILS='Off'",
"-DCOMPILER_RT_DEFAULT_TARGET_ARCH='aarch64'",
"-DCOMPILER_RT_BUILD_CRT='On'",
"-DCOMPILER_RT_BUILD_BUILTINS='On'",
"-DCOMPILER_RT_BUILD_SANITIZERS='Off'",
"-DCOMPILER_RT_BUILD_XRAY='Off'",
"-DCOMPILER_RT_BUILD_LIBFUZZER='Off'",
"-DCOMPILER_RT_BUILD_PROFILE='Off'",
"-DCOMPILER_RT_BUILD_MEMPROF='Off'",
"-DCOMPILER_RT_BUILD_ORC='Off'",
])
.args(crate::platforms::shared::SHARED_BUILD_OPTS)
.args(crate::platforms::shared::shared_build_opts_ccache(
ccache_variant,
)),
"CRT building cmake",
)?;
crate::utils::command(
Command::new("ninja")
.arg("-C")
.arg(build_directory)
.arg("install-crt"),
"CRT building ninja",
)?;
Ok(())
}
///
/// The host toolchain building sequence.
///
fn build_host(
source_directory: &Path,
build_directory: &Path,
target_directory: &Path,
musl_target_directory: &Path,
crt_target_directory: &Path,
ccache_variant: Option<CcacheVariant>,
) -> anyhow::Result<()> {
crate::utils::command(
Command::new("cmake")
.args([
"-S",
source_directory.to_string_lossy().as_ref(),
"-B",
build_directory.to_string_lossy().as_ref(),
"-G",
"Ninja",
format!(
"-DDEFAULT_SYSROOT='{}'",
musl_target_directory.to_string_lossy()
)
.as_str(),
"-DLINKER_SUPPORTS_COLOR_DIAGNOSTICS=0",
format!(
"-DCMAKE_INSTALL_PREFIX='{}'",
target_directory.to_string_lossy()
)
.as_str(),
"-DCMAKE_BUILD_TYPE='Release'",
"-DCMAKE_C_COMPILER='clang'",
"-DCMAKE_CXX_COMPILER='clang++'",
"-DCLANG_DEFAULT_CXX_STDLIB='libc++'",
"-DCLANG_DEFAULT_RTLIB='compiler-rt'",
"-DLLVM_DEFAULT_TARGET_TRIPLE='aarch64-unknown-linux-musl'",
"-DLLVM_TARGETS_TO_BUILD='AArch64'",
"-DLLVM_BUILD_TESTS='Off'",
"-DLLVM_BUILD_UTILS='Off'",
"-DLLVM_INCLUDE_TESTS='Off'",
"-DLLVM_INCLUDE_UTILS='Off'",
"-DLLVM_ENABLE_PROJECTS='clang;lld'",
"-DLLVM_ENABLE_RUNTIMES='compiler-rt;libcxx;libcxxabi;libunwind'",
"-DLIBCXX_CXX_ABI='libcxxabi'",
"-DLIBCXX_HAS_MUSL_LIBC='On'",
"-DLIBCXX_ENABLE_SHARED='Off'",
"-DLIBCXX_ENABLE_STATIC='On'",
"-DLIBCXX_ENABLE_STATIC_ABI_LIBRARY='On'",
"-DLIBCXXABI_ENABLE_SHARED='Off'",
"-DLIBCXXABI_ENABLE_STATIC='On'",
"-DLIBCXXABI_ENABLE_STATIC_UNWINDER='On'",
"-DLIBCXXABI_USE_LLVM_UNWINDER='On'",
"-DLIBCXXABI_USE_COMPILER_RT='On'",
"-DLIBUNWIND_ENABLE_STATIC='On'",
"-DLIBUNWIND_ENABLE_SHARED='Off'",
"-DCOMPILER_RT_BUILD_CRT='On'",
"-DCOMPILER_RT_BUILD_SANITIZERS='Off'",
"-DCOMPILER_RT_BUILD_XRAY='Off'",
"-DCOMPILER_RT_BUILD_LIBFUZZER='Off'",
"-DCOMPILER_RT_BUILD_PROFILE='On'",
"-DCOMPILER_RT_BUILD_MEMPROF='Off'",
"-DCOMPILER_RT_BUILD_ORC='Off'",
"-DCOMPILER_RT_DEFAULT_TARGET_ARCH='aarch64'",
"-DCOMPILER_RT_DEFAULT_TARGET_ONLY='On'",
])
.args(crate::platforms::shared::SHARED_BUILD_OPTS)
.args(crate::platforms::shared::shared_build_opts_ccache(
ccache_variant,
)),
"LLVM host building cmake",
)?;
let mut crt_lib_directory = crt_target_directory.to_path_buf();
crt_lib_directory.push("lib/");
let mut build_lib_directory = build_directory.to_path_buf();
build_lib_directory.push("lib/");
let copy_options = fs_extra::dir::CopyOptions {
overwrite: true,
copy_inside: true,
content_only: true,
..Default::default()
};
fs_extra::dir::copy(crt_lib_directory, build_lib_directory, &copy_options)?;
crate::utils::command(
Command::new("ninja")
.arg("-C")
.arg(build_directory)
.arg("install"),
"LLVM host building ninja",
)?;
Ok(())
}
///
/// The target toolchain building sequence.
///
#[allow(clippy::too_many_arguments)]
fn build_target(
build_type: BuildType,
targets: HashSet<Platform>,
llvm_projects: HashSet<LLVMProject>,
enable_rtti: bool,
default_target: Option<TargetTriple>,
source_directory: &Path,
build_directory: &Path,
target_directory: &Path,
musl_target_directory: &Path,
host_target_directory: &Path,
enable_tests: bool,
enable_coverage: bool,
extra_args: &[String],
ccache_variant: Option<CcacheVariant>,
enable_assertions: bool,
sanitizer: Option<Sanitizer>,
enable_valgrind: bool,
) -> anyhow::Result<()> {
let mut clang_path = host_target_directory.to_path_buf();
clang_path.push("bin/clang");
let mut clang_cxx_path = host_target_directory.to_path_buf();
clang_cxx_path.push("bin/clang++");
crate::utils::command(
Command::new("cmake")
.args([
"-S",
source_directory.to_string_lossy().as_ref(),
"-B",
build_directory.to_string_lossy().as_ref(),
"-G",
"Ninja",
"-DBUILD_SHARED_LIBS='Off'",
"-DLINKER_SUPPORTS_COLOR_DIAGNOSTICS=0",
format!(
"-DCMAKE_INSTALL_PREFIX='{}'",
target_directory.to_string_lossy()
)
.as_str(),
format!("-DCMAKE_BUILD_TYPE='{build_type}'").as_str(),
format!("-DCMAKE_C_COMPILER='{}'", clang_path.to_string_lossy()).as_str(),
format!(
"-DCMAKE_CXX_COMPILER='{}'",
clang_cxx_path.to_string_lossy()
)
.as_str(),
"-DCMAKE_FIND_LIBRARY_SUFFIXES='.a'",
"-DCMAKE_BUILD_WITH_INSTALL_RPATH=1",
"-DCMAKE_EXE_LINKER_FLAGS='-fuse-ld=lld -static'",
format!(
"-DLLVM_TARGETS_TO_BUILD='{}'",
targets
.into_iter()
.map(|platform| platform.to_string())
.collect::<Vec<String>>()
.join(";")
)
.as_str(),
format!(
"-DLLVM_ENABLE_PROJECTS='{}'",
llvm_projects
.into_iter()
.map(|project| project.to_string())
.collect::<Vec<String>>()
.join(";")
)
.as_str(),
])
.args(crate::platforms::shared::shared_build_opts_default_target(
default_target,
))
.args(crate::platforms::shared::SHARED_BUILD_OPTS)
.args(crate::platforms::shared::SHARED_BUILD_OPTS_NOT_MUSL)
.args(crate::platforms::shared::shared_build_opts_werror(
crate::target_env::TargetEnv::MUSL,
))
.args(crate::platforms::shared::shared_build_opts_tests(
enable_tests,
))
.args(crate::platforms::shared::shared_build_opts_coverage(
enable_coverage,
))
.args(extra_args)
.args(crate::platforms::shared::shared_build_opts_ccache(
ccache_variant,
))
.args(crate::platforms::shared::shared_build_opts_assertions(
enable_assertions,
))
.args(crate::platforms::shared::shared_build_opts_rtti(
enable_rtti,
))
.args(crate::platforms::shared::shared_build_opts_sanitizers(
sanitizer,
))
.args(crate::platforms::shared::shared_build_opts_valgrind(
enable_valgrind,
)),
"LLVM target building cmake",
)?;
crate::utils::ninja(build_directory)?;
let mut musl_lib_directory = musl_target_directory.to_path_buf();
musl_lib_directory.push("lib/");
let mut host_lib_directory = host_target_directory.to_path_buf();
host_lib_directory.push("lib/aarch64-unknown-linux-musl/");
let mut target_lib_directory = target_directory.to_path_buf();
target_lib_directory.push("lib/");
let copy_options = fs_extra::dir::CopyOptions {
overwrite: true,
copy_inside: true,
content_only: true,
..Default::default()
};
fs_extra::dir::copy(
musl_lib_directory,
target_lib_directory.as_path(),
&copy_options,
)?;
fs_extra::dir::copy(
host_lib_directory,
target_lib_directory.as_path(),
&copy_options,
)?;
Ok(())
}
@@ -0,0 +1,105 @@
//! The revive LLVM arm64 `macos-aarch64` builder.
use std::collections::HashSet;
use std::process::Command;
use crate::build_type::BuildType;
use crate::ccache_variant::CcacheVariant;
use crate::llvm_path::LLVMPath;
use crate::llvm_project::LLVMProject;
use crate::platforms::Platform;
use crate::sanitizer::Sanitizer;
use crate::target_triple::TargetTriple;
/// The building sequence.
#[allow(clippy::too_many_arguments)]
pub fn build(
build_type: BuildType,
targets: HashSet<Platform>,
llvm_projects: HashSet<LLVMProject>,
enable_rtti: bool,
default_target: Option<TargetTriple>,
enable_tests: bool,
enable_coverage: bool,
extra_args: &[String],
ccache_variant: Option<CcacheVariant>,
enable_assertions: bool,
sanitizer: Option<Sanitizer>,
) -> anyhow::Result<()> {
crate::utils::check_presence("cmake")?;
crate::utils::check_presence("ninja")?;
let llvm_module_llvm = LLVMPath::llvm_module_llvm()?;
let llvm_build_final = LLVMPath::llvm_build_final()?;
let llvm_target_final = LLVMPath::llvm_target_final()?;
crate::utils::command(
Command::new("cmake")
.args([
"-S",
llvm_module_llvm.to_string_lossy().as_ref(),
"-B",
llvm_build_final.to_string_lossy().as_ref(),
"-G",
"Ninja",
format!(
"-DCMAKE_INSTALL_PREFIX='{}'",
llvm_target_final.to_string_lossy().as_ref(),
)
.as_str(),
format!("-DCMAKE_BUILD_TYPE='{build_type}'").as_str(),
format!(
"-DLLVM_TARGETS_TO_BUILD='{}'",
targets
.into_iter()
.map(|platform| platform.to_string())
.collect::<Vec<String>>()
.join(";")
)
.as_str(),
format!(
"-DLLVM_ENABLE_PROJECTS='{}'",
llvm_projects
.into_iter()
.map(|project| project.to_string())
.collect::<Vec<String>>()
.join(";")
)
.as_str(),
"-DCMAKE_OSX_DEPLOYMENT_TARGET='11.0'",
])
.args(crate::platforms::shared::shared_build_opts_default_target(
default_target,
))
.args(crate::platforms::shared::shared_build_opts_tests(
enable_tests,
))
.args(crate::platforms::shared::shared_build_opts_coverage(
enable_coverage,
))
.args(crate::platforms::shared::SHARED_BUILD_OPTS)
.args(crate::platforms::shared::SHARED_BUILD_OPTS_NOT_MUSL)
.args(crate::platforms::shared::shared_build_opts_werror(
crate::target_env::TargetEnv::GNU,
))
.args(extra_args)
.args(crate::platforms::shared::shared_build_opts_ccache(
ccache_variant,
))
.args(crate::platforms::shared::shared_build_opts_assertions(
enable_assertions,
))
.args(crate::platforms::shared::shared_build_opts_rtti(
enable_rtti,
))
.args(crate::platforms::shared::macos_build_opts_ignore_dupicate_libs_warnings())
.args(crate::platforms::shared::shared_build_opts_sanitizers(
sanitizer,
)),
"LLVM building cmake",
)?;
crate::utils::ninja(llvm_build_final.as_ref())?;
Ok(())
}
+45
View File
@@ -0,0 +1,45 @@
//! The revive LLVM builder platforms.
pub mod aarch64_linux_gnu;
pub mod aarch64_linux_musl;
pub mod aarch64_macos;
pub mod shared;
pub mod wasm32_emscripten;
pub mod x86_64_linux_gnu;
pub mod x86_64_linux_musl;
pub mod x86_64_macos;
pub mod x86_64_windows_gnu;
use std::str::FromStr;
/// The list of platforms used as constants.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Platform {
/// The native X86 platform.
X86,
/// The native AArch64 platform.
AArch64,
/// The PolkaVM RISC-V platform.
PolkaVM,
}
impl FromStr for Platform {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"PolkaVM" => Ok(Self::PolkaVM),
value => Err(format!("Unsupported platform: `{}`", value)),
}
}
}
impl std::fmt::Display for Platform {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::X86 => write!(f, "X86"),
Self::AArch64 => write!(f, "AArch64"),
Self::PolkaVM => write!(f, "RISCV"),
}
}
}
+232
View File
@@ -0,0 +1,232 @@
//! The shared options for building various platforms.
use crate::ccache_variant::CcacheVariant;
use crate::sanitizer::Sanitizer;
use crate::target_env::TargetEnv;
use crate::target_triple::TargetTriple;
use std::path::Path;
use std::process::Command;
/// The build options shared by all platforms.
pub const SHARED_BUILD_OPTS: [&str; 19] = [
"-DPACKAGE_VENDOR='Parity Technologies'",
"-DCMAKE_BUILD_WITH_INSTALL_RPATH=1",
"-DLLVM_BUILD_DOCS='Off'",
"-DLLVM_INCLUDE_DOCS='Off'",
"-DLLVM_INCLUDE_BENCHMARKS='Off'",
"-DLLVM_INCLUDE_EXAMPLES='Off'",
"-DLLVM_ENABLE_DOXYGEN='Off'",
"-DLLVM_ENABLE_SPHINX='Off'",
"-DLLVM_ENABLE_OCAMLDOC='Off'",
"-DLLVM_ENABLE_ZLIB='Off'",
"-DLLVM_ENABLE_ZSTD='Off'",
"-DLLVM_ENABLE_LIBXML2='Off'",
"-DLLVM_ENABLE_BINDINGS='Off'",
"-DLLVM_ENABLE_TERMINFO='Off'",
"-DLLVM_ENABLE_LIBEDIT='Off'",
"-DLLVM_ENABLE_LIBPFM='Off'",
"-DCMAKE_EXPORT_COMPILE_COMMANDS='On'",
"-DPython3_FIND_REGISTRY='LAST'", // Use Python version from $PATH, not from registry
"-DBUG_REPORT_URL='https://github.com/paritytech/contract-issues/issues/'",
];
/// The build options shared by all platforms except MUSL.
pub const SHARED_BUILD_OPTS_NOT_MUSL: [&str; 4] = [
"-DLLVM_OPTIMIZED_TABLEGEN='On'",
"-DLLVM_BUILD_RUNTIME='Off'",
"-DLLVM_BUILD_RUNTIMES='Off'",
"-DLLVM_INCLUDE_RUNTIMES='Off'",
];
/// The shared build options to treat warnings as errors.
///
/// Disabled on Windows due to the following upstream issue with MSYS2 with mingw-w64:
/// ProgramTest.cpp:23:15: error: '__p__environ' redeclared without 'dllimport' attribute
pub fn shared_build_opts_werror(target_env: TargetEnv) -> Vec<String> {
vec![format!(
"-DLLVM_ENABLE_WERROR='{}'",
if cfg!(target_os = "windows") || target_env == TargetEnv::Emscripten {
"Off"
} else {
"On"
},
)]
}
/// The build options to set the default target.
pub fn shared_build_opts_default_target(target: Option<TargetTriple>) -> Vec<String> {
match target {
Some(target) => vec![format!(
"-DLLVM_DEFAULT_TARGET_TRIPLE='{}'",
target.to_string()
)],
None => vec![format!(
"-DLLVM_DEFAULT_TARGET_TRIPLE='{}'",
TargetTriple::PolkaVM
)],
}
}
/// The `musl` building sequence.
pub fn build_musl(build_directory: &Path, target_directory: &Path) -> anyhow::Result<()> {
std::fs::create_dir_all(build_directory)?;
std::fs::create_dir_all(target_directory)?;
crate::utils::command(
Command::new("../configure")
.current_dir(build_directory)
.arg(format!("--prefix={}", target_directory.to_string_lossy()))
.arg(format!(
"--syslibdir={}/lib/",
target_directory.to_string_lossy()
))
.arg("--enable-wrapper='clang'"),
"MUSL configuring",
)?;
crate::utils::command(
Command::new("make")
.current_dir(build_directory)
.arg("-j")
.arg(num_cpus::get().to_string()),
"MUSL building",
)?;
crate::utils::command(
Command::new("make")
.current_dir(build_directory)
.arg("install"),
"MUSL installing",
)?;
let mut include_directory = target_directory.to_path_buf();
include_directory.push("include/");
let mut asm_include_directory = include_directory.clone();
asm_include_directory.push("asm/");
std::fs::create_dir_all(asm_include_directory.as_path())?;
let mut types_header_path = asm_include_directory.clone();
types_header_path.push("types.h");
let copy_options = fs_extra::dir::CopyOptions {
overwrite: true,
copy_inside: true,
..Default::default()
};
fs_extra::dir::copy("/usr/include/linux", include_directory, &copy_options)?;
let copy_options = fs_extra::dir::CopyOptions {
overwrite: true,
copy_inside: true,
content_only: true,
..Default::default()
};
fs_extra::dir::copy(
"/usr/include/asm-generic",
asm_include_directory,
&copy_options,
)?;
crate::utils::command(
Command::new("sed")
.arg("-i")
.arg("s/asm-generic/asm/")
.arg(types_header_path),
"types_header asm signature replacement",
)?;
Ok(())
}
/// The build options to enable assertions.
pub fn shared_build_opts_assertions(enabled: bool) -> Vec<String> {
vec![format!(
"-DLLVM_ENABLE_ASSERTIONS='{}'",
if enabled { "On" } else { "Off" },
)]
}
/// The build options to build with RTTI support.
pub fn shared_build_opts_rtti(enabled: bool) -> Vec<String> {
vec![format!(
"-DLLVM_ENABLE_RTTI='{}'",
if enabled { "On" } else { "Off" },
)]
}
/// The build options to enable sanitizers.
pub fn shared_build_opts_sanitizers(sanitizer: Option<Sanitizer>) -> Vec<String> {
match sanitizer {
Some(sanitizer) => vec![format!("-DLLVM_USE_SANITIZER='{}'", sanitizer)],
None => vec![],
}
}
/// The build options to enable Valgrind for LLVM regression tests.
pub fn shared_build_opts_valgrind(enabled: bool) -> Vec<String> {
if enabled {
vec!["-DLLVM_LIT_ARGS='-sv --vg --vg-leak'".to_owned()]
} else {
vec![]
}
}
/// The LLVM tests build options shared by all platforms.
pub fn shared_build_opts_tests(enabled: bool) -> Vec<String> {
vec![
format!(
"-DLLVM_BUILD_UTILS='{}'",
if enabled { "On" } else { "Off" },
),
format!(
"-DLLVM_BUILD_TESTS='{}'",
if enabled { "On" } else { "Off" },
),
format!(
"-DLLVM_INCLUDE_UTILS='{}'",
if enabled { "On" } else { "Off" },
),
format!(
"-DLLVM_INCLUDE_TESTS='{}'",
if enabled { "On" } else { "Off" },
),
]
}
/// The code coverage build options shared by all platforms.
pub fn shared_build_opts_coverage(enabled: bool) -> Vec<String> {
vec![format!(
"-DLLVM_BUILD_INSTRUMENTED_COVERAGE='{}'",
if enabled { "On" } else { "Off" },
)]
}
/// Use of compiler cache (ccache) to speed up the build process.
pub fn shared_build_opts_ccache(ccache_variant: Option<CcacheVariant>) -> Vec<String> {
match ccache_variant {
Some(ccache_variant) => vec![
format!(
"-DCMAKE_C_COMPILER_LAUNCHER='{}'",
ccache_variant.to_string()
),
format!(
"-DCMAKE_CXX_COMPILER_LAUNCHER='{}'",
ccache_variant.to_string()
),
],
None => vec![],
}
}
/// Ignore duplicate libraries warnings for MacOS with XCode>=15.
pub fn macos_build_opts_ignore_dupicate_libs_warnings() -> Vec<String> {
let xcode_version =
crate::utils::get_xcode_version().unwrap_or(crate::utils::XCODE_MIN_VERSION);
if xcode_version >= crate::utils::XCODE_VERSION_15 {
vec![
"-DCMAKE_EXE_LINKER_FLAGS='-Wl,-no_warn_duplicate_libraries'".to_owned(),
"-DCMAKE_SHARED_LINKER_FLAGS='-Wl,-no_warn_duplicate_libraries'".to_owned(),
]
} else {
vec![]
}
}
@@ -0,0 +1,224 @@
//! The revive LLVM `wasm32_unknown_emscripten` builder.
//!
//! Cross-compiling LLVM for Emscripten requires llvm-tblgen, clang-tblgen and llvm-config.
use std::{collections::HashSet, path::Path, process::Command};
/// The building sequence.
#[allow(clippy::too_many_arguments)]
pub fn build(
build_type: crate::BuildType,
targets: HashSet<crate::Platform>,
llvm_projects: HashSet<crate::llvm_project::LLVMProject>,
enable_rtti: bool,
default_target: Option<crate::TargetTriple>,
enable_tests: bool,
enable_coverage: bool,
extra_args: &[String],
ccache_variant: Option<crate::ccache_variant::CcacheVariant>,
enable_assertions: bool,
sanitizer: Option<crate::sanitizer::Sanitizer>,
enable_valgrind: bool,
) -> anyhow::Result<()> {
crate::utils::check_presence("cmake")?;
crate::utils::check_presence("ninja")?;
crate::utils::check_presence("emsdk")?;
crate::utils::check_presence("clang")?;
crate::utils::check_presence("clang++")?;
if cfg!(target_os = "linux") {
crate::utils::check_presence("lld")?;
}
let llvm_module_llvm = crate::LLVMPath::llvm_module_llvm()?;
let llvm_host_module_llvm = crate::LLVMPath::llvm_host_module_llvm()?;
let llvm_build_host = crate::LLVMPath::llvm_build_host()?;
let llvm_target_host = crate::LLVMPath::llvm_target_host()?;
let llvm_build_final = crate::LLVMPath::llvm_build_final()?;
let llvm_target_final = crate::LLVMPath::llvm_target_final()?;
build_host(
llvm_host_module_llvm.as_path(),
llvm_build_host.as_path(),
llvm_target_host.as_path(),
ccache_variant,
)?;
build_target(
build_type,
targets,
llvm_projects,
enable_rtti,
default_target,
llvm_module_llvm.as_path(),
llvm_build_final.as_path(),
llvm_target_final.as_path(),
llvm_target_host.as_path(),
enable_tests,
enable_coverage,
extra_args,
ccache_variant,
enable_assertions,
sanitizer,
enable_valgrind,
)?;
Ok(())
}
/// The host toolchain building sequence.
fn build_host(
source_directory: &Path,
build_directory: &Path,
target_directory: &Path,
ccache_variant: Option<crate::ccache_variant::CcacheVariant>,
) -> anyhow::Result<()> {
log::info!("building the LLVM Emscripten host utilities");
crate::utils::command(
Command::new("cmake")
.args([
"-S",
source_directory.to_string_lossy().as_ref(),
"-B",
build_directory.to_string_lossy().as_ref(),
"-G",
"Ninja",
"-DLINKER_SUPPORTS_COLOR_DIAGNOSTICS=0",
&format!(
"-DCMAKE_INSTALL_PREFIX='{}'",
target_directory.to_string_lossy()
),
"-DLLVM_BUILD_SHARED_LIBS='Off'",
"-DCMAKE_BUILD_TYPE='Release'",
&format!(
"-DLLVM_TARGETS_TO_BUILD='WebAssembly;{}'",
crate::Platform::PolkaVM
),
"-DLLVM_ENABLE_PROJECTS='clang;lld'",
])
.args(crate::platforms::shared::SHARED_BUILD_OPTS)
.args(crate::platforms::shared::SHARED_BUILD_OPTS_NOT_MUSL)
.args(crate::platforms::shared::shared_build_opts_ccache(
ccache_variant,
)),
"LLVM host building cmake config",
)?;
crate::utils::ninja(build_directory)?;
Ok(())
}
/// The target toolchain building sequence.
#[allow(clippy::too_many_arguments)]
fn build_target(
build_type: crate::BuildType,
targets: HashSet<crate::Platform>,
llvm_projects: HashSet<crate::llvm_project::LLVMProject>,
enable_rtti: bool,
default_target: Option<crate::TargetTriple>,
source_directory: &Path,
build_directory: &Path,
target_directory: &Path,
host_target_directory: &Path,
enable_tests: bool,
enable_coverage: bool,
extra_args: &[String],
ccache_variant: Option<crate::ccache_variant::CcacheVariant>,
enable_assertions: bool,
sanitizer: Option<crate::sanitizer::Sanitizer>,
enable_valgrind: bool,
) -> anyhow::Result<()> {
let mut llvm_tblgen_path = host_target_directory.to_path_buf();
llvm_tblgen_path.push("bin/llvm-tblgen");
let mut clang_tblgen_path = host_target_directory.to_path_buf();
clang_tblgen_path.push("bin/clang-tblgen");
crate::utils::command(
Command::new("emcmake")
.env("EMCC_DEBUG", "2")
.env("CXXFLAGS", "-Dwait4=__syscall_wait4")
.env("LDFLAGS", "-lnodefs.js -s NO_INVOKE_RUN -s EXIT_RUNTIME -s INITIAL_MEMORY=64MB -s ALLOW_MEMORY_GROWTH -s EXPORTED_RUNTIME_METHODS=FS,callMain,NODEFS -s MODULARIZE -s EXPORT_ES6 -s WASM_BIGINT")
.arg("cmake")
.args([
"-S",
source_directory.to_string_lossy().as_ref(),
"-B",
build_directory.to_string_lossy().as_ref(),
"-G",
"Ninja",
"-DLINKER_SUPPORTS_COLOR_DIAGNOSTICS=0",
"-DCMAKE_BUILD_WITH_INSTALL_RPATH=1",
// Enable thin LTO but emscripten has various issues with it.
// FIXME: https://github.com/paritytech/revive/issues/148
//"-DLLVM_ENABLE_LTO='Thin'",
//"-DCMAKE_EXE_LINKER_FLAGS='-Wl,-u,htons -Wl,-u,htonl -Wl,-u,fileno -Wl,-u,ntohs'",
&format!(
"-DCMAKE_INSTALL_PREFIX='{}'",
target_directory.to_string_lossy()
),
&format!("-DCMAKE_BUILD_TYPE='{build_type}'"),
&format!(
"-DLLVM_TARGETS_TO_BUILD='{}'",
targets
.into_iter()
.map(|platform| platform.to_string())
.collect::<Vec<String>>()
.join(";")
),
&format!(
"-DLLVM_ENABLE_PROJECTS='{}'",
llvm_projects
.into_iter()
.map(|project| project.to_string())
.collect::<Vec<String>>()
.join(";")
),
"-DLLVM_BUILD_SHARED_LIBS='Off'",
"-DLLVM_ENABLE_DUMP='Off'",
"-DLLVM_ENABLE_EXPENSIVE_CHECKS='Off'",
"-DLLVM_ENABLE_BACKTRACES='Off'",
"-DLLVM_ENABLE_BACKTRACES='Off'",
"-DLLVM_ENABLE_THREADS='Off'",
"-DLLVM_BUILD_TOOLS='Off'",
&format!("-DLLVM_TABLEGEN='{}'", llvm_tblgen_path.to_string_lossy()),
&format!("-DCLANG_TABLEGEN='{}'", clang_tblgen_path.to_string_lossy()),
])
.args(crate::platforms::shared::shared_build_opts_default_target(
default_target,
))
.args(crate::platforms::shared::SHARED_BUILD_OPTS)
.args(crate::platforms::shared::SHARED_BUILD_OPTS_NOT_MUSL)
.args(crate::platforms::shared::shared_build_opts_werror(crate::target_env::TargetEnv::Emscripten))
.args(crate::platforms::shared::shared_build_opts_tests(
enable_tests,
))
.args(crate::platforms::shared::shared_build_opts_coverage(
enable_coverage,
))
.args(extra_args)
.args(crate::platforms::shared::shared_build_opts_ccache(
ccache_variant,
))
.args(crate::platforms::shared::shared_build_opts_assertions(
enable_assertions,
))
.args(crate::platforms::shared::shared_build_opts_rtti(
enable_rtti,
))
.args(crate::platforms::shared::shared_build_opts_sanitizers(
sanitizer,
))
.args(crate::platforms::shared::shared_build_opts_valgrind(
enable_valgrind,
)),
"LLVM target building cmake",
)?;
crate::utils::ninja(build_directory)?;
Ok(())
}
@@ -0,0 +1,111 @@
//! The revive LLVM amd64 `linux-gnu` builder.
use std::collections::HashSet;
use std::process::Command;
use crate::build_type::BuildType;
use crate::ccache_variant::CcacheVariant;
use crate::llvm_path::LLVMPath;
use crate::llvm_project::LLVMProject;
use crate::platforms::Platform;
use crate::sanitizer::Sanitizer;
use crate::target_triple::TargetTriple;
/// The building sequence.
#[allow(clippy::too_many_arguments)]
pub fn build(
build_type: BuildType,
targets: HashSet<Platform>,
llvm_projects: HashSet<LLVMProject>,
enable_rtti: bool,
default_target: Option<TargetTriple>,
enable_tests: bool,
enable_coverage: bool,
extra_args: &[String],
ccache_variant: Option<CcacheVariant>,
enable_assertions: bool,
sanitizer: Option<Sanitizer>,
enable_valgrind: bool,
) -> anyhow::Result<()> {
crate::utils::check_presence("cmake")?;
crate::utils::check_presence("clang")?;
crate::utils::check_presence("clang++")?;
crate::utils::check_presence("lld")?;
crate::utils::check_presence("ninja")?;
let llvm_module_llvm = LLVMPath::llvm_module_llvm()?;
let llvm_build_final = LLVMPath::llvm_build_final()?;
let llvm_target_final = LLVMPath::llvm_target_final()?;
crate::utils::command(
Command::new("cmake")
.args([
"-S",
llvm_module_llvm.to_string_lossy().as_ref(),
"-B",
llvm_build_final.to_string_lossy().as_ref(),
"-G",
"Ninja",
format!(
"-DCMAKE_INSTALL_PREFIX='{}'",
llvm_target_final.to_string_lossy().as_ref(),
)
.as_str(),
format!("-DCMAKE_BUILD_TYPE='{build_type}'").as_str(),
"-DCMAKE_C_COMPILER='clang'",
"-DCMAKE_CXX_COMPILER='clang++'",
format!(
"-DLLVM_TARGETS_TO_BUILD='{}'",
targets
.into_iter()
.map(|platform| platform.to_string())
.collect::<Vec<String>>()
.join(";")
)
.as_str(),
format!(
"-DLLVM_ENABLE_PROJECTS='{}'",
llvm_projects
.into_iter()
.map(|project| project.to_string())
.collect::<Vec<String>>()
.join(";")
)
.as_str(),
"-DLLVM_USE_LINKER='lld'",
])
.args(crate::platforms::shared::shared_build_opts_default_target(
default_target,
))
.args(crate::platforms::shared::shared_build_opts_tests(
enable_tests,
))
.args(crate::platforms::shared::shared_build_opts_coverage(
enable_coverage,
))
.args(crate::platforms::shared::shared_build_opts_ccache(
ccache_variant,
))
.args(crate::platforms::shared::SHARED_BUILD_OPTS)
.args(crate::platforms::shared::SHARED_BUILD_OPTS_NOT_MUSL)
.args(crate::platforms::shared::shared_build_opts_werror(
crate::target_env::TargetEnv::GNU,
))
.args(extra_args)
.args(crate::platforms::shared::shared_build_opts_assertions(
enable_assertions,
))
.args(crate::platforms::shared::shared_build_opts_rtti(
enable_rtti,
))
.args(crate::platforms::shared::shared_build_opts_sanitizers(
sanitizer,
))
.args(crate::platforms::shared::shared_build_opts_valgrind(
enable_valgrind,
)),
"LLVM building cmake",
)?;
crate::utils::ninja(llvm_build_final.as_ref())?;
Ok(())
}
@@ -0,0 +1,394 @@
//! The revive LLVM amd64 `linux-musl` builder.
use std::collections::HashSet;
use std::path::Path;
use std::process::Command;
use crate::build_type::BuildType;
use crate::ccache_variant::CcacheVariant;
use crate::llvm_path::LLVMPath;
use crate::llvm_project::LLVMProject;
use crate::platforms::Platform;
use crate::sanitizer::Sanitizer;
use crate::target_triple::TargetTriple;
/// The building sequence.
#[allow(clippy::too_many_arguments)]
pub fn build(
build_type: BuildType,
targets: HashSet<Platform>,
llvm_projects: HashSet<LLVMProject>,
enable_rtti: bool,
default_target: Option<TargetTriple>,
enable_tests: bool,
enable_coverage: bool,
extra_args: &[String],
ccache_variant: Option<CcacheVariant>,
enable_assertions: bool,
sanitizer: Option<Sanitizer>,
enable_valgrind: bool,
) -> anyhow::Result<()> {
log::info!("building for target x86_64_linux_musl");
crate::utils::check_presence("cmake")?;
crate::utils::check_presence("clang")?;
crate::utils::check_presence("clang++")?;
crate::utils::check_presence("lld")?;
crate::utils::check_presence("ninja")?;
let musl_name = "musl-1.2.3";
let musl_build = LLVMPath::musl_build(musl_name)?;
let musl_target = LLVMPath::musl_target()?;
let llvm_module_llvm = LLVMPath::llvm_module_llvm()?;
let llvm_host_module_llvm = LLVMPath::llvm_host_module_llvm()?;
let llvm_build_crt = LLVMPath::llvm_build_crt()?;
let llvm_target_crt = LLVMPath::llvm_target_crt()?;
let llvm_build_host = LLVMPath::llvm_build_host()?;
let llvm_target_host = LLVMPath::llvm_target_host()?;
let llvm_build_final = LLVMPath::llvm_build_final()?;
let llvm_target_final = LLVMPath::llvm_target_final()?;
if !LLVMPath::musl_source(musl_name)?.exists() {
crate::utils::download_musl(musl_name)?;
}
crate::platforms::shared::build_musl(musl_build.as_path(), musl_target.as_path())?;
build_crt(
targets.clone(),
llvm_host_module_llvm.as_path(),
llvm_build_crt.as_path(),
llvm_target_crt.as_path(),
ccache_variant,
)?;
build_host(
llvm_host_module_llvm.as_path(),
llvm_build_host.as_path(),
llvm_target_host.as_path(),
musl_target.as_path(),
llvm_target_crt.as_path(),
ccache_variant,
)?;
build_target(
build_type,
targets,
llvm_projects,
enable_rtti,
default_target,
llvm_module_llvm.as_path(),
llvm_build_final.as_path(),
llvm_target_final.as_path(),
musl_target.as_path(),
llvm_target_host.as_path(),
enable_tests,
enable_coverage,
extra_args,
ccache_variant,
enable_assertions,
sanitizer,
enable_valgrind,
)?;
Ok(())
}
/// The `crt` building sequence.
fn build_crt(
mut targets: HashSet<Platform>,
source_directory: &Path,
build_directory: &Path,
target_directory: &Path,
ccache_variant: Option<CcacheVariant>,
) -> anyhow::Result<()> {
targets.insert(Platform::X86);
crate::utils::command(
Command::new("cmake")
.args([
"-S",
source_directory.to_string_lossy().as_ref(),
"-B",
build_directory.to_string_lossy().as_ref(),
"-G",
"Ninja",
format!(
"-DCMAKE_INSTALL_PREFIX='{}'",
target_directory.to_string_lossy()
)
.as_str(),
"-DCMAKE_BUILD_TYPE='Release'",
"-DCMAKE_C_COMPILER='clang'",
"-DCMAKE_CXX_COMPILER='clang++'",
"-DLLVM_ENABLE_PROJECTS='compiler-rt'",
format!("-DLLVM_TARGETS_TO_BUILD='{}'", Platform::X86).as_str(),
"-DLLVM_DEFAULT_TARGET_TRIPLE='x86_64-pc-linux-musl'",
"-DLLVM_BUILD_TESTS='Off'",
"-DLLVM_BUILD_RUNTIMES='Off'",
"-DLLVM_BUILD_UTILS='Off'",
"-DLLVM_INCLUDE_TESTS='Off'",
"-DLLVM_INCLUDE_RUNTIMES='Off'",
"-DLLVM_INCLUDE_UTILS='Off'",
"-DCOMPILER_RT_DEFAULT_TARGET_ARCH='x86_64'",
"-DCOMPILER_RT_BUILD_CRT='On'",
"-DCOMPILER_RT_BUILD_SANITIZERS='Off'",
"-DCOMPILER_RT_BUILD_XRAY='Off'",
"-DCOMPILER_RT_BUILD_LIBFUZZER='Off'",
"-DCOMPILER_RT_BUILD_PROFILE='Off'",
"-DCOMPILER_RT_BUILD_MEMPROF='Off'",
"-DCOMPILER_RT_BUILD_ORC='Off'",
])
.args(crate::platforms::shared::SHARED_BUILD_OPTS)
.args(crate::platforms::shared::shared_build_opts_ccache(
ccache_variant,
)),
"CRT building cmake",
)?;
crate::utils::command(
Command::new("ninja")
.arg("-C")
.arg(build_directory)
.arg("install-crt"),
"CRT building ninja",
)?;
Ok(())
}
/// The host toolchain building sequence.
fn build_host(
source_directory: &Path,
build_directory: &Path,
target_directory: &Path,
musl_target_directory: &Path,
crt_target_directory: &Path,
ccache_variant: Option<CcacheVariant>,
) -> anyhow::Result<()> {
crate::utils::command(
Command::new("cmake")
.args([
"-S",
source_directory.to_string_lossy().as_ref(),
"-B",
build_directory.to_string_lossy().as_ref(),
"-G",
"Ninja",
format!(
"-DDEFAULT_SYSROOT='{}'",
musl_target_directory.to_string_lossy()
)
.as_str(),
"-DLINKER_SUPPORTS_COLOR_DIAGNOSTICS=0",
format!(
"-DCMAKE_INSTALL_PREFIX='{}'",
target_directory.to_string_lossy()
)
.as_str(),
"-DCMAKE_BUILD_TYPE='Release'",
"-DCMAKE_C_COMPILER='clang'",
"-DCMAKE_CXX_COMPILER='clang++'",
"-DCLANG_DEFAULT_CXX_STDLIB='libc++'",
"-DCLANG_DEFAULT_RTLIB='compiler-rt'",
"-DLLVM_DEFAULT_TARGET_TRIPLE='x86_64-pc-linux-musl'",
"-DLLVM_TARGETS_TO_BUILD='X86'",
"-DLLVM_BUILD_TESTS='Off'",
"-DLLVM_BUILD_UTILS='Off'",
"-DLLVM_INCLUDE_TESTS='Off'",
"-DLLVM_INCLUDE_UTILS='Off'",
"-DLLVM_ENABLE_PROJECTS='clang;lld'",
"-DLLVM_ENABLE_RUNTIMES='compiler-rt;libcxx;libcxxabi;libunwind'",
"-DLIBCXX_CXX_ABI='libcxxabi'",
"-DLIBCXX_HAS_MUSL_LIBC='On'",
"-DLIBCXX_ENABLE_SHARED='Off'",
"-DLIBCXX_ENABLE_STATIC='On'",
"-DLIBCXX_ENABLE_STATIC_ABI_LIBRARY='On'",
"-DLIBCXXABI_ENABLE_SHARED='Off'",
"-DLIBCXXABI_ENABLE_STATIC='On'",
"-DLIBCXXABI_ENABLE_STATIC_UNWINDER='On'",
"-DLIBCXXABI_USE_LLVM_UNWINDER='On'",
"-DLIBCXXABI_USE_COMPILER_RT='On'",
"-DLIBUNWIND_ENABLE_STATIC='On'",
"-DLIBUNWIND_ENABLE_SHARED='Off'",
"-DCOMPILER_RT_BUILD_CRT='On'",
"-DCOMPILER_RT_BUILD_SANITIZERS='Off'",
"-DCOMPILER_RT_BUILD_XRAY='Off'",
"-DCOMPILER_RT_BUILD_LIBFUZZER='Off'",
"-DCOMPILER_RT_BUILD_PROFILE='On'",
"-DCOMPILER_RT_BUILD_MEMPROF='Off'",
"-DCOMPILER_RT_BUILD_ORC='Off'",
"-DCOMPILER_RT_DEFAULT_TARGET_ARCH='x86_64'",
"-DCOMPILER_RT_DEFAULT_TARGET_ONLY='On'",
"-DLIBCLANG_BUILD_STATIC='On'",
"-DBUILD_SHARED_LIBS='Off'",
])
.args(crate::platforms::shared::SHARED_BUILD_OPTS)
.args(crate::platforms::shared::shared_build_opts_ccache(
ccache_variant,
)),
"LLVM host building cmake",
)?;
let mut crt_lib_directory = crt_target_directory.to_path_buf();
crt_lib_directory.push("lib/");
let mut build_lib_directory = build_directory.to_path_buf();
build_lib_directory.push("lib/");
let copy_options = fs_extra::dir::CopyOptions {
overwrite: true,
copy_inside: true,
content_only: true,
..Default::default()
};
fs_extra::dir::copy(crt_lib_directory, build_lib_directory, &copy_options)?;
crate::utils::command(
Command::new("ninja")
.arg("-C")
.arg(build_directory)
.arg("install"),
"LLVM host building ninja",
)?;
Ok(())
}
/// The target toolchain building sequence.
#[allow(clippy::too_many_arguments)]
fn build_target(
build_type: BuildType,
targets: HashSet<Platform>,
llvm_projects: HashSet<LLVMProject>,
enable_rtti: bool,
default_target: Option<TargetTriple>,
source_directory: &Path,
build_directory: &Path,
target_directory: &Path,
musl_target_directory: &Path,
host_target_directory: &Path,
enable_tests: bool,
enable_coverage: bool,
extra_args: &[String],
ccache_variant: Option<CcacheVariant>,
enable_assertions: bool,
sanitizer: Option<Sanitizer>,
enable_valgrind: bool,
) -> anyhow::Result<()> {
let mut clang_path = host_target_directory.to_path_buf();
clang_path.push("bin/clang");
let mut clang_cxx_path = host_target_directory.to_path_buf();
clang_cxx_path.push("bin/clang++");
crate::utils::command(
Command::new("cmake")
.args([
"-S",
source_directory.to_string_lossy().as_ref(),
"-B",
build_directory.to_string_lossy().as_ref(),
"-G",
"Ninja",
"-DBUILD_SHARED_LIBS='Off'",
"-DLIBCLANG_BUILD_STATIC='On'",
"-DLLVM_BUILD_STATIC='On'",
"-DLINKER_SUPPORTS_COLOR_DIAGNOSTICS=0",
format!(
"-DCMAKE_INSTALL_PREFIX='{}'",
target_directory.to_string_lossy()
)
.as_str(),
format!("-DCMAKE_BUILD_TYPE='{build_type}'").as_str(),
format!("-DCMAKE_C_COMPILER='{}'", clang_path.to_string_lossy()).as_str(),
format!(
"-DCMAKE_CXX_COMPILER='{}'",
clang_cxx_path.to_string_lossy()
)
.as_str(),
"-DCMAKE_FIND_LIBRARY_SUFFIXES='.a'",
"-DCMAKE_BUILD_WITH_INSTALL_RPATH=1",
"-DCMAKE_EXE_LINKER_FLAGS='-fuse-ld=lld -static'",
format!(
"-DLLVM_TARGETS_TO_BUILD='{}'",
targets
.into_iter()
.map(|platform| platform.to_string())
.collect::<Vec<String>>()
.join(";")
)
.as_str(),
format!(
"-DLLVM_ENABLE_PROJECTS='{}'",
llvm_projects
.into_iter()
.map(|project| project.to_string())
.collect::<Vec<String>>()
.join(";")
)
.as_str(),
])
.args(crate::platforms::shared::shared_build_opts_default_target(
default_target,
))
.args(crate::platforms::shared::SHARED_BUILD_OPTS)
.args(crate::platforms::shared::SHARED_BUILD_OPTS_NOT_MUSL)
.args(crate::platforms::shared::shared_build_opts_werror(
crate::target_env::TargetEnv::MUSL,
))
.args(crate::platforms::shared::shared_build_opts_tests(
enable_tests,
))
.args(crate::platforms::shared::shared_build_opts_coverage(
enable_coverage,
))
.args(extra_args)
.args(crate::platforms::shared::shared_build_opts_ccache(
ccache_variant,
))
.args(crate::platforms::shared::shared_build_opts_assertions(
enable_assertions,
))
.args(crate::platforms::shared::shared_build_opts_rtti(
enable_rtti,
))
.args(crate::platforms::shared::shared_build_opts_sanitizers(
sanitizer,
))
.args(crate::platforms::shared::shared_build_opts_valgrind(
enable_valgrind,
)),
"LLVM target building cmake",
)?;
crate::utils::ninja(build_directory)?;
let mut musl_lib_directory = musl_target_directory.to_path_buf();
musl_lib_directory.push("lib/");
let mut host_lib_directory = host_target_directory.to_path_buf();
host_lib_directory.push("lib/x86_64-pc-linux-musl/");
let mut target_lib_directory = target_directory.to_path_buf();
target_lib_directory.push("lib/");
let copy_options = fs_extra::dir::CopyOptions {
overwrite: true,
copy_inside: true,
content_only: true,
..Default::default()
};
fs_extra::dir::copy(
musl_lib_directory,
target_lib_directory.as_path(),
&copy_options,
)?;
fs_extra::dir::copy(
host_lib_directory,
target_lib_directory.as_path(),
&copy_options,
)?;
Ok(())
}
@@ -0,0 +1,105 @@
//! The revive LLVM amd64 `macos` builder.
use std::collections::HashSet;
use std::process::Command;
use crate::build_type::BuildType;
use crate::ccache_variant::CcacheVariant;
use crate::llvm_path::LLVMPath;
use crate::llvm_project::LLVMProject;
use crate::platforms::Platform;
use crate::sanitizer::Sanitizer;
use crate::target_triple::TargetTriple;
/// The building sequence.
#[allow(clippy::too_many_arguments)]
pub fn build(
build_type: BuildType,
targets: HashSet<Platform>,
llvm_projects: HashSet<LLVMProject>,
enable_rtti: bool,
default_target: Option<TargetTriple>,
enable_tests: bool,
enable_coverage: bool,
extra_args: &[String],
ccache_variant: Option<CcacheVariant>,
enable_assertions: bool,
sanitizer: Option<Sanitizer>,
) -> anyhow::Result<()> {
crate::utils::check_presence("cmake")?;
crate::utils::check_presence("ninja")?;
let llvm_module_llvm = LLVMPath::llvm_module_llvm()?;
let llvm_build_final = LLVMPath::llvm_build_final()?;
let llvm_target_final = LLVMPath::llvm_target_final()?;
crate::utils::command(
Command::new("cmake")
.args([
"-S",
llvm_module_llvm.to_string_lossy().as_ref(),
"-B",
llvm_build_final.to_string_lossy().as_ref(),
"-G",
"Ninja",
format!(
"-DCMAKE_INSTALL_PREFIX='{}'",
llvm_target_final.to_string_lossy().as_ref(),
)
.as_str(),
format!("-DCMAKE_BUILD_TYPE='{build_type}'").as_str(),
format!(
"-DLLVM_TARGETS_TO_BUILD='{}'",
targets
.into_iter()
.map(|platform| platform.to_string())
.collect::<Vec<String>>()
.join(";")
)
.as_str(),
format!(
"-DLLVM_ENABLE_PROJECTS='{}'",
llvm_projects
.into_iter()
.map(|project| project.to_string())
.collect::<Vec<String>>()
.join(";")
)
.as_str(),
"-DCMAKE_OSX_DEPLOYMENT_TARGET='11.0'",
])
.args(crate::platforms::shared::shared_build_opts_default_target(
default_target,
))
.args(crate::platforms::shared::shared_build_opts_tests(
enable_tests,
))
.args(crate::platforms::shared::shared_build_opts_coverage(
enable_coverage,
))
.args(crate::platforms::shared::SHARED_BUILD_OPTS)
.args(crate::platforms::shared::SHARED_BUILD_OPTS_NOT_MUSL)
.args(crate::platforms::shared::shared_build_opts_werror(
crate::target_env::TargetEnv::GNU,
))
.args(extra_args)
.args(crate::platforms::shared::shared_build_opts_ccache(
ccache_variant,
))
.args(crate::platforms::shared::shared_build_opts_assertions(
enable_assertions,
))
.args(crate::platforms::shared::shared_build_opts_rtti(
enable_rtti,
))
.args(crate::platforms::shared::macos_build_opts_ignore_dupicate_libs_warnings())
.args(crate::platforms::shared::shared_build_opts_sanitizers(
sanitizer,
)),
"LLVM building cmake",
)?;
crate::utils::ninja(llvm_build_final.as_ref())?;
Ok(())
}
@@ -0,0 +1,127 @@
//! The revive LLVM amd64 `windows-gnu` builder.
use std::collections::HashSet;
use std::path::PathBuf;
use std::process::Command;
use crate::build_type::BuildType;
use crate::ccache_variant::CcacheVariant;
use crate::llvm_path::LLVMPath;
use crate::llvm_project::LLVMProject;
use crate::platforms::Platform;
use crate::sanitizer::Sanitizer;
use crate::target_triple::TargetTriple;
/// The building sequence.
#[allow(clippy::too_many_arguments)]
pub fn build(
build_type: BuildType,
targets: HashSet<Platform>,
llvm_projects: HashSet<LLVMProject>,
enable_rtti: bool,
default_target: Option<TargetTriple>,
enable_tests: bool,
enable_coverage: bool,
extra_args: &[String],
ccache_variant: Option<CcacheVariant>,
enable_assertions: bool,
sanitizer: Option<Sanitizer>,
) -> anyhow::Result<()> {
crate::utils::check_presence("cmake")?;
crate::utils::check_presence("clang")?;
crate::utils::check_presence("clang++")?;
crate::utils::check_presence("lld")?;
crate::utils::check_presence("ninja")?;
let llvm_module_llvm =
LLVMPath::llvm_module_llvm().and_then(crate::utils::path_windows_to_unix)?;
let llvm_build_final =
LLVMPath::llvm_build_final().and_then(crate::utils::path_windows_to_unix)?;
let llvm_target_final =
LLVMPath::llvm_target_final().and_then(crate::utils::path_windows_to_unix)?;
crate::utils::command(
Command::new("cmake")
.args([
"-S",
llvm_module_llvm.to_string_lossy().as_ref(),
"-B",
llvm_build_final.to_string_lossy().as_ref(),
"-G",
"Ninja",
format!(
"-DCMAKE_INSTALL_PREFIX='{}'",
llvm_target_final.to_string_lossy().as_ref(),
)
.as_str(),
format!("-DCMAKE_BUILD_TYPE='{build_type}'").as_str(),
"-DCMAKE_C_COMPILER='clang'",
"-DCMAKE_CXX_COMPILER='clang++'",
format!(
"-DLLVM_TARGETS_TO_BUILD='{}'",
targets
.into_iter()
.map(|platform| platform.to_string())
.collect::<Vec<String>>()
.join(";")
)
.as_str(),
format!(
"-DLLVM_ENABLE_PROJECTS='{}'",
llvm_projects
.into_iter()
.map(|project| project.to_string())
.collect::<Vec<String>>()
.join(";")
)
.as_str(),
"-DLLVM_USE_LINKER='lld'",
])
.args(crate::platforms::shared::shared_build_opts_default_target(
default_target,
))
.args(crate::platforms::shared::shared_build_opts_tests(
enable_tests,
))
.args(crate::platforms::shared::shared_build_opts_coverage(
enable_coverage,
))
.args(crate::platforms::shared::SHARED_BUILD_OPTS)
.args(crate::platforms::shared::SHARED_BUILD_OPTS_NOT_MUSL)
.args(crate::platforms::shared::shared_build_opts_werror(
crate::target_env::TargetEnv::GNU,
))
.args(extra_args)
.args(crate::platforms::shared::shared_build_opts_ccache(
ccache_variant,
))
.args(crate::platforms::shared::shared_build_opts_assertions(
enable_assertions,
))
.args(crate::platforms::shared::shared_build_opts_rtti(
enable_rtti,
))
.args(crate::platforms::shared::shared_build_opts_sanitizers(
sanitizer,
)),
"LLVM building cmake",
)?;
crate::utils::ninja(llvm_build_final.as_ref())?;
let libstdcpp_source_path = match std::env::var("LIBSTDCPP_SOURCE_PATH") {
Ok(libstdcpp_source_path) => PathBuf::from(libstdcpp_source_path),
Err(error) => anyhow::bail!(
"The `LIBSTDCPP_SOURCE_PATH` must be set to the path to the libstdc++.a static library: {}", error
),
};
let mut libstdcpp_destination_path = llvm_target_final;
libstdcpp_destination_path.push("./lib/libstdc++.a");
fs_extra::file::copy(
crate::utils::path_windows_to_unix(libstdcpp_source_path)?,
crate::utils::path_windows_to_unix(libstdcpp_destination_path)?,
&fs_extra::file::CopyOptions::default(),
)?;
Ok(())
}
@@ -0,0 +1,113 @@
//! The revive LLVM builder arguments.
use clap::Parser;
use revive_llvm_builder::ccache_variant::CcacheVariant;
/// The revive LLVM builder arguments.
#[derive(Debug, Parser)]
#[command(version, about)]
pub struct Arguments {
/// Target environment to build LLVM (gnu, musl, emscripten).
#[arg(long, default_value_t = revive_llvm_builder::target_env::TargetEnv::GNU)]
pub target_env: revive_llvm_builder::target_env::TargetEnv,
#[command(subcommand)]
pub subcommand: Subcommand,
}
/// The revive LLVM builder arguments.
#[derive(Debug, clap::Subcommand)]
pub enum Subcommand {
/// Clone the branch specified in `LLVM.lock`.
Clone {
/// Clone with full commits history.
#[arg(long)]
deep: bool,
},
/// Build the LLVM framework.
Build {
/// LLVM build type (`Debug`, `Release`, `RelWithDebInfo`, or `MinSizeRel`).
#[arg(long, default_value_t = revive_llvm_builder::BuildType::Release)]
build_type: revive_llvm_builder::BuildType,
/// Additional targets to build LLVM with.
#[arg(long)]
targets: Vec<String>,
/// LLVM projects to build LLVM with.
#[arg(long)]
llvm_projects: Vec<revive_llvm_builder::llvm_project::LLVMProject>,
/// Whether to build LLVM with run-time type information (RTTI) enabled.
#[arg(long)]
enable_rtti: bool,
/// The default target to build LLVM with.
#[arg(long)]
default_target: Option<revive_llvm_builder::target_triple::TargetTriple>,
/// Whether to build the LLVM tests.
#[arg(long)]
enable_tests: bool,
/// Whether to build LLVM for source-based code coverage.
#[arg(long)]
enable_coverage: bool,
/// Extra arguments to pass to CMake.
/// A leading backslash will be unescaped.
#[arg(long)]
extra_args: Vec<String>,
/// Whether to use compiler cache (ccache) to speed-up builds.
#[arg(long)]
ccache_variant: Option<CcacheVariant>,
/// Whether to build with assertions enabled or not.
#[arg(long, default_value_t = true)]
enable_assertions: bool,
/// Build LLVM with sanitizer enabled (`Address`, `Memory`, `MemoryWithOrigins`, `Undefined`, `Thread`, `DataFlow`, or `Address;Undefined`).
#[arg(long)]
sanitizer: Option<revive_llvm_builder::sanitizer::Sanitizer>,
/// Whether to run LLVM unit tests under valgrind or not.
#[arg(long)]
enable_valgrind: bool,
},
/// Checkout the branch specified in `LLVM.lock`.
Checkout {
/// Remove all artifacts preventing the checkout (removes all local changes!).
#[arg(long)]
force: bool,
},
/// Clean the build artifacts.
Clean,
/// Build the LLVM compiler-rt builtins for the PolkaVM target.
Builtins {
/// LLVM build type (`Debug`, `Release`, `RelWithDebInfo`, or `MinSizeRel`).
#[arg(long, default_value_t = revive_llvm_builder::BuildType::Release)]
build_type: revive_llvm_builder::BuildType,
/// The default target to build LLVM with.
#[arg(long)]
default_target: Option<revive_llvm_builder::target_triple::TargetTriple>,
/// Extra arguments to pass to CMake.
/// A leading backslash will be unescaped.
#[arg(long)]
extra_args: Vec<String>,
/// Whether to use compiler cache (ccache) to speed-up builds.
#[arg(long)]
ccache_variant: Option<CcacheVariant>,
/// Build LLVM with sanitizer enabled (`Address`, `Memory`, `MemoryWithOrigins`, `Undefined`, `Thread`, `DataFlow`, or `Address;Undefined`).
#[arg(long)]
sanitizer: Option<revive_llvm_builder::sanitizer::Sanitizer>,
},
}
+141
View File
@@ -0,0 +1,141 @@
//! The revive LLVM builder.
pub(crate) mod arguments;
use std::collections::HashSet;
use std::path::PathBuf;
use std::str::FromStr;
use anyhow::Context;
use clap::Parser;
use self::arguments::{Arguments, Subcommand};
fn main() {
env_logger::init();
match main_inner() {
Ok(()) => std::process::exit(0),
Err(error) => {
log::error!("{error:?}");
std::process::exit(1)
}
}
}
fn main_inner() -> anyhow::Result<()> {
let arguments = Arguments::parse();
revive_llvm_builder::utils::directory_target_llvm(arguments.target_env);
match arguments.subcommand {
Subcommand::Clone { deep } => {
let lock = revive_llvm_builder::Lock::try_from(&PathBuf::from(
revive_llvm_builder::lock::LLVM_LOCK_DEFAULT_PATH,
))?;
revive_llvm_builder::clone(lock, deep, arguments.target_env)?;
}
Subcommand::Build {
build_type,
targets,
llvm_projects,
enable_rtti,
default_target,
enable_tests,
enable_coverage,
extra_args,
ccache_variant,
enable_assertions,
sanitizer,
enable_valgrind,
} => {
let mut targets = targets
.into_iter()
.map(|target| revive_llvm_builder::Platform::from_str(target.as_str()))
.collect::<Result<HashSet<revive_llvm_builder::Platform>, String>>()
.map_err(|platform| anyhow::anyhow!("Unknown platform `{}`", platform))?;
targets.insert(revive_llvm_builder::Platform::PolkaVM);
log::info!("build targets: {:?}", &targets);
let extra_args_unescaped: Vec<String> = extra_args
.iter()
.map(|argument| {
argument
.strip_prefix('\\')
.unwrap_or(argument.as_str())
.to_owned()
})
.collect();
log::debug!("extra_args: {:#?}", extra_args);
log::debug!("extra_args_unescaped: {:#?}", extra_args_unescaped);
if let Some(ccache_variant) = ccache_variant {
revive_llvm_builder::utils::check_presence(ccache_variant.to_string().as_str())?;
}
let mut projects = llvm_projects
.into_iter()
.map(|project| {
revive_llvm_builder::llvm_project::LLVMProject::from_str(
project.to_string().as_str(),
)
})
.collect::<Result<HashSet<revive_llvm_builder::llvm_project::LLVMProject>, String>>(
)
.map_err(|project| anyhow::anyhow!("Unknown LLVM project `{}`", project))?;
projects.insert(revive_llvm_builder::llvm_project::LLVMProject::LLD);
log::info!("build projects: {:?}", &projects);
revive_llvm_builder::build(
build_type,
arguments.target_env,
targets,
projects,
enable_rtti,
default_target,
enable_tests,
enable_coverage,
&extra_args_unescaped,
ccache_variant,
enable_assertions,
sanitizer,
enable_valgrind,
)?;
}
Subcommand::Checkout { force } => {
let lock = revive_llvm_builder::Lock::try_from(&PathBuf::from(
revive_llvm_builder::lock::LLVM_LOCK_DEFAULT_PATH,
))?;
revive_llvm_builder::checkout(lock, force)?;
}
Subcommand::Clean => {
revive_llvm_builder::clean()
.with_context(|| "Unable to remove target LLVM directory")?;
}
Subcommand::Builtins {
build_type,
default_target,
extra_args,
ccache_variant,
sanitizer,
} => {
revive_llvm_builder::builtins::build(
build_type,
arguments.target_env,
default_target,
&extra_args,
ccache_variant,
sanitizer,
)?;
}
}
Ok(())
}
+50
View File
@@ -0,0 +1,50 @@
//! LLVM sanitizers.
/// LLVM sanitizers.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Sanitizer {
/// The address sanitizer.
Address,
/// The memory sanitizer.
Memory,
/// The memory with origins sanitizer.
MemoryWithOrigins,
/// The undefined behavior sanitizer
Undefined,
/// The thread sanitizer.
Thread,
/// The data flow sanitizer.
DataFlow,
/// Combine address and undefined behavior sanitizer.
AddressUndefined,
}
impl std::str::FromStr for Sanitizer {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value.to_lowercase().as_str() {
"address" => Ok(Self::Address),
"memory" => Ok(Self::Memory),
"memorywithorigins" => Ok(Self::MemoryWithOrigins),
"undefined" => Ok(Self::Undefined),
"thread" => Ok(Self::Thread),
"dataflow" => Ok(Self::DataFlow),
"address;undefined" => Ok(Self::AddressUndefined),
value => Err(format!("Unsupported sanitizer: `{}`", value)),
}
}
}
impl std::fmt::Display for Sanitizer {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Address => write!(f, "Address"),
Self::Memory => write!(f, "Memory"),
Self::MemoryWithOrigins => write!(f, "MemoryWithOrigins"),
Self::Undefined => write!(f, "Undefined"),
Self::Thread => write!(f, "Thread"),
Self::DataFlow => write!(f, "DataFlow"),
Self::AddressUndefined => write!(f, "Address;Undefined"),
}
}
}
+36
View File
@@ -0,0 +1,36 @@
//! The target environments to build LLVM.
/// The list of target environments used as constants.
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TargetEnv {
/// The GNU target environment.
#[default]
GNU,
/// The MUSL target environment.
MUSL,
/// The wasm32 Emscripten environment.
Emscripten,
}
impl std::str::FromStr for TargetEnv {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"gnu" => Ok(Self::GNU),
"musl" => Ok(Self::MUSL),
"emscripten" => Ok(Self::Emscripten),
value => Err(format!("Unsupported target environment: `{}`", value)),
}
}
}
impl std::fmt::Display for TargetEnv {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::GNU => write!(f, "gnu"),
Self::MUSL => write!(f, "musl"),
Self::Emscripten => write!(f, "emscripten"),
}
}
}
+29
View File
@@ -0,0 +1,29 @@
//! The PolkaVM LLVM target triples.
/// The list of target triples used as constants.
///
/// It must be in the lowercase.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TargetTriple {
/// The PolkaVM RISC-V target triple.
PolkaVM,
}
impl std::str::FromStr for TargetTriple {
type Err = String;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"polkavm" => Ok(Self::PolkaVM),
value => Err(format!("Unsupported target triple: `{}`", value)),
}
}
}
impl std::fmt::Display for TargetTriple {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::PolkaVM => write!(f, "riscv64-unknown-elf"),
}
}
}
+230
View File
@@ -0,0 +1,230 @@
//! The LLVM builder utilities.
use std::fs::File;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::process::Stdio;
use std::time::Duration;
use path_slash::PathBufExt;
/// The LLVM host repository URL.
pub const LLVM_HOST_SOURCE_URL: &str = "https://github.com/llvm/llvm-project";
/// The LLVM host repository tag.
pub const LLVM_HOST_SOURCE_TAG: &str = "llvmorg-18.1.8";
/// The minimum required XCode version.
pub const XCODE_MIN_VERSION: u32 = 11;
/// The XCode version 15.
pub const XCODE_VERSION_15: u32 = 15;
/// The number of download retries if failed.
pub const DOWNLOAD_RETRIES: u16 = 16;
/// The number of parallel download requests.
pub const DOWNLOAD_PARALLEL_REQUESTS: u16 = 1;
/// The download timeout in seconds.
pub const DOWNLOAD_TIMEOUT_SECONDS: u64 = 300;
/// The musl snapshots URL.
pub const MUSL_SNAPSHOTS_URL: &str = "https://git.musl-libc.org/cgit/musl/snapshot";
/// The emscripten SDK git URL.
pub const EMSDK_SOURCE_URL: &str = "https://github.com/emscripten-core/emsdk.git";
/// The emscripten SDK version.
pub const EMSDK_VERSION: &str = "3.1.64";
/// The subprocess runner.
///
/// Checks the status and prints `stderr`.
pub fn command(command: &mut Command, description: &str) -> anyhow::Result<()> {
log::debug!("executing '{command:?}' ({description})");
if std::env::var("DRY_RUN").is_ok() {
log::warn!("Only a dry run; not executing the command.");
return Ok(());
}
let status = command
.status()
.map_err(|error| anyhow::anyhow!("{} process: {}", description, error))?;
if !status.success() {
log::error!("the command '{command:?}' failed!");
anyhow::bail!("{} failed", description);
}
Ok(())
}
/// Download a file from the URL to the path.
pub fn download(url: &str, path: &str) -> anyhow::Result<()> {
log::trace!("downloading '{url}' into '{path}'");
let mut downloader = downloader::Downloader::builder()
.download_folder(Path::new(path))
.parallel_requests(DOWNLOAD_PARALLEL_REQUESTS)
.retries(DOWNLOAD_RETRIES)
.timeout(Duration::from_secs(DOWNLOAD_TIMEOUT_SECONDS))
.build()?;
while let Err(error) = downloader.download(&[downloader::Download::new(url)]) {
log::error!("MUSL download from `{url}` failed: {error}");
}
Ok(())
}
/// Unpack a tarball.
pub fn unpack_tar(filename: PathBuf, path: &str) -> anyhow::Result<()> {
let tar_gz = File::open(filename)?;
let tar = flate2::read::GzDecoder::new(tar_gz);
let mut archive = tar::Archive::new(tar);
archive.unpack(path)?;
Ok(())
}
/// The `musl` downloading sequence.
pub fn download_musl(name: &str) -> anyhow::Result<()> {
log::info!("downloading musl {name}");
let tar_file_name = format!("{name}.tar.gz");
let url = format!("{}/{tar_file_name}", MUSL_SNAPSHOTS_URL);
let target_path = crate::llvm_path::DIRECTORY_LLVM_TARGET
.get()
.unwrap()
.to_string_lossy();
download(url.as_str(), &target_path)?;
let musl_tarball = crate::LLVMPath::musl_source(tar_file_name.as_str())?;
unpack_tar(musl_tarball, &target_path)?;
Ok(())
}
/// Call ninja to build the LLVM.
pub fn ninja(build_dir: &Path) -> anyhow::Result<()> {
let mut ninja = Command::new("ninja");
ninja.args(["-C", build_dir.to_string_lossy().as_ref()]);
if std::env::var("DRY_RUN").is_ok() {
ninja.arg("-n");
}
command(ninja.arg("install"), "Running ninja install")?;
Ok(())
}
/// Create an absolute path, appending it to the current working directory.
pub fn absolute_path<P: AsRef<Path>>(path: P) -> anyhow::Result<PathBuf> {
let mut full_path = std::env::current_dir()?;
full_path.push(path);
Ok(full_path)
}
///
/// Converts a Windows path into a Unix path.
///
pub fn path_windows_to_unix<P: AsRef<Path> + PathBufExt>(path: P) -> anyhow::Result<PathBuf> {
path.to_slash()
.map(|pathbuf| PathBuf::from(pathbuf.to_string()))
.ok_or_else(|| anyhow::anyhow!("Windows-to-Unix path conversion error"))
}
/// Checks if the tool exists in the system.
pub fn check_presence(name: &str) -> anyhow::Result<()> {
let description = &format!("checking the `{name}` executable");
log::info!("{description}");
command(Command::new("which").arg(name), description)
.map_err(|_| anyhow::anyhow!("Tool `{}` is missing. Please install", name))
}
/// Identify XCode version using `pkgutil`.
pub fn get_xcode_version() -> anyhow::Result<u32> {
let pkgutil = Command::new("pkgutil")
.args(["--pkg-info", "com.apple.pkg.CLTools_Executables"])
.stdout(Stdio::piped())
.spawn()
.map_err(|error| anyhow::anyhow!("`pkgutil` process: {}", error))?;
let grep_version = Command::new("grep")
.arg("version")
.stdin(Stdio::from(pkgutil.stdout.expect(
"Failed to identify XCode version - XCode or CLI tools are not installed",
)))
.output()
.map_err(|error| anyhow::anyhow!("`grep` process: {}", error))?;
let version_string = String::from_utf8(grep_version.stdout)?;
let version_regex = regex::Regex::new(r"version: (\d+)\..*")?;
let captures = version_regex
.captures(version_string.as_str())
.ok_or(anyhow::anyhow!(
"Failed to parse XCode version: {version_string}"
))?;
let xcode_version: u32 = captures
.get(1)
.expect("Always has a major version")
.as_str()
.parse()
.map_err(|error| anyhow::anyhow!("Failed to parse XCode version: {error}"))?;
Ok(xcode_version)
}
/// Install the Emscripten SDK.
pub fn install_emsdk() -> anyhow::Result<()> {
log::info!("installing emsdk v{EMSDK_VERSION}");
let emsdk_source_path = PathBuf::from(crate::LLVMPath::DIRECTORY_EMSDK_SOURCE);
if emsdk_source_path.exists() {
log::warn!(
"emsdk source path {emsdk_source_path:?} already exists.
Skipping the emsdk installation, delete the source path for re-installation"
);
return Ok(());
}
crate::utils::command(
Command::new("git")
.arg("clone")
.arg(crate::utils::EMSDK_SOURCE_URL)
.arg(emsdk_source_path.to_string_lossy().as_ref()),
"Emscripten SDK repository cloning",
)?;
crate::utils::command(
Command::new("git")
.arg("checkout")
.arg(format!("tags/{}", crate::utils::EMSDK_VERSION))
.current_dir(&emsdk_source_path),
"Emscripten SDK repository version checkout",
)?;
crate::utils::command(
Command::new("./emsdk")
.arg("install")
.arg(EMSDK_VERSION)
.current_dir(&emsdk_source_path),
"Emscripten SDK installation",
)?;
crate::utils::command(
Command::new("./emsdk")
.arg("activate")
.arg(EMSDK_VERSION)
.current_dir(&emsdk_source_path),
"Emscripten SDK activation",
)?;
log::warn!(
"run 'source {}emsdk_env.sh' to finish the emsdk installation",
emsdk_source_path.display()
);
Ok(())
}
/// The LLVM target directory default path.
pub fn directory_target_llvm(target_env: crate::target_env::TargetEnv) -> PathBuf {
crate::llvm_path::DIRECTORY_LLVM_TARGET
.get_or_init(|| PathBuf::from(format!("./target-llvm/{}/", target_env)))
.clone()
}
+158
View File
@@ -0,0 +1,158 @@
pub mod common;
use std::process::Command;
use assert_cmd::prelude::*;
/// This test verifies that the LLVM repository can be successfully cloned, built, and cleaned.
#[test]
fn clone_build_and_clean() -> anyhow::Result<()> {
let test_dir = common::TestDir::with_lockfile(None)?;
Command::cargo_bin(common::REVIVE_LLVM)?
.current_dir(test_dir.path())
.arg("clone")
.assert()
.success();
Command::cargo_bin(common::REVIVE_LLVM)?
.current_dir(test_dir.path())
.arg("build")
.arg("--llvm-projects")
.arg("clang")
.arg("--llvm-projects")
.arg("lld")
.assert()
.success();
Command::cargo_bin(common::REVIVE_LLVM)?
.current_dir(test_dir.path())
.arg("builtins")
.assert()
.success();
Command::cargo_bin(common::REVIVE_LLVM)?
.current_dir(test_dir.path())
.arg("clean")
.assert()
.success();
Ok(())
}
/// This test verifies that the LLVM repository can be successfully cloned, built, and cleaned
/// with 2-staged build using MUSL as sysroot.
#[test]
#[cfg(target_os = "linux")]
fn clone_build_and_clean_musl() -> anyhow::Result<()> {
let test_dir = common::TestDir::with_lockfile(None)?;
Command::cargo_bin(common::REVIVE_LLVM)?
.arg("clone")
.current_dir(test_dir.path())
.assert()
.success();
Command::cargo_bin(common::REVIVE_LLVM)?
.arg("--target-env")
.arg("musl")
.arg("build")
.arg("--llvm-projects")
.arg("clang")
.arg("--llvm-projects")
.arg("lld")
.current_dir(test_dir.path())
.assert()
.success();
Command::cargo_bin(common::REVIVE_LLVM)?
.current_dir(test_dir.path())
.arg("builtins")
.assert()
.success();
Command::cargo_bin(common::REVIVE_LLVM)?
.current_dir(test_dir.path())
.arg("clean")
.assert()
.success();
Ok(())
}
/// This test verifies that the LLVM repository can be successfully cloned and built in debug mode
/// with tests and coverage enabled.
#[test]
fn debug_build_with_tests_coverage() -> anyhow::Result<()> {
let test_dir = common::TestDir::with_lockfile(None)?;
Command::cargo_bin(common::REVIVE_LLVM)?
.current_dir(test_dir.path())
.arg("clone")
.assert()
.success();
Command::cargo_bin(common::REVIVE_LLVM)?
.current_dir(test_dir.path())
.arg("build")
.arg("--enable-coverage")
.arg("--enable-tests")
.arg("--build-type")
.arg("Debug")
.assert()
.success();
Ok(())
}
/// This test verifies that the LLVM repository can be successfully built with address sanitizer.
#[test]
fn build_with_sanitizers() -> anyhow::Result<()> {
let test_dir = common::TestDir::with_lockfile(None)?;
Command::cargo_bin(common::REVIVE_LLVM)?
.current_dir(test_dir.path())
.arg("clone")
.assert()
.success();
Command::cargo_bin(common::REVIVE_LLVM)?
.current_dir(test_dir.path())
.arg("build")
.arg("--sanitizer")
.arg("Address")
.assert()
.success();
Ok(())
}
/// Tests the clone, build, and clean process of the LLVM repository for the emscripten target.
#[test]
#[cfg(any(target_os = "linux", target_os = "macos"))]
fn clone_build_and_clean_emscripten() -> anyhow::Result<()> {
let test_dir = common::TestDir::with_lockfile(None)?;
let command = Command::cargo_bin(common::REVIVE_LLVM)?;
let program = command.get_program().to_string_lossy();
let emsdk_wrapped_build_command = format!(
"{program} --target-env emscripten clone && \
source {}emsdk_env.sh && \
{program} --target-env emscripten build --llvm-projects clang --llvm-projects lld",
revive_llvm_builder::LLVMPath::DIRECTORY_EMSDK_SOURCE,
);
Command::new("sh")
.arg("-c")
.arg(emsdk_wrapped_build_command)
.current_dir(test_dir.path())
.assert()
.success();
Command::cargo_bin(common::REVIVE_LLVM)?
.arg("clean")
.current_dir(test_dir.path())
.assert()
.success();
Ok(())
}
+48
View File
@@ -0,0 +1,48 @@
pub mod common;
use std::process::Command;
use assert_cmd::prelude::*;
/// This test verifies that after cloning the LLVM repository, checking out a specific branch
/// or reference works as expected.
#[test]
fn checkout_after_clone() -> anyhow::Result<()> {
let test_dir = common::TestDir::with_lockfile(None)?;
Command::cargo_bin(common::REVIVE_LLVM)?
.current_dir(test_dir.path())
.arg("clone")
.assert()
.success();
Command::cargo_bin(common::REVIVE_LLVM)?
.current_dir(test_dir.path())
.arg("checkout")
.assert()
.success();
Ok(())
}
/// This test verifies that after cloning the LLVM repository, checking out a specific branch
/// or reference with the `--force` option works as expected.
#[test]
fn force_checkout() -> anyhow::Result<()> {
let test_dir = common::TestDir::with_lockfile(None)?;
Command::cargo_bin(common::REVIVE_LLVM)?
.current_dir(test_dir.path())
.arg("clone")
.assert()
.success();
Command::cargo_bin(common::REVIVE_LLVM)?
.current_dir(test_dir.path())
.arg("checkout")
.arg("--force")
.assert()
.success();
Ok(())
}
+36
View File
@@ -0,0 +1,36 @@
pub mod common;
use std::process::Command;
use assert_cmd::prelude::*;
/// This test verifies that the LLVM repository can be successfully cloned using a specific branch
/// and reference.
#[test]
fn clone() -> anyhow::Result<()> {
let test_dir = common::TestDir::with_lockfile(None)?;
Command::cargo_bin(common::REVIVE_LLVM)?
.current_dir(test_dir.path())
.arg("clone")
.assert()
.success();
Ok(())
}
/// This test verifies that the LLVM repository can be successfully cloned using a specific branch
/// and reference with --deep option.
#[test]
fn clone_deep() -> anyhow::Result<()> {
let test_dir = common::TestDir::with_lockfile(None)?;
Command::cargo_bin(common::REVIVE_LLVM)?
.current_dir(test_dir.path())
.arg("clone")
.arg("--deep")
.assert()
.success();
Ok(())
}
+36
View File
@@ -0,0 +1,36 @@
use assert_fs::fixture::FileWriteStr;
pub const REVIVE_LLVM: &str = "revive-llvm";
pub const REVIVE_LLVM_REPO_URL: &str = "https://github.com/llvm/llvm-project";
pub const REVIVE_LLVM_REPO_TEST_BRANCH: &str = "release/18.x";
pub struct TestDir {
_lockfile: assert_fs::NamedTempFile,
path: std::path::PathBuf,
}
/// Creates a temporary lock file for testing.
impl TestDir {
pub fn with_lockfile(reference: Option<String>) -> anyhow::Result<Self> {
let file =
assert_fs::NamedTempFile::new(revive_llvm_builder::lock::LLVM_LOCK_DEFAULT_PATH)?;
let lock = revive_llvm_builder::Lock {
url: REVIVE_LLVM_REPO_URL.to_string(),
branch: REVIVE_LLVM_REPO_TEST_BRANCH.to_string(),
r#ref: reference,
};
file.write_str(toml::to_string(&lock)?.as_str())?;
Ok(Self {
path: file
.parent()
.expect("lockfile parent dir always exists")
.into(),
_lockfile: file,
})
}
pub fn path(&self) -> &std::path::Path {
&self.path
}
}
-8
View File
@@ -13,20 +13,13 @@ 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 }
itertools = { workspace = true }
serde = { workspace = true, features = ["derive"] }
regex = { workspace = true }
once_cell = { workspace = true }
num = { workspace = true }
hex = { workspace = true }
sha2 = { workspace = true }
sha3 = { workspace = true }
md5 = { workspace = true }
inkwell = { workspace = true }
@@ -36,5 +29,4 @@ polkavm-common = { workspace = true }
revive-common = { workspace = true }
revive-runtime-api = { workspace = true }
revive-linker = { workspace = true }
revive-builtins = { workspace = true }
revive-stdlib = { 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,
}
}
}
+45 -12
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();
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();
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();
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<()> {
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);
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<()> {
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);
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();
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();
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(())
}
+1 -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;
@@ -60,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.
+1 -4
View File
@@ -3,15 +3,12 @@
/// The LLVM framework version.
pub const LLVM_VERSION: semver::Version = semver::Version::new(18, 1, 4);
/// 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";
@@ -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,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;
@@ -94,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()
@@ -54,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()
@@ -72,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(())
}
}
@@ -18,35 +18,12 @@ impl Entry {
/// The call flags argument index.
pub const ARGUMENT_INDEX_CALL_FLAGS: usize = 0;
/// 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,
{
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,
);
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()),
@@ -65,9 +42,9 @@ impl Entry {
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(
@@ -80,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(
revive_runtime_api::polkavm_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(())
}
@@ -137,6 +83,8 @@ impl Entry {
where
D: Dependency + Clone,
{
context.set_debug_location(0, 0, None)?;
let is_deploy = context
.current_function()
.borrow()
@@ -195,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(())
}
@@ -214,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(())
}
}
@@ -27,14 +27,14 @@ where
runtime::FUNCTION_LOAD_IMMUTABLE_DATA,
context.void_type().fn_type(Default::default(), false),
0,
Some(inkwell::module::Linkage::Private),
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)?;
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
@@ -111,6 +111,8 @@ where
context.set_basic_block(return_block);
context.build_return(None);
context.pop_debug_scope();
Ok(())
}
}
@@ -54,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()
@@ -73,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);
+158 -23
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,6 +21,8 @@ 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;
@@ -35,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;
@@ -85,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>,
@@ -138,7 +140,7 @@ where
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);
}
}
@@ -163,8 +165,9 @@ where
fn link_immutable_data(&self, contract_path: &str) -> anyhow::Result<()> {
let size = self.solidity().immutables_size() as u32;
let exports = revive_runtime_api::immutable_data::module(self.llvm(), size);
self.module.link_in_module(exports).map_err(|error| {
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,
@@ -200,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,
@@ -207,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);
@@ -216,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,
@@ -232,7 +251,8 @@ where
dependency_manager,
include_metadata_hash,
// debug_info,
debug_info,
debug_config,
solidity_data: None,
@@ -253,11 +273,11 @@ where
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: {}",
@@ -275,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: {}",
@@ -296,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(),
) {
@@ -422,6 +449,21 @@ where
) -> anyhow::Result<Rc<RefCell<Function<'ctx>>>> {
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");
@@ -455,6 +497,8 @@ where
let function = Rc::new(RefCell::new(function));
self.functions.insert(name.to_string(), function.clone());
self.pop_debug_scope();
Ok(function)
}
@@ -470,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,
@@ -548,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.
@@ -1210,11 +1339,17 @@ where
self.llvm.custom_width_int_type(bit_length as u32)
}
/// Returns the register witdh sized type.
/// Returns the XLEN witdh sized type.
pub fn xlen_type(&self) -> inkwell::types::IntType<'ctx> {
self.llvm.custom_width_int_type(crate::polkavm::XLEN as u32)
}
/// Returns the PolkaVM native register width sized type.
pub fn register_type(&self) -> inkwell::types::IntType<'ctx> {
self.llvm
.custom_width_int_type(revive_common::BIT_LENGTH_X64 as u32)
}
/// Returns the sentinel pointer value.
pub fn sentinel_pointer(&self) -> Pointer<'ctx> {
let sentinel_pointer = self
@@ -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]
+122 -36
View File
@@ -13,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>,
@@ -40,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)?;
@@ -48,6 +48,9 @@ where
let output_length_pointer = context.build_alloca_at_entry(context.xlen_type(), "output_length");
context.build_store(output_length_pointer, output_length)?;
let deposit_pointer = context.build_alloca_at_entry(context.word_type(), "deposit_pointer");
context.build_store(deposit_pointer, context.word_type().const_all_ones())?;
let flags = if static_call {
REENTRANT_CALL_FLAG | STATIC_CALL_FLAG
} else {
@@ -55,42 +58,55 @@ where
};
let flags = context.xlen_type().const_int(flags as u64, false);
let argument_type = revive_runtime_api::calling_convention::call(context.llvm());
let argument_pointer = context.build_alloca_at_entry(argument_type, "call_arguments");
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.sentinel_pointer().value.as_basic_value_enum(),
value_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(
let flags_and_callee = revive_runtime_api::calling_convention::pack_hi_lo_reg(
context.builder(),
argument_pointer.value,
argument_type,
arguments,
context.llvm(),
flags,
address_pointer.to_int(context),
"address_and_callee",
)?;
let deposit_and_value = revive_runtime_api::calling_convention::pack_hi_lo_reg(
context.builder(),
context.llvm(),
deposit_pointer.to_int(context),
value_pointer.to_int(context),
"deposit_and_value",
)?;
let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
context.builder(),
context.llvm(),
input_length,
input_pointer.to_int(context),
"input_data",
)?;
let output_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
context.builder(),
context.llvm(),
output_length_pointer.to_int(context),
output_pointer.to_int(context),
"output_data",
)?;
let name = revive_runtime_api::polkavm_imports::CALL;
let argument_pointer = context.builder().build_ptr_to_int(
argument_pointer.value,
context.xlen_type(),
"call_argument_pointer",
)?;
let success = context
.build_runtime_call(name, &[argument_pointer.into()])
.build_runtime_call(
name,
&[
flags_and_callee.into(),
context.register_type().const_all_ones().into(),
context.register_type().const_all_ones().into(),
deposit_and_value.into(),
input_data.into(),
output_data.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.xlen_type().const_zero(),
context.integer_const(revive_common::BIT_LENGTH_X64, 0),
"is_success",
)?;
@@ -102,20 +118,90 @@ 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 deposit_pointer = context.build_alloca_at_entry(context.word_type(), "deposit_pointer");
context.build_store(deposit_pointer, context.word_type().const_all_ones())?;
let flags = context.xlen_type().const_int(0u64, false);
let flags_and_callee = revive_runtime_api::calling_convention::pack_hi_lo_reg(
context.builder(),
context.llvm(),
flags,
address_pointer.to_int(context),
"address_and_callee",
)?;
let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
context.builder(),
context.llvm(),
input_length,
input_pointer.to_int(context),
"input_data",
)?;
let output_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
context.builder(),
context.llvm(),
output_length_pointer.to_int(context),
output_pointer.to_int(context),
"output_data",
)?;
let name = revive_runtime_api::polkavm_imports::DELEGATE_CALL;
let success = context
.build_runtime_call(
name,
&[
flags_and_callee.into(),
context.register_type().const_all_ones().into(),
context.register_type().const_all_ones().into(),
deposit_pointer.to_int(context).into(),
input_data.into(),
output_data.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(())
}
+35 -8
View File
@@ -7,22 +7,38 @@ use crate::polkavm::Dependency;
/// 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.
@@ -74,13 +90,24 @@ where
/// 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.
@@ -110,7 +137,7 @@ pub fn basefee<'ctx, D>(
where
D: Dependency + Clone,
{
Ok(context.word_const(0).as_basic_value_enum())
context.build_runtime_call_to_getter(revive_runtime_api::polkavm_imports::BASE_FEE)
}
/// Translates the `address` instruction.
+30 -34
View File
@@ -26,15 +26,6 @@ where
let code_hash_pointer = context.build_heap_gep(input_offset, input_length)?;
let input_data_pointer = context.build_gep(
code_hash_pointer,
&[context
.xlen_type()
.const_int(revive_common::BYTE_LENGTH_WORD as u64, false)],
context.byte_type(),
"input_ptr_parameter_offset",
);
let value_pointer = context.build_alloca_at_entry(context.value_type(), "transferred_value");
context.build_store(value_pointer, value)?;
@@ -53,36 +44,41 @@ where
);
context.build_store(address_pointer, context.word_const(0))?;
let argument_type = revive_runtime_api::calling_convention::instantiate(context.llvm());
let argument_pointer = context.build_alloca_at_entry(argument_type, "instantiate_arguments");
let arguments = &[
code_hash_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.sentinel_pointer().value.as_basic_value_enum(),
value_pointer.value.as_basic_value_enum(),
input_data_pointer.value.as_basic_value_enum(),
input_length.as_basic_value_enum(),
address_pointer.value.as_basic_value_enum(),
context.sentinel_pointer().value.as_basic_value_enum(),
context.sentinel_pointer().value.as_basic_value_enum(),
salt_pointer.value.as_basic_value_enum(),
];
revive_runtime_api::calling_convention::spill(
let deposit_pointer = context.build_alloca_at_entry(context.word_type(), "deposit_pointer");
context.build_store(deposit_pointer, context.word_type().const_all_ones())?;
let deposit_and_value = revive_runtime_api::calling_convention::pack_hi_lo_reg(
context.builder(),
argument_pointer.value,
argument_type,
arguments,
context.llvm(),
deposit_pointer.to_int(context),
value_pointer.to_int(context),
"deposit_and_value",
)?;
let input_data = revive_runtime_api::calling_convention::pack_hi_lo_reg(
context.builder(),
context.llvm(),
input_length,
code_hash_pointer.to_int(context),
"input_data",
)?;
let address_and_salt = revive_runtime_api::calling_convention::pack_hi_lo_reg(
context.builder(),
context.llvm(),
address_pointer.to_int(context),
salt_pointer.to_int(context),
"output_data",
)?;
let argument_pointer = context.builder().build_ptr_to_int(
argument_pointer.value,
context.xlen_type(),
"instantiate_argument_pointer",
)?;
context.build_runtime_call(
revive_runtime_api::polkavm_imports::INSTANTIATE,
&[argument_pointer.into()],
&[
context.register_type().const_all_ones().into(),
context.register_type().const_all_ones().into(),
deposit_and_value.into(),
input_data.into(),
context.register_type().const_all_ones().into(),
address_and_salt.into(),
],
);
let address = context.build_byte_swap(context.build_load(address_pointer, "address")?)?;
@@ -1,7 +1,5 @@
//! Translates the value and balance operations.
use inkwell::values::BasicValue;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
@@ -12,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.
@@ -1,7 +1,5 @@
//! Translates the external code operations.
use inkwell::values::BasicValue;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
@@ -14,37 +12,24 @@ 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(
revive_runtime_api::polkavm_imports::CODE_SIZE,
&[address_pointer_casted.into()],
&[address_pointer.to_int(context).into()],
)
.unwrap_or_else(|| {
panic!(
"{} should return a value",
revive_runtime_api::polkavm_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.
@@ -10,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(
revive_runtime_api::polkavm_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
+5 -8
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)?;
}
Ok(Build::new(
assembly_text.to_owned(),
metadata_hash,
bytecode.to_owned(),
keccak256(bytecode),
hex::encode(sha3::Keccak256::digest(bytecode)),
))
}
@@ -98,7 +95,7 @@ pub trait Dependency {
path: &str,
optimizer_settings: OptimizerSettings,
include_metadata_hash: bool,
debug_config: Option<DebugConfig>,
debug_config: DebugConfig,
) -> anyhow::Result<String>;
/// Resolves a full contract path.
@@ -118,7 +115,7 @@ impl Dependency for DummyDependency {
_path: &str,
_optimizer_settings: OptimizerSettings,
_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,+a,+e,+m,+c,+fast-unaligned-access,+xtheadcondmov";
#[cfg(not(feature = "riscv-zbb"))]
pub const VM_FEATURES: &'static str = "+a,+e,+m,+c,+fast-unaligned-access,+xtheadcondmov";
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,

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