mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into breaking
This commit is contained in:
commit
88cc42230f
@ -21,8 +21,8 @@ parameters:
|
||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:c26a7ffc9fc243a4ec3105b9dc1edcdd964ad0e9665c83172b7ebda74bbf3021"
|
||||
emscripten-docker-image:
|
||||
type: string
|
||||
# solbuildpackpusher/solidity-buildpack-deps:emscripten-6
|
||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:092da5817bc032c91a806b4f73db2a1a31e5cc4c066d94d43eedd9f365df7154"
|
||||
# solbuildpackpusher/solidity-buildpack-deps:emscripten-7
|
||||
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:9ffcd0944433fe100e9433f2aa9ba5c21e096e758ad8a05a4a76feaed3d1f463"
|
||||
evm-version:
|
||||
type: string
|
||||
default: london
|
||||
@ -31,7 +31,7 @@ orbs:
|
||||
win: circleci/windows@2.2.0
|
||||
|
||||
commands:
|
||||
gitter_notify:
|
||||
gitter_notify_unless_pr:
|
||||
description: "Posts a notification to the main room on Gitter (if not running on a PR)."
|
||||
parameters:
|
||||
event:
|
||||
@ -44,6 +44,8 @@ commands:
|
||||
name: "Gitter notification"
|
||||
when: << parameters.condition >>
|
||||
command: |
|
||||
[[ $CI_PULL_REQUEST == "" ]] || { echo "Running on a PR - notification skipped."; exit 0; }
|
||||
|
||||
[[ "<< parameters.event >>" == "failure" ]] && message=" ❌ Nightly job **${CIRCLE_JOB}** failed on **${CIRCLE_BRANCH}**. Please see [build #${CIRCLE_BUILD_NUM}](${CIRCLE_BUILD_URL}) for details."
|
||||
[[ "<< parameters.event >>" == "success" ]] && message=" ✅ Nightly job **${CIRCLE_JOB}** succeeded on **${CIRCLE_BRANCH}**. Please see [build #${CIRCLE_BUILD_NUM}](${CIRCLE_BUILD_URL}) for details."
|
||||
|
||||
@ -55,17 +57,17 @@ commands:
|
||||
--header "Authorization: Bearer ${GITTER_API_TOKEN}" \
|
||||
--data "{\"text\":\"${message}\"}"
|
||||
|
||||
gitter_notify_failure:
|
||||
gitter_notify_failure_unless_pr:
|
||||
description: "Posts a failure notification to the main room on Gitter (if not running on a PR)."
|
||||
steps:
|
||||
- gitter_notify:
|
||||
- gitter_notify_unless_pr:
|
||||
event: failure
|
||||
condition: on_fail
|
||||
|
||||
gitter_notify_success:
|
||||
gitter_notify_success_unless_pr:
|
||||
description: "Posts a success notification to the main room on Gitter (if not running on a PR)."
|
||||
steps:
|
||||
- gitter_notify:
|
||||
- gitter_notify_unless_pr:
|
||||
event: success
|
||||
condition: on_success
|
||||
|
||||
@ -170,7 +172,7 @@ defaults:
|
||||
destination: test_results/
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Tests Templates
|
||||
# Step Templates
|
||||
|
||||
# store_test_results helper
|
||||
- store_test_results: &store_test_results
|
||||
@ -181,9 +183,13 @@ defaults:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: build
|
||||
# NOTE: Different build jobs produce different soltest executables (release/debug,
|
||||
# clang/gcc, windows/linux/macos, etc.). The executable used by these steps comes from the
|
||||
# attached workspace and we only see the items added to the workspace by jobs we depend on.
|
||||
- run: *run_soltest
|
||||
- store_test_results: *store_test_results
|
||||
- store_artifacts: *artifacts_test_results
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
- steps_soltest_all: &steps_soltest_all
|
||||
steps:
|
||||
@ -193,6 +199,7 @@ defaults:
|
||||
- run: *run_soltest_all
|
||||
- store_test_results: *store_test_results
|
||||
- store_artifacts: *artifacts_test_results
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
- steps_cmdline_tests: &steps_cmdline_tests
|
||||
steps:
|
||||
@ -202,31 +209,101 @@ defaults:
|
||||
- run: *run_cmdline_tests
|
||||
- store_test_results: *store_test_results
|
||||
- store_artifacts: *artifacts_test_results
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
- test_ubuntu1604_clang: &test_ubuntu1604_clang
|
||||
# --------------------------------------------------------------------------
|
||||
# Base Image Templates
|
||||
|
||||
- base_ubuntu1604_clang: &base_ubuntu1604_clang
|
||||
docker:
|
||||
- image: << pipeline.parameters.ubuntu-1604-clang-ossfuzz-docker-image >>
|
||||
<<: *steps_soltest
|
||||
environment:
|
||||
TERM: xterm
|
||||
|
||||
- test_ubuntu2004_clang: &test_ubuntu2004_clang
|
||||
- base_ubuntu2004_clang: &base_ubuntu2004_clang
|
||||
docker:
|
||||
- image: << pipeline.parameters.ubuntu-2004-clang-docker-image >>
|
||||
<<: *steps_soltest
|
||||
environment:
|
||||
TERM: xterm
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
MAKEFLAGS: -j 3
|
||||
|
||||
- test_ubuntu2004: &test_ubuntu2004
|
||||
- base_ubuntu2004_clang_xlarge: &base_ubuntu2004_clang_xlarge
|
||||
<<: *base_ubuntu2004_clang
|
||||
resource_class: xlarge
|
||||
environment:
|
||||
TERM: xterm
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
MAKEFLAGS: -j 10
|
||||
|
||||
- base_ubuntu2004: &base_ubuntu2004
|
||||
docker:
|
||||
- image: << pipeline.parameters.ubuntu-2004-docker-image >>
|
||||
parallelism: 6
|
||||
<<: *steps_soltest_all
|
||||
environment:
|
||||
TERM: xterm
|
||||
|
||||
- test_asan: &test_asan
|
||||
<<: *test_ubuntu2004
|
||||
<<: *steps_soltest
|
||||
- base_ubuntu2004_xlarge: &base_ubuntu2004_xlarge
|
||||
<<: *base_ubuntu2004
|
||||
resource_class: xlarge
|
||||
environment:
|
||||
TERM: xterm
|
||||
MAKEFLAGS: -j 10
|
||||
|
||||
- test_ubuntu2004_clang_cli: &test_ubuntu2004_clang_cli
|
||||
- base_buildpack_focal: &base_buildpack_focal
|
||||
docker:
|
||||
- image: << pipeline.parameters.ubuntu-2004-clang-docker-image >>
|
||||
<<: *steps_cmdline_tests
|
||||
- image: buildpack-deps:focal
|
||||
environment:
|
||||
TERM: xterm
|
||||
|
||||
- base_buildpack_latest: &base_buildpack_latest
|
||||
docker:
|
||||
- image: buildpack-deps:latest
|
||||
environment:
|
||||
TERM: xterm
|
||||
|
||||
- base_archlinux: &base_archlinux
|
||||
docker:
|
||||
- image: archlinux:base
|
||||
environment:
|
||||
TERM: xterm
|
||||
|
||||
- base_win_powershell: &base_win_powershell
|
||||
executor:
|
||||
name: win/default
|
||||
shell: powershell.exe
|
||||
|
||||
- base_win_cmd: &base_win_cmd
|
||||
executor:
|
||||
name: win/default
|
||||
shell: cmd.exe
|
||||
|
||||
- base_osx: &base_osx
|
||||
macos:
|
||||
xcode: "11.0.0"
|
||||
environment:
|
||||
TERM: xterm
|
||||
|
||||
- base_ems_xlarge: &base_ems_xlarge
|
||||
docker:
|
||||
- image: << pipeline.parameters.emscripten-docker-image >>
|
||||
resource_class: xlarge
|
||||
environment:
|
||||
TERM: xterm
|
||||
MAKEFLAGS: -j 10
|
||||
|
||||
- base_python: &base_python
|
||||
docker:
|
||||
- image: circleci/python:3.6
|
||||
environment:
|
||||
TERM: xterm
|
||||
|
||||
- base_node_latest: &base_node_latest
|
||||
docker:
|
||||
- image: circleci/node
|
||||
environment:
|
||||
TERM: xterm
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Workflow Templates
|
||||
@ -310,10 +387,7 @@ defaults:
|
||||
jobs:
|
||||
|
||||
chk_spelling:
|
||||
docker:
|
||||
- image: circleci/python:3.6
|
||||
environment:
|
||||
TERM: xterm
|
||||
<<: *base_python
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
@ -325,12 +399,10 @@ jobs:
|
||||
- run:
|
||||
name: Check spelling
|
||||
command: ~/.local/bin/codespell -S "*.enc,.git,Dockerfile*" -I ./scripts/codespell_whitelist.txt
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
chk_docs_examples:
|
||||
docker:
|
||||
- image: circleci/node
|
||||
environment:
|
||||
TERM: xterm
|
||||
<<: *base_node_latest
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
@ -341,10 +413,10 @@ jobs:
|
||||
- run:
|
||||
name: Test Docs examples
|
||||
command: ./test/docsCodeStyle.sh
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
chk_coding_style:
|
||||
docker:
|
||||
- image: buildpack-deps:focal
|
||||
<<: *base_buildpack_focal
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
@ -359,19 +431,19 @@ jobs:
|
||||
- run:
|
||||
name: Check for broken symlinks
|
||||
command: ./scripts/check_symlinks.sh
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
chk_errorcodes:
|
||||
docker:
|
||||
- image: circleci/python:3.6
|
||||
<<: *base_python
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Check for error codes
|
||||
command: ./scripts/error_codes.py --check
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
chk_pylint:
|
||||
docker:
|
||||
- image: buildpack-deps:focal
|
||||
<<: *base_buildpack_focal
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
@ -384,10 +456,10 @@ jobs:
|
||||
- run:
|
||||
name: Linting Python Scripts
|
||||
command: ./scripts/pylint_all.py
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
chk_antlr_grammar:
|
||||
docker:
|
||||
- image: buildpack-deps:focal
|
||||
<<: *base_buildpack_focal
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
@ -396,12 +468,10 @@ jobs:
|
||||
- run:
|
||||
name: Run tests
|
||||
command: ./scripts/test_antlr_grammar.sh
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
chk_buglist:
|
||||
docker:
|
||||
- image: circleci/node
|
||||
environment:
|
||||
TERM: xterm
|
||||
<<: *base_node_latest
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
@ -413,12 +483,10 @@ jobs:
|
||||
- run:
|
||||
name: Test buglist
|
||||
command: ./test/buglistTests.js
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
chk_proofs:
|
||||
docker:
|
||||
- image: buildpack-deps:latest
|
||||
environment:
|
||||
TERM: xterm
|
||||
<<: *base_buildpack_latest
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
@ -428,42 +496,36 @@ jobs:
|
||||
apt-get -qy install python3-pip
|
||||
pip3 install --user z3-solver
|
||||
- run: *run_proofs
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
chk_docs_pragma_min_version:
|
||||
docker:
|
||||
- image: << pipeline.parameters.ubuntu-2004-docker-image >>
|
||||
environment:
|
||||
TERM: xterm
|
||||
<<: *base_ubuntu2004
|
||||
steps:
|
||||
- checkout
|
||||
- run: *run_docs_pragma_min_version
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
t_pyscripts_ubu:
|
||||
docker:
|
||||
- image: << pipeline.parameters.ubuntu-2004-docker-image >>
|
||||
t_ubu_pyscripts:
|
||||
<<: *base_ubuntu2004
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Python unit tests
|
||||
command: python3 test/pyscriptTests.py
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
t_pyscripts_win:
|
||||
executor:
|
||||
name: win/default
|
||||
shell: powershell.exe
|
||||
t_win_pyscripts:
|
||||
<<: *base_win_powershell
|
||||
steps:
|
||||
- run: git config --global core.autocrlf false
|
||||
- checkout
|
||||
- run:
|
||||
name: Python unit tests
|
||||
command: python.exe test/pyscriptTests.py
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
b_ubu: &b_ubu
|
||||
resource_class: xlarge
|
||||
docker:
|
||||
- image: << pipeline.parameters.ubuntu-2004-docker-image >>
|
||||
environment:
|
||||
MAKEFLAGS: -j 10
|
||||
<<: *base_ubuntu2004_xlarge
|
||||
steps:
|
||||
- checkout
|
||||
- run: *run_build
|
||||
@ -471,10 +533,11 @@ jobs:
|
||||
- store_artifacts: *artifact_solidity_upgrade
|
||||
- store_artifacts: *artifact_yul_phaser
|
||||
- persist_to_workspace: *artifacts_executables
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
# x64 ASAN build, for testing for memory related bugs
|
||||
b_ubu_asan: &b_ubu_asan
|
||||
<<: *b_ubu
|
||||
<<: *base_ubuntu2004_xlarge
|
||||
environment:
|
||||
CMAKE_OPTIONS: -DSANITIZE=address
|
||||
MAKEFLAGS: -j 10
|
||||
@ -484,24 +547,19 @@ jobs:
|
||||
- run: *run_build
|
||||
- store_artifacts: *artifacts_solc
|
||||
- persist_to_workspace: *artifacts_executables
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
b_ubu_clang: &b_ubu_clang
|
||||
resource_class: xlarge
|
||||
docker:
|
||||
- image: << pipeline.parameters.ubuntu-2004-clang-docker-image >>
|
||||
environment:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
MAKEFLAGS: -j 10
|
||||
<<: *base_ubuntu2004_clang_xlarge
|
||||
steps:
|
||||
- checkout
|
||||
- run: *run_build
|
||||
- store_artifacts: *artifacts_solc
|
||||
- persist_to_workspace: *artifacts_executables
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
b_ubu_asan_clang: &b_ubu_asan_clang
|
||||
docker:
|
||||
- image: << pipeline.parameters.ubuntu-2004-clang-docker-image >>
|
||||
<<: *base_ubuntu2004_clang
|
||||
environment:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
@ -512,10 +570,10 @@ jobs:
|
||||
- run: *run_build
|
||||
- store_artifacts: *artifacts_solc
|
||||
- persist_to_workspace: *artifacts_executables
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
b_ubu_ubsan_clang: &b_ubu_ubsan_clang
|
||||
docker:
|
||||
- image: << pipeline.parameters.ubuntu-2004-clang-docker-image >>
|
||||
<<: *base_ubuntu2004_clang
|
||||
environment:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
@ -524,9 +582,9 @@ jobs:
|
||||
steps:
|
||||
- checkout
|
||||
- run: *run_build
|
||||
- gitter_notify_failure
|
||||
- store_artifacts: *artifacts_solc
|
||||
- persist_to_workspace: *artifacts_executables
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
b_ubu_release: &b_ubu_release
|
||||
<<: *b_ubu
|
||||
@ -535,7 +593,7 @@ jobs:
|
||||
MAKEFLAGS: -j 10
|
||||
|
||||
b_ubu_static:
|
||||
<<: *b_ubu
|
||||
<<: *base_ubuntu2004_xlarge
|
||||
environment:
|
||||
MAKEFLAGS: -j 10
|
||||
CMAKE_OPTIONS: -DCMAKE_BUILD_TYPE=Release -DUSE_Z3_DLOPEN=ON -DUSE_CVC4=OFF -DSOLC_STATIC_STDLIBS=ON
|
||||
@ -546,9 +604,10 @@ jobs:
|
||||
name: strip binary
|
||||
command: strip build/solc/solc
|
||||
- store_artifacts: *artifacts_solc
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
b_ubu_codecov:
|
||||
<<: *b_ubu
|
||||
<<: *base_ubuntu2004_xlarge
|
||||
environment:
|
||||
COVERAGE: ON
|
||||
CMAKE_BUILD_TYPE: Debug
|
||||
@ -557,9 +616,11 @@ jobs:
|
||||
- checkout
|
||||
- run: *run_build
|
||||
- persist_to_workspace: *artifacts_executables
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
t_ubu_codecov:
|
||||
<<: *test_ubuntu2004
|
||||
<<: *base_ubuntu2004
|
||||
parallelism: 6
|
||||
environment:
|
||||
EVM: << pipeline.parameters.evm-version >>
|
||||
OPTIMIZE: 1
|
||||
@ -578,11 +639,12 @@ jobs:
|
||||
name: "Coverage: All"
|
||||
command: codecov --flags all --gcov-root build
|
||||
- store_artifacts: *artifacts_test_results
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
# Builds in C++20 mode and uses debug build in order to speed up.
|
||||
# Do *NOT* store any artifacts or workspace as we don't run tests on this build.
|
||||
b_ubu_cxx20:
|
||||
<<: *b_ubu
|
||||
<<: *base_ubuntu2004_xlarge
|
||||
environment:
|
||||
CMAKE_BUILD_TYPE: Debug
|
||||
CMAKE_OPTIONS: -DCMAKE_CXX_STANDARD=20 -DUSE_CVC4=OFF
|
||||
@ -590,23 +652,19 @@ jobs:
|
||||
steps:
|
||||
- checkout
|
||||
- run: *run_build
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
b_ubu_ossfuzz: &b_ubu_ossfuzz
|
||||
docker:
|
||||
- image: << pipeline.parameters.ubuntu-1604-clang-ossfuzz-docker-image >>
|
||||
environment:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
TERM: xterm
|
||||
MAKEFLAGS: -j 3
|
||||
<<: *base_ubuntu1604_clang
|
||||
steps:
|
||||
- checkout
|
||||
- run: *setup_prerelease_commit_hash
|
||||
- run: *run_build_ossfuzz
|
||||
- persist_to_workspace: *artifacts_executables_ossfuzz
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
t_ubu_ossfuzz: &t_ubu_ossfuzz
|
||||
<<: *test_ubuntu1604_clang
|
||||
<<: *base_ubuntu1604_clang
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
@ -617,14 +675,13 @@ jobs:
|
||||
git clone https://github.com/ethereum/solidity-fuzzing-corpus /tmp/solidity-fuzzing-corpus
|
||||
mkdir -p test_results
|
||||
scripts/regressions.py -o test_results
|
||||
- gitter_notify_failure
|
||||
- gitter_notify_success
|
||||
- store_test_results: *store_test_results
|
||||
- store_artifacts: *artifacts_test_results
|
||||
- gitter_notify_failure_unless_pr
|
||||
- gitter_notify_success_unless_pr
|
||||
|
||||
b_archlinux:
|
||||
docker:
|
||||
- image: archlinux:base
|
||||
<<: *base_archlinux
|
||||
environment:
|
||||
TERM: xterm
|
||||
MAKEFLAGS: -j 3
|
||||
@ -637,10 +694,10 @@ jobs:
|
||||
- run: *run_build
|
||||
- store_artifacts: *artifacts_solc
|
||||
- persist_to_workspace: *artifacts_executables
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
b_osx:
|
||||
macos:
|
||||
xcode: "11.0.0"
|
||||
<<: *base_osx
|
||||
environment:
|
||||
TERM: xterm
|
||||
CMAKE_BUILD_TYPE: Release
|
||||
@ -674,10 +731,10 @@ jobs:
|
||||
- build/solc/solc
|
||||
- build/test/soltest
|
||||
- build/test/tools/solfuzzer
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
t_osx_soltest:
|
||||
macos:
|
||||
xcode: "11.0.0"
|
||||
<<: *base_osx
|
||||
environment:
|
||||
EVM: << pipeline.parameters.evm-version >>
|
||||
OPTIMIZE: 0
|
||||
@ -692,12 +749,10 @@ jobs:
|
||||
- run: *run_soltest
|
||||
- store_test_results: *store_test_results
|
||||
- store_artifacts: *artifacts_test_results
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
t_osx_cli:
|
||||
macos:
|
||||
xcode: "11.0.0"
|
||||
environment:
|
||||
TERM: xterm
|
||||
<<: *base_osx
|
||||
steps:
|
||||
- checkout
|
||||
- restore_cache:
|
||||
@ -707,14 +762,10 @@ jobs:
|
||||
at: .
|
||||
- run: *run_cmdline_tests
|
||||
- store_artifacts: *artifacts_test_results
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
b_ems:
|
||||
resource_class: xlarge
|
||||
docker:
|
||||
- image: << pipeline.parameters.emscripten-docker-image >>
|
||||
environment:
|
||||
MAKEFLAGS: -j 10
|
||||
TERM: xterm
|
||||
<<: *base_ems_xlarge
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
@ -732,10 +783,10 @@ jobs:
|
||||
paths:
|
||||
- soljson.js
|
||||
- version.txt
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
b_docs:
|
||||
docker:
|
||||
- image: << pipeline.parameters.ubuntu-2004-docker-image >>
|
||||
<<: *base_ubuntu2004
|
||||
steps:
|
||||
- checkout
|
||||
- run: *setup_prerelease_commit_hash
|
||||
@ -745,32 +796,33 @@ jobs:
|
||||
- store_artifacts:
|
||||
path: docs/_build/html/
|
||||
destination: docs-html
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
t_ubu_soltest: &t_ubu_soltest
|
||||
<<: *test_ubuntu2004
|
||||
t_ubu_soltest_all: &t_ubu_soltest_all
|
||||
<<: *base_ubuntu2004
|
||||
parallelism: 6
|
||||
<<: *steps_soltest_all
|
||||
|
||||
t_archlinux_soltest: &t_archlinux_soltest
|
||||
docker:
|
||||
- image: archlinux:base
|
||||
environment:
|
||||
EVM: << pipeline.parameters.evm-version >>
|
||||
OPTIMIZE: 0
|
||||
TERM: xterm
|
||||
# For Archlinux we do not have prebuilt docker images and we would need to build evmone from source,
|
||||
# thus we forgo semantics tests to speed things up.
|
||||
SOLTEST_FLAGS: --no-semantic-tests
|
||||
steps:
|
||||
- run:
|
||||
name: Install runtime dependencies
|
||||
command: |
|
||||
pacman --noconfirm -Syu --noprogressbar --needed base-devel boost cmake z3 cvc4 git openssh tar
|
||||
- when:
|
||||
condition: true
|
||||
<<: *steps_soltest
|
||||
<<: *base_archlinux
|
||||
environment:
|
||||
EVM: << pipeline.parameters.evm-version >>
|
||||
OPTIMIZE: 0
|
||||
TERM: xterm
|
||||
# For Archlinux we do not have prebuilt docker images and we would need to build evmone from source,
|
||||
# thus we forgo semantics tests to speed things up.
|
||||
SOLTEST_FLAGS: --no-semantic-tests
|
||||
steps:
|
||||
- run:
|
||||
name: Install runtime dependencies
|
||||
command: |
|
||||
pacman --noconfirm -Syu --noprogressbar --needed base-devel boost cmake z3 cvc4 git openssh tar
|
||||
- when:
|
||||
condition: true
|
||||
<<: *steps_soltest
|
||||
|
||||
t_ubu_soltest_enforce_yul: &t_ubu_soltest_enforce_yul
|
||||
docker:
|
||||
- image: << pipeline.parameters.ubuntu-2004-docker-image >>
|
||||
<<: *base_ubuntu2004
|
||||
environment:
|
||||
EVM: << pipeline.parameters.evm-version >>
|
||||
SOLTEST_FLAGS: --enforce-via-yul
|
||||
@ -778,74 +830,63 @@ jobs:
|
||||
TERM: xterm
|
||||
<<: *steps_soltest
|
||||
|
||||
|
||||
t_ubu_clang_soltest: &t_ubu_clang_soltest
|
||||
<<: *test_ubuntu2004_clang
|
||||
<<: *base_ubuntu2004_clang
|
||||
environment:
|
||||
EVM: << pipeline.parameters.evm-version >>
|
||||
OPTIMIZE: 0
|
||||
<<: *steps_soltest
|
||||
|
||||
t_ubu_release_soltest: &t_ubu_release_soltest
|
||||
<<: *t_ubu_soltest
|
||||
t_ubu_release_soltest_all: &t_ubu_release_soltest_all
|
||||
# NOTE: This definition is identical to t_ubu_soltest_all but in the workflow we make it depend on
|
||||
# a different job (b_ubu_release) so the workspace it attaches contains a different executable.
|
||||
<<: *t_ubu_soltest_all
|
||||
|
||||
t_ubu_cli: &t_ubu_cli
|
||||
docker:
|
||||
- image: << pipeline.parameters.ubuntu-2004-docker-image >>
|
||||
environment:
|
||||
TERM: xterm
|
||||
<<: *base_ubuntu2004
|
||||
<<: *steps_cmdline_tests
|
||||
|
||||
t_ubu_release_cli: &t_ubu_release_cli
|
||||
<<: *t_ubu_cli
|
||||
|
||||
t_ubu_asan_cli:
|
||||
<<: *t_ubu_cli
|
||||
<<: *base_ubuntu2004
|
||||
environment:
|
||||
TERM: xterm
|
||||
ASAN_OPTIONS: check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2
|
||||
<<: *steps_cmdline_tests
|
||||
|
||||
t_ubu_asan:
|
||||
<<: *test_asan
|
||||
t_ubu_asan_soltest:
|
||||
<<: *base_ubuntu2004
|
||||
parallelism: 6
|
||||
environment:
|
||||
EVM: << pipeline.parameters.evm-version >>
|
||||
OPTIMIZE: 0
|
||||
SOLTEST_FLAGS: --no-smt
|
||||
ASAN_OPTIONS: check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2
|
||||
<<: *steps_soltest
|
||||
|
||||
t_ubu_asan_clang:
|
||||
<<: *test_ubuntu2004_clang
|
||||
t_ubu_asan_clang_soltest:
|
||||
<<: *base_ubuntu2004_clang
|
||||
environment:
|
||||
EVM: << pipeline.parameters.evm-version >>
|
||||
OPTIMIZE: 0
|
||||
SOLTEST_FLAGS: --no-smt
|
||||
ASAN_OPTIONS: check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2
|
||||
<<: *steps_soltest
|
||||
|
||||
t_ubu_ubsan_clang:
|
||||
t_ubu_ubsan_clang_soltest:
|
||||
<<: *base_ubuntu2004_clang
|
||||
environment:
|
||||
EVM: << pipeline.parameters.evm-version >>
|
||||
docker:
|
||||
- image: << pipeline.parameters.ubuntu-2004-clang-docker-image >>
|
||||
steps:
|
||||
- when:
|
||||
condition: true
|
||||
<<: *steps_soltest
|
||||
- gitter_notify_failure
|
||||
<<: *steps_soltest
|
||||
|
||||
t_ubu_ubsan_clang_cli:
|
||||
docker:
|
||||
- image: << pipeline.parameters.ubuntu-2004-clang-docker-image >>
|
||||
steps:
|
||||
- when:
|
||||
condition: true
|
||||
<<: *steps_cmdline_tests
|
||||
- gitter_notify_failure
|
||||
<<: *base_ubuntu2004_clang
|
||||
<<: *steps_cmdline_tests
|
||||
|
||||
t_ems_solcjs:
|
||||
docker:
|
||||
- image: << pipeline.parameters.ubuntu-2004-docker-image >>
|
||||
environment:
|
||||
TERM: xterm
|
||||
<<: *base_ubuntu2004
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
@ -862,10 +903,10 @@ jobs:
|
||||
node --version
|
||||
npm --version
|
||||
test/externalTests/solc-js/solc-js.sh /tmp/workspace/soljson.js $(cat /tmp/workspace/version.txt)
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
t_ems_ext_hardhat:
|
||||
docker:
|
||||
- image: circleci/node
|
||||
<<: *base_node_latest
|
||||
environment:
|
||||
TERM: xterm
|
||||
HARDHAT_TESTS_SOLC_PATH: /tmp/workspace/soljson.js
|
||||
@ -888,6 +929,7 @@ jobs:
|
||||
# NOTE: This is expected to work without running `yarn build` first.
|
||||
cd hardhat/packages/hardhat-core
|
||||
yarn test
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
t_ems_ext:
|
||||
parameters:
|
||||
@ -899,9 +941,6 @@ jobs:
|
||||
nodejs_version:
|
||||
type: integer
|
||||
default: 14
|
||||
gitter_notify:
|
||||
type: boolean
|
||||
default: no
|
||||
docker:
|
||||
- image: circleci/node:<<parameters.nodejs_version>>
|
||||
environment:
|
||||
@ -920,16 +959,10 @@ jobs:
|
||||
name: External <<parameters.project>> tests
|
||||
command: |
|
||||
test/externalTests/<<parameters.project>>.sh /tmp/workspace/soljson.js
|
||||
- when:
|
||||
condition: <<parameters.gitter_notify>>
|
||||
steps:
|
||||
- gitter_notify_failure
|
||||
- gitter_notify_success
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
b_win: &b_win
|
||||
executor:
|
||||
name: win/default
|
||||
shell: powershell.exe
|
||||
<<: *base_win_powershell
|
||||
steps:
|
||||
# NOTE: Not disabling git's core.autocrlf here because we want to build using the typical Windows config.
|
||||
- checkout
|
||||
@ -957,16 +990,15 @@ jobs:
|
||||
paths:
|
||||
- .\solc\*\solc.exe
|
||||
- .\test\*\soltest.exe
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
b_win_release:
|
||||
<<: *b_win
|
||||
environment:
|
||||
FORCE_RELEASE: ON
|
||||
|
||||
t_win: &t_win
|
||||
executor:
|
||||
name: win/default
|
||||
shell: powershell.exe
|
||||
t_win_soltest: &t_win_soltest
|
||||
<<: *base_win_powershell
|
||||
steps:
|
||||
# NOTE: Git's default core.autocrlf is fine for running soltest. We get additional coverage
|
||||
# for files using CRLF that way.
|
||||
@ -981,13 +1013,13 @@ jobs:
|
||||
command: .circleci/soltest.ps1
|
||||
- store_test_results: *store_test_results
|
||||
- store_artifacts: *artifacts_test_results
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
t_win_release:
|
||||
<<: *t_win
|
||||
t_win_release_soltest:
|
||||
<<: *t_win_soltest
|
||||
|
||||
b_bytecode_ubu:
|
||||
docker:
|
||||
- image: << pipeline.parameters.ubuntu-2004-docker-image >>
|
||||
<<: *base_ubuntu2004
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
@ -1005,12 +1037,10 @@ jobs:
|
||||
paths:
|
||||
- bytecode-report-ubuntu-json.txt
|
||||
- bytecode-report-ubuntu-cli.txt
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
b_bytecode_osx:
|
||||
macos:
|
||||
xcode: "11.0.0"
|
||||
environment:
|
||||
TERM: xterm
|
||||
<<: *base_osx
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
@ -1028,11 +1058,10 @@ jobs:
|
||||
paths:
|
||||
- bytecode-report-osx-json.txt
|
||||
- bytecode-report-osx-cli.txt
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
b_bytecode_win:
|
||||
executor:
|
||||
name: win/default
|
||||
shell: cmd.exe
|
||||
<<: *base_win_cmd
|
||||
steps:
|
||||
# NOTE: For bytecode generation we need the input files to be byte-for-byte identical on all
|
||||
# platforms so line ending conversions must absolutely be disabled.
|
||||
@ -1053,10 +1082,10 @@ jobs:
|
||||
paths:
|
||||
- bytecode-report-windows-json.txt
|
||||
- bytecode-report-windows-cli.txt
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
b_bytecode_ems:
|
||||
docker:
|
||||
- image: circleci/node:16
|
||||
<<: *base_node_latest
|
||||
environment:
|
||||
SOLC_EMSCRIPTEN: "On"
|
||||
steps:
|
||||
@ -1070,10 +1099,10 @@ jobs:
|
||||
root: .
|
||||
paths:
|
||||
- bytecode-report-emscripten.txt
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
t_bytecode_compare:
|
||||
docker:
|
||||
- image: << pipeline.parameters.ubuntu-2004-docker-image >>
|
||||
<<: *base_ubuntu2004
|
||||
environment:
|
||||
REPORT_FILES: |
|
||||
bytecode-report-emscripten.txt
|
||||
@ -1107,6 +1136,7 @@ jobs:
|
||||
# NOTE: store_artifacts does not support the 'when' attribute.
|
||||
# Fortunately when the artifact does not exist it just says "No artifact files found" and ignores it.
|
||||
path: all-bytecode-reports.zip
|
||||
- gitter_notify_failure_unless_pr
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
@ -1123,8 +1153,8 @@ workflows:
|
||||
- chk_errorcodes: *workflow_trigger_on_tags
|
||||
- chk_antlr_grammar: *workflow_trigger_on_tags
|
||||
- chk_docs_pragma_min_version: *workflow_trigger_on_tags
|
||||
- t_pyscripts_ubu: *workflow_trigger_on_tags
|
||||
- t_pyscripts_win: *workflow_trigger_on_tags
|
||||
- t_ubu_pyscripts: *workflow_trigger_on_tags
|
||||
- t_win_pyscripts: *workflow_trigger_on_tags
|
||||
|
||||
# build-only
|
||||
- b_docs: *workflow_trigger_on_tags
|
||||
@ -1146,7 +1176,7 @@ workflows:
|
||||
# Ubuntu build and tests
|
||||
- b_ubu: *workflow_trigger_on_tags
|
||||
- t_ubu_cli: *workflow_ubuntu2004
|
||||
- t_ubu_soltest: *workflow_ubuntu2004
|
||||
- t_ubu_soltest_all: *workflow_ubuntu2004
|
||||
- t_ubu_soltest_enforce_yul: *workflow_ubuntu2004
|
||||
- b_ubu_clang: *workflow_trigger_on_tags
|
||||
- t_ubu_clang_soltest: *workflow_ubuntu2004_clang
|
||||
@ -1154,7 +1184,7 @@ workflows:
|
||||
# Ubuntu fake release build and tests
|
||||
- b_ubu_release: *workflow_trigger_on_tags
|
||||
- t_ubu_release_cli: *workflow_ubuntu2004_release
|
||||
- t_ubu_release_soltest: *workflow_ubuntu2004_release
|
||||
- t_ubu_release_soltest_all: *workflow_ubuntu2004_release
|
||||
|
||||
# Emscripten build and tests that take 15 minutes or less
|
||||
- b_ems: *workflow_trigger_on_tags
|
||||
@ -1217,8 +1247,8 @@ workflows:
|
||||
# Windows build and tests
|
||||
- b_win: *workflow_trigger_on_tags
|
||||
- b_win_release: *workflow_trigger_on_tags
|
||||
- t_win: *workflow_win
|
||||
- t_win_release: *workflow_win_release
|
||||
- t_win_soltest: *workflow_win
|
||||
- t_win_release_soltest: *workflow_win_release
|
||||
|
||||
# Bytecode comparison:
|
||||
- b_bytecode_ubu:
|
||||
@ -1262,13 +1292,13 @@ workflows:
|
||||
# ASan build and tests
|
||||
- b_ubu_asan: *workflow_trigger_on_tags
|
||||
- b_ubu_asan_clang: *workflow_trigger_on_tags
|
||||
- t_ubu_asan: *workflow_ubuntu2004_asan
|
||||
- t_ubu_asan_clang: *workflow_ubuntu2004_asan_clang
|
||||
- t_ubu_asan_soltest: *workflow_ubuntu2004_asan
|
||||
- t_ubu_asan_clang_soltest: *workflow_ubuntu2004_asan_clang
|
||||
- t_ubu_asan_cli: *workflow_ubuntu2004_asan
|
||||
|
||||
# UBSan build and tests
|
||||
- b_ubu_ubsan_clang: *workflow_trigger_on_tags
|
||||
- t_ubu_ubsan_clang: *workflow_ubuntu2004_ubsan_clang
|
||||
- t_ubu_ubsan_clang_soltest: *workflow_ubuntu2004_ubsan_clang
|
||||
- t_ubu_ubsan_clang_cli: *workflow_ubuntu2004_ubsan_clang
|
||||
|
||||
# Emscripten build and tests that take more than 15 minutes to execute
|
||||
@ -1277,4 +1307,3 @@ workflows:
|
||||
<<: *workflow_emscripten
|
||||
name: t_ems_test_ext_colony
|
||||
project: colony
|
||||
gitter_notify: yes
|
||||
|
@ -46,7 +46,7 @@ source "${REPODIR}/scripts/common.sh"
|
||||
# Test result output directory (CircleCI is reading test results from here)
|
||||
mkdir -p test_results
|
||||
|
||||
# in case we run with ASAN enabled, we must increase stck size.
|
||||
# in case we run with ASAN enabled, we must increase stack size.
|
||||
ulimit -s 16384
|
||||
|
||||
get_logfile_basename() {
|
||||
|
@ -21,7 +21,7 @@ include(EthPolicy)
|
||||
eth_policy()
|
||||
|
||||
# project name and version should be set after cmake_policy CMP0048
|
||||
set(PROJECT_VERSION "0.8.10")
|
||||
set(PROJECT_VERSION "0.8.11")
|
||||
# OSX target needed in order to support std::visit
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14")
|
||||
project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX)
|
||||
|
30
Changelog.md
30
Changelog.md
@ -7,20 +7,36 @@ Breaking changes:
|
||||
* Commandline Interface: Remapping targets are not automatically added to allowed paths.
|
||||
|
||||
|
||||
### 0.8.10 (unreleased)
|
||||
### 0.8.11 (unreleased)
|
||||
|
||||
Language Features:
|
||||
|
||||
|
||||
Compiler Features:
|
||||
|
||||
|
||||
Bugfixes:
|
||||
|
||||
|
||||
### 0.8.10 (2021-11-09)
|
||||
|
||||
Language Features:
|
||||
* Inline Assembly: Support ``.address`` and ``.selector`` on external function pointers to access their address and function selector.
|
||||
|
||||
|
||||
Compiler Features:
|
||||
* Code Generator: Skip existence check for external contract if return data is expected. In this case, the ABI decoder will revert if the contract does not exist.
|
||||
* Commandline Interface: Accept nested brackets in step sequences passed to ``--yul-optimizations``.
|
||||
* Commandline Interface: Add ``--debug-info`` option for selecting how much extra debug information should be included in the produced EVM assembly and Yul code.
|
||||
* Commandline Interface: Support ``--asm``, ``--bin``, ``--ir-optimized``, ``--ewasm`` and ``--ewasm-ir`` output selection options in assembler mode.
|
||||
* Commandline Interface: Use different colors when printing errors, warnings and infos.
|
||||
* JSON AST: Set absolute paths of imports earlier, in the ``parsing`` stage.
|
||||
* SMTChecker: Output values for ``block.*``, ``msg.*`` and ``tx.*`` variables that are present in the called functions.
|
||||
* SMTChecker: Report contract invariants and reentrancy properties. This can be enabled via the CLI option ``--model-checker-invariants`` or the Standard JSON option ``settings.modelChecker.invariants``.
|
||||
* Standard JSON: Accept nested brackets in step sequences passed to ``settings.optimizer.details.yulDetails.optimizerSteps``.
|
||||
* Standard JSON: Add ``settings.debug.debugInfo`` option for selecting how much extra debug information should be included in the produced EVM assembly and Yul code.
|
||||
* Yul EVM Code Transform: Switch to new optimized code transform when compiling via Yul with enabled optimizer.
|
||||
* Yul Optimizer: Take control-flow side-effects of user-defined functions into account in various optimizer steps.
|
||||
|
||||
|
||||
Bugfixes:
|
||||
@ -31,13 +47,23 @@ Bugfixes:
|
||||
* Commandline Interface: Report output selection options unsupported by the selected input mode instead of ignoring them.
|
||||
* Commandline Interface: When linking only accept exact matches for library names passed to the ``--libraries`` option. Library names not prefixed with a file name used to match any library with that name.
|
||||
* SMTChecker: Fix internal error in magic type access (``block``, ``msg``, ``tx``).
|
||||
* TypeChecker: Fix internal error when using user defined value types in public library functions.
|
||||
* SMTChecker: Fix internal error in the CHC engine when passing gas in the function options.
|
||||
* TypeChecker: Fix internal error when using arrays and structs with user defined value types before declaration.
|
||||
* TypeChecker: Fix internal error when using user defined value types in public library functions.
|
||||
* TypeChecker: Improved error message for constant variables with (nested) mapping types.
|
||||
* Yul Assembler: Fix internal error when function names are not unique.
|
||||
* Yul IR Generator: Do not output empty switches/if-bodies for empty contracts.
|
||||
|
||||
|
||||
Important Bugfixes in Experimental Features:
|
||||
* Yul IR Generator: Changes to function return variables referenced in modifier invocation arguments were not properly forwarded if there was more than one return variable.
|
||||
|
||||
|
||||
Build System:
|
||||
* Pass linker-only emscripten options only when linking.
|
||||
* Remove obsolete compatibility workaround for emscripten builds.
|
||||
* Update emscripten to version 2.0.33.
|
||||
|
||||
|
||||
### 0.8.9 (2021-09-29)
|
||||
|
||||
|
@ -111,39 +111,41 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
|
||||
# http://stackoverflow.com/questions/21617158/how-to-silence-unused-command-line-argument-error-with-clang-without-disabling-i
|
||||
add_compile_options(-Qunused-arguments)
|
||||
elseif(EMSCRIPTEN)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --memory-init-file 0")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --memory-init-file 0")
|
||||
# Leave only exported symbols as public and aggressively remove others
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -ffunction-sections -fvisibility=hidden")
|
||||
# Optimisation level
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
|
||||
# Re-enable exception catching (optimisations above -O1 disable it)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0")
|
||||
# Remove any code related to exit (such as atexit)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXIT_RUNTIME=0")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXIT_RUNTIME=0")
|
||||
# Remove any code related to filesystem access
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s FILESYSTEM=0")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s FILESYSTEM=0")
|
||||
# Allow memory growth, but disable some optimisations
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ALLOW_MEMORY_GROWTH=1")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1")
|
||||
# Disable eval()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s DYNAMIC_EXECUTION=0")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DYNAMIC_EXECUTION=0")
|
||||
# Disable greedy exception catcher
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NODEJS_CATCH_EXIT=0")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s NODEJS_CATCH_EXIT=0")
|
||||
# Abort if linking results in any undefined symbols
|
||||
# Note: this is on by default in the CMake Emscripten module which we aren't using
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1")
|
||||
# Disallow deprecated emscripten build options.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s STRICT=1")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s STRICT=1")
|
||||
# Export the Emscripten-generated auxiliary methods which are needed by solc-js.
|
||||
# Which methods of libsolc itself are exported is specified in libsolc/CMakeLists.txt.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap','addFunction','removeFunction','UTF8ToString','lengthBytesUTF8','stringToUTF8','setValue']")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap','addFunction','removeFunction','UTF8ToString','lengthBytesUTF8','stringToUTF8','setValue']")
|
||||
# Build for webassembly target.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WASM=1")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s WASM=1")
|
||||
# Set webassembly build to synchronous loading.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WASM_ASYNC_COMPILATION=0")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s WASM_ASYNC_COMPILATION=0")
|
||||
# Output a single js file with the wasm binary embedded as base64 string.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s SINGLE_FILE=1")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s SINGLE_FILE=1")
|
||||
# Allow new functions to be added to the wasm module via addFunction.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ALLOW_TABLE_GROWTH=1")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_TABLE_GROWTH=1")
|
||||
# Disable warnings about not being pure asm.js due to memory growth.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-almost-asm")
|
||||
endif()
|
||||
|
@ -6,9 +6,34 @@ static std::string const otherLicenses{R"(Most of the code is licensed under GPL
|
||||
parts are as follows:
|
||||
|
||||
libkeccak-tiny:
|
||||
The file libsolutil/Keccak256.cpp incorporates libkeccak-tiny.
|
||||
|
||||
A single-file implementation of SHA-3 and SHAKE implemented by David Leon Gil
|
||||
License: CC0, attribution kindly requested. Blame taken too, but not liability.
|
||||
|
||||
picosha2:
|
||||
The file libsolutil/picosha2.h is imported.
|
||||
|
||||
Copyright (C) 2017 okdshin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
jsoncpp:
|
||||
The JsonCpp library's source code, including accompanying documentation,
|
||||
tests and demonstration applications, are licensed under the following
|
||||
|
@ -1544,6 +1544,10 @@
|
||||
],
|
||||
"released": "2021-01-27"
|
||||
},
|
||||
"0.8.10": {
|
||||
"bugs": [],
|
||||
"released": "2021-11-09"
|
||||
},
|
||||
"0.8.2": {
|
||||
"bugs": [
|
||||
"SignedImmutables",
|
||||
|
@ -91,7 +91,7 @@ else:
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = ['_build', 'contracts', 'types', 'examples', 'grammar', 'ir']
|
||||
exclude_patterns = ['_build', 'contracts', 'types', 'examples', 'grammar']
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
|
@ -222,6 +222,9 @@ Pure Functions
|
||||
--------------
|
||||
|
||||
Functions can be declared ``pure`` in which case they promise not to read from or modify the state.
|
||||
In particular, it should be possible to evaluate a ``pure`` function at compile-time given
|
||||
only its inputs and ``msg.data``, but without any knowledge of the current blockchain state.
|
||||
This means that reading from ``immutable`` variables can be a non-pure operation.
|
||||
|
||||
.. note::
|
||||
If the compiler's EVM target is Byzantium or newer (default) the opcode ``STATICCALL`` is used,
|
||||
|
@ -114,10 +114,19 @@ otherwise, the ``value`` option would not be available.
|
||||
Due to the fact that the EVM considers a call to a non-existing contract to
|
||||
always succeed, Solidity uses the ``extcodesize`` opcode to check that
|
||||
the contract that is about to be called actually exists (it contains code)
|
||||
and causes an exception if it does not.
|
||||
and causes an exception if it does not. This check is skipped if the return
|
||||
data will be decoded after the call and thus the ABI decoder will catch the
|
||||
case of a non-existing contract.
|
||||
|
||||
Note that this check is not performed in case of :ref:`low-level calls <address_related>` which
|
||||
operate on addresses rather than contract instances.
|
||||
|
||||
.. note::
|
||||
Be careful when using high-level calls to
|
||||
:ref:`precompiled contracts <precompiledContracts>`,
|
||||
since the compiler considers them non-existing according to the
|
||||
above logic even though they execute code and can return data.
|
||||
|
||||
Function calls also cause exceptions if the called contract itself
|
||||
throws an exception or goes out of gas.
|
||||
|
||||
|
@ -21,7 +21,7 @@ version of Solidity. Apart from exceptional cases, only the latest version recei
|
||||
`security fixes <https://github.com/ethereum/solidity/security/policy#supported-versions>`_.
|
||||
Furthermore, breaking changes as well as
|
||||
new features are introduced regularly. We currently use
|
||||
a 0.x version number `to indicate this fast pace of change <https://semver.org/#spec-item-4>`_.
|
||||
a 0.y.z version number `to indicate this fast pace of change <https://semver.org/#spec-item-4>`_.
|
||||
|
||||
.. warning::
|
||||
|
||||
@ -136,6 +136,7 @@ Contents
|
||||
|
||||
using-the-compiler.rst
|
||||
analysing-compilation-output.rst
|
||||
ir-breaking-changes.rst
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
@ -411,24 +411,13 @@ in Visual Studio 2019 Build Tools or Visual Studio 2019:
|
||||
.. _Visual Studio 2019: https://www.visualstudio.com/vs/
|
||||
.. _Visual Studio 2019 Build Tools: https://www.visualstudio.com/downloads/#build-tools-for-visual-studio-2019
|
||||
|
||||
Dependencies Helper Script
|
||||
--------------------------
|
||||
|
||||
We have a helper script which you can use to install all required external dependencies
|
||||
on macOS, Windows and on numerous Linux distros.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
./scripts/install_deps.sh
|
||||
|
||||
Or, on Windows:
|
||||
We have a helper script which you can use to install all required external dependencies:
|
||||
|
||||
.. code-block:: bat
|
||||
|
||||
scripts\install_deps.ps1
|
||||
|
||||
Note that the latter command will install ``boost`` and ``cmake`` to the ``deps`` subdirectory, while the former command
|
||||
will attempt to install the dependencies globally.
|
||||
This will install ``boost`` and ``cmake`` to the ``deps`` subdirectory.
|
||||
|
||||
Clone the Repository
|
||||
--------------------
|
||||
|
@ -429,11 +429,9 @@ is transformed to
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
{
|
||||
Init...
|
||||
for {} C { Post... } {
|
||||
Body...
|
||||
}
|
||||
Init...
|
||||
for {} C { Post... } {
|
||||
Body...
|
||||
}
|
||||
|
||||
This eases the rest of the optimization process because we can ignore
|
||||
|
@ -565,3 +565,24 @@ contracts, the Ether is forever lost.
|
||||
If you want to deactivate your contracts, you should instead **disable** them
|
||||
by changing some internal state which causes all functions to revert. This
|
||||
makes it impossible to use the contract, as it returns Ether immediately.
|
||||
|
||||
|
||||
.. index:: ! precompiled contracts, ! precompiles, ! contract;precompiled
|
||||
|
||||
.. _precompiledContracts:
|
||||
|
||||
Precompiled Contracts
|
||||
=====================
|
||||
|
||||
There is a small set of contract addresses that are special:
|
||||
The address range between ``1`` and (including) ``8`` contains
|
||||
"precompiled contracts" that can be called as any other contract
|
||||
but their behaviour (and their gas consumption) is not defined
|
||||
by EVM code stored at that address (they do not contain code)
|
||||
but instead is implemented in the EVM execution environment itself.
|
||||
|
||||
Different EVM-compatible chains might use a different set of
|
||||
precompiled contracts. It might also be possible that new
|
||||
precompiled contracts are added to the Ethereum main chain in the future,
|
||||
but you can reasonabyly expect them to always be in the range between
|
||||
``1`` and ``0xffff`` (inclusive).
|
@ -5,8 +5,26 @@
|
||||
Solidity IR-based Codegen Changes
|
||||
*********************************
|
||||
|
||||
This section highlights the main differences between the old and the IR-based codegen,
|
||||
along with the reasoning behind the changes and how to update affected code.
|
||||
Solidity can generate EVM bytecode in two different ways:
|
||||
Either directly from Solidity to EVM opcodes ("old codegen") or through
|
||||
an intermediate representation ("IR") in Yul ("new codegen" or "IR-based codegen").
|
||||
|
||||
The IR-based code generator was introduced with an aim to not only allow
|
||||
code generation to be more transparent and auditable but also
|
||||
to enable more powerful optimization passes that span across functions.
|
||||
|
||||
Currently, the IR-based code generator is still marked experimental,
|
||||
but it supports all language features and has received a lot of testing,
|
||||
so we consider it almost ready for production use.
|
||||
|
||||
You can enable it on the command line using ``--experimental-via-ir``
|
||||
or with the option ``{"viaIR": true}`` in standard-json and we
|
||||
encourage everyone to try it out!
|
||||
|
||||
For several reasons, there are tiny semantic differences between the old
|
||||
and the IR-based code generator, mostly in areas where we would not
|
||||
expect people to rely on this behaviour anyway.
|
||||
This section highlights the main differences between the old and the IR-based codegen.
|
||||
|
||||
Semantic Only Changes
|
||||
=====================
|
||||
@ -14,8 +32,13 @@ Semantic Only Changes
|
||||
This section lists the changes that are semantic-only, thus potentially
|
||||
hiding new and different behavior in existing code.
|
||||
|
||||
- When storage structs are deleted, every storage slot that contains a member of the struct is set to zero entirely. Formally, padding space was left untouched.
|
||||
Consequently, if the padding space within a struct is used to store data (e.g. in the context of a contract upgrade), you have to be aware that ``delete`` will now also clear the added member (while it wouldn't have been cleared in the past).
|
||||
- When storage structs are deleted, every storage slot that contains
|
||||
a member of the struct is set to zero entirely. Formerly, padding space
|
||||
was left untouched.
|
||||
Consequently, if the padding space within a struct is used to store data
|
||||
(e.g. in the context of a contract upgrade), you have to be aware that
|
||||
``delete`` will now also clear the added member (while it wouldn't
|
||||
have been cleared in the past).
|
||||
|
||||
.. code-block:: solidity
|
||||
|
||||
@ -132,7 +155,10 @@ This causes differences in some contracts, for example:
|
||||
Previously, ``y`` would be set to 0. This is due to the fact that we would first initialize state variables: First, ``x`` is set to 0, and when initializing ``y``, ``f()`` would return 0 causing ``y`` to be 0 as well.
|
||||
With the new rules, ``y`` will be set to 42. We first initialize ``x`` to 0, then call A's constructor which sets ``x`` to 42. Finally, when initializing ``y``, ``f()`` returns 42 causing ``y`` to be 42.
|
||||
|
||||
- Copying ``bytes`` arrays from memory to storage is implemented in a different way. The old code generator always copies full words, while the new one cuts the byte array after its end. The old behaviour can lead to dirty data being copied after the end of the array (but still in the same storage slot).
|
||||
- Copying ``bytes`` arrays from memory to storage is implemented in a different way.
|
||||
The old code generator always copies full words, while the new one cuts the byte
|
||||
array after its end. The old behaviour can lead to dirty data being copied after
|
||||
the end of the array (but still in the same storage slot).
|
||||
This causes differences in some contracts, for example:
|
||||
|
||||
.. code-block:: solidity
|
||||
@ -155,8 +181,10 @@ This causes differences in some contracts, for example:
|
||||
}
|
||||
}
|
||||
|
||||
Previously ``f()`` would return ``0x6465616462656566313564656164000000000000000000000000000000000010`` (it has correct length, and correct first 8 elements, but then it contains dirty data which was set via assembly).
|
||||
Now it is returning ``0x6465616462656566000000000000000000000000000000000000000000000010`` (it has correct length, and correct elements, but does not contain superfluous data).
|
||||
Previously ``f()`` would return ``0x6465616462656566313564656164000000000000000000000000000000000010``
|
||||
(it has correct length, and correct first 8 elements, but then it contains dirty data which was set via assembly).
|
||||
Now it is returning ``0x6465616462656566000000000000000000000000000000000000000000000010`` (it has
|
||||
correct length, and correct elements, but does not contain superfluous data).
|
||||
|
||||
.. index:: ! evaluation order; expression
|
||||
|
||||
@ -183,7 +211,8 @@ This causes differences in some contracts, for example:
|
||||
|
||||
.. index:: ! evaluation order; function arguments
|
||||
|
||||
On the other hand, function argument expressions are evaluated in the same order by both code generators with the exception of the global functions ``addmod`` and ``mulmod``.
|
||||
On the other hand, function argument expressions are evaluated in the same order
|
||||
by both code generators with the exception of the global functions ``addmod`` and ``mulmod``.
|
||||
For example:
|
||||
|
||||
.. code-block:: solidity
|
||||
@ -227,11 +256,15 @@ This causes differences in some contracts, for example:
|
||||
- Old code generator: ``aMod = 0`` and ``mMod = 2``
|
||||
- New code generator: ``aMod = 4`` and ``mMod = 0``
|
||||
|
||||
- The new code generator imposes a hard limit of ``type(uint64).max`` (``0xffffffffffffffff``) for the free memory pointer. Allocations that would increase its value beyond this limit revert. The old code generator does not have this limit.
|
||||
- The new code generator imposes a hard limit of ``type(uint64).max``
|
||||
(``0xffffffffffffffff``) for the free memory pointer. Allocations that would
|
||||
increase its value beyond this limit revert. The old code generator does not
|
||||
have this limit.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: solidity
|
||||
:force:
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
pragma solidity >0.8.0;
|
||||
@ -264,7 +297,7 @@ The old code generator uses code offsets or tags for values of internal function
|
||||
these offsets are different at construction time and after deployment and the values can cross this border via storage.
|
||||
Because of that, both offsets are encoded at construction time into the same value (into different bytes).
|
||||
|
||||
In the new code generator, function pointers use the AST IDs of the functions as values. Since calls via jumps are not possible,
|
||||
In the new code generator, function pointers use internal IDs that are allocated in sequence. Since calls via jumps are not possible,
|
||||
calls through function pointers always have to use an internal dispatch function that uses the ``switch`` statement to select
|
||||
the right function.
|
||||
|
||||
@ -280,6 +313,7 @@ Cleanup
|
||||
|
||||
The old code generator only performs cleanup before an operation whose result could be affected by the values of the dirty bits.
|
||||
The new code generator performs cleanup after any operation that can result in dirty bits.
|
||||
The hope is that the optimizer will be powerful enough to eliminate redundant cleanup operations.
|
||||
|
||||
For example:
|
||||
|
@ -241,7 +241,7 @@ and to send Ether (in units of wei) to a payable address using the ``transfer``
|
||||
.. code-block:: solidity
|
||||
:force:
|
||||
|
||||
address payable x = address(0x123);
|
||||
address payable x = payable(0x123);
|
||||
address myAddress = address(this);
|
||||
if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);
|
||||
|
||||
|
@ -2,7 +2,7 @@ if (EMSCRIPTEN)
|
||||
# Specify which functions to export in soljson.js.
|
||||
# Note that additional Emscripten-generated methods needed by solc-js are
|
||||
# defined to be exported in cmake/EthCompilerSettings.cmake.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXPORTED_FUNCTIONS='[\"_solidity_license\",\"_solidity_version\",\"_solidity_compile\",\"_solidity_alloc\",\"_solidity_free\",\"_solidity_reset\"]'")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORTED_FUNCTIONS='[\"_solidity_license\",\"_solidity_version\",\"_solidity_compile\",\"_solidity_alloc\",\"_solidity_free\",\"_solidity_reset\"]'")
|
||||
add_executable(soljson libsolc.cpp libsolc.h)
|
||||
target_link_libraries(soljson PRIVATE solidity)
|
||||
else()
|
||||
|
@ -2630,9 +2630,21 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
||||
// Check the target contract exists (has code) for non-low-level calls.
|
||||
if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall)
|
||||
{
|
||||
m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO;
|
||||
m_context.appendConditionalRevert(false, "Target contract does not contain code");
|
||||
existenceChecked = true;
|
||||
size_t encodedHeadSize = 0;
|
||||
for (auto const& t: returnTypes)
|
||||
encodedHeadSize += t->decodingType()->calldataHeadSize();
|
||||
// We do not need to check extcodesize if we expect return data, since if there is no
|
||||
// code, the call will return empty data and the ABI decoder will revert.
|
||||
if (
|
||||
encodedHeadSize == 0 ||
|
||||
!haveReturndatacopy ||
|
||||
m_context.revertStrings() >= RevertStrings::Debug
|
||||
)
|
||||
{
|
||||
m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO;
|
||||
m_context.appendConditionalRevert(false, "Target contract does not contain code");
|
||||
existenceChecked = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (_functionType.gasSet())
|
||||
|
@ -438,7 +438,7 @@ string IRGenerator::generateModifier(
|
||||
for (size_t i = 0; i < retParamsIn.size(); ++i)
|
||||
{
|
||||
retParams.emplace_back(m_context.newYulVariable());
|
||||
assignRetParams += retParams.back() + " := " + retParamsIn[i] + "\n";
|
||||
assignRetParams += retParams.at(i) + " := " + retParamsIn.at(i) + "\n";
|
||||
}
|
||||
t("retParams", joinHumanReadable(retParams));
|
||||
t("assignRetParams", assignRetParams);
|
||||
@ -529,7 +529,7 @@ string IRGenerator::generateFunctionWithModifierInner(FunctionDefinition const&
|
||||
for (size_t i = 0; i < retParams.size(); ++i)
|
||||
{
|
||||
retParamsIn.emplace_back(m_context.newYulVariable());
|
||||
assignRetParams += retParams.back() + " := " + retParamsIn[i] + "\n";
|
||||
assignRetParams += retParams.at(i) + " := " + retParamsIn.at(i) + "\n";
|
||||
}
|
||||
vector<string> params = retParamsIn;
|
||||
for (auto const& varDecl: _function.parameters())
|
||||
@ -1069,9 +1069,6 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
||||
|
||||
string IRGenerator::memoryInit(bool _useMemoryGuard)
|
||||
{
|
||||
// TODO: Remove once we have made sure it is safe, i.e. after "Yul memory objects lite".
|
||||
// Also restore the tests removed in the commit that adds this comment.
|
||||
_useMemoryGuard = false;
|
||||
// This function should be called at the beginning of the EVM call frame
|
||||
// and thus can assume all memory to be zero, including the contents of
|
||||
// the "zero memory area" (the position CompilerUtils::zeroPointer points to).
|
||||
|
@ -2451,8 +2451,10 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
appendCode() << "mstore(add(" << m_utils.allocateUnboundedFunction() << "() , " << to_string(returnInfo.estimatedReturnSize) << "), 0)\n";
|
||||
}
|
||||
|
||||
Whiskers templ(R"(if iszero(extcodesize(<address>)) { <revertNoCode>() }
|
||||
|
||||
Whiskers templ(R"(
|
||||
<?checkExtcodesize>
|
||||
if iszero(extcodesize(<address>)) { <revertNoCode>() }
|
||||
</checkExtcodesize>
|
||||
// storage for arguments and returned data
|
||||
let <pos> := <allocateUnbounded>()
|
||||
mstore(<pos>, <shl28>(<funSel>))
|
||||
@ -2477,6 +2479,18 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
}
|
||||
)");
|
||||
templ("revertNoCode", m_utils.revertReasonIfDebugFunction("Target contract does not contain code"));
|
||||
|
||||
// We do not need to check extcodesize if we expect return data: If there is no
|
||||
// code, the call will return empty data and the ABI decoder will revert.
|
||||
size_t encodedHeadSize = 0;
|
||||
for (auto const& t: returnInfo.returnTypes)
|
||||
encodedHeadSize += t->decodingType()->calldataHeadSize();
|
||||
bool const checkExtcodesize =
|
||||
encodedHeadSize == 0 ||
|
||||
!m_context.evmVersion().supportsReturndata() ||
|
||||
m_context.revertStrings() >= RevertStrings::Debug;
|
||||
templ("checkExtcodesize", checkExtcodesize);
|
||||
|
||||
templ("pos", m_context.newYulVariable());
|
||||
templ("end", m_context.newYulVariable());
|
||||
if (_functionCall.annotation().tryCall)
|
||||
@ -2532,6 +2546,8 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
|
||||
u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10;
|
||||
if (funType.valueSet())
|
||||
gasNeededByCaller += evmasm::GasCosts::callValueTransferGas;
|
||||
if (!checkExtcodesize)
|
||||
gasNeededByCaller += evmasm::GasCosts::callNewAccountGas; // we never know
|
||||
templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")");
|
||||
}
|
||||
// Order is important here, STATICCALL might overlap with DELEGATECALL.
|
||||
|
@ -791,8 +791,8 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall)
|
||||
valueIndex = i;
|
||||
break;
|
||||
}
|
||||
solAssert(valueIndex, "");
|
||||
state().addBalance(state().thisAddress(), 0 - expr(*callOptions->options().at(*valueIndex)));
|
||||
if (valueIndex)
|
||||
state().addBalance(state().thisAddress(), 0 - expr(*callOptions->options().at(*valueIndex)));
|
||||
}
|
||||
|
||||
auto preCallState = vector<smtutil::Expression>{state().state()} + currentStateVariables();
|
||||
@ -1623,28 +1623,23 @@ void CHC::checkVerificationTargets()
|
||||
// Also, all possible contexts in which an external function can be called has been recorded (m_queryPlaceholders).
|
||||
// Here we combine every context in which an external function can be called with all possible verification conditions
|
||||
// in its call graph. Each such combination forms a unique verification target.
|
||||
vector<CHCVerificationTarget> verificationTargets;
|
||||
map<unsigned, vector<CHCQueryPlaceholder>> targetEntryPoints;
|
||||
for (auto const& [function, placeholders]: m_queryPlaceholders)
|
||||
{
|
||||
auto functionTargets = transactionVerificationTargetsIds(function);
|
||||
for (auto const& placeholder: placeholders)
|
||||
for (unsigned id: functionTargets)
|
||||
{
|
||||
auto const& target = m_verificationTargets.at(id);
|
||||
verificationTargets.push_back(CHCVerificationTarget{
|
||||
{target.type, placeholder.fromPredicate, placeholder.constraints && placeholder.errorExpression == target.errorId},
|
||||
target.errorId,
|
||||
target.errorNode
|
||||
});
|
||||
}
|
||||
targetEntryPoints[id].push_back(placeholder);
|
||||
}
|
||||
|
||||
set<unsigned> checkedErrorIds;
|
||||
for (auto const& target: verificationTargets)
|
||||
for (auto const& [targetId, placeholders]: targetEntryPoints)
|
||||
{
|
||||
string errorType;
|
||||
ErrorId errorReporterId;
|
||||
|
||||
auto const& target = m_verificationTargets.at(targetId);
|
||||
|
||||
if (target.type == VerificationTargetType::PopEmptyArray)
|
||||
{
|
||||
solAssert(dynamic_cast<FunctionCall const*>(target.errorNode), "");
|
||||
@ -1692,7 +1687,7 @@ void CHC::checkVerificationTargets()
|
||||
else
|
||||
solAssert(false, "");
|
||||
|
||||
checkAndReportTarget(target, errorReporterId, errorType + " happens here.", errorType + " might happen here.");
|
||||
checkAndReportTarget(target, placeholders, errorReporterId, errorType + " happens here.", errorType + " might happen here.");
|
||||
checkedErrorIds.insert(target.errorId);
|
||||
}
|
||||
|
||||
@ -1750,7 +1745,7 @@ void CHC::checkVerificationTargets()
|
||||
{
|
||||
set<unsigned> seenErrors;
|
||||
msg += "<errorCode> = 0 -> no errors\n";
|
||||
for (auto const& target: verificationTargets)
|
||||
for (auto const& [id, target]: m_verificationTargets)
|
||||
if (!seenErrors.count(target.errorId))
|
||||
{
|
||||
seenErrors.insert(target.errorId);
|
||||
@ -1785,6 +1780,7 @@ void CHC::checkVerificationTargets()
|
||||
|
||||
void CHC::checkAndReportTarget(
|
||||
CHCVerificationTarget const& _target,
|
||||
vector<CHCQueryPlaceholder> const& _placeholders,
|
||||
ErrorId _errorReporterId,
|
||||
string _satMsg,
|
||||
string _unknownMsg
|
||||
@ -1794,7 +1790,12 @@ void CHC::checkAndReportTarget(
|
||||
return;
|
||||
|
||||
createErrorBlock();
|
||||
connectBlocks(_target.value, error(), _target.constraints);
|
||||
for (auto const& placeholder: _placeholders)
|
||||
connectBlocks(
|
||||
placeholder.fromPredicate,
|
||||
error(),
|
||||
placeholder.constraints && placeholder.errorExpression == _target.errorId
|
||||
);
|
||||
auto const& location = _target.errorNode->location();
|
||||
auto [result, invariant, model] = query(error(), location);
|
||||
if (result == CheckResult::UNSATISFIABLE)
|
||||
|
@ -252,11 +252,13 @@ private:
|
||||
void verificationTargetEncountered(ASTNode const* const _errorNode, VerificationTargetType _type, smtutil::Expression const& _errorCondition);
|
||||
|
||||
void checkVerificationTargets();
|
||||
// Forward declaration. Definition is below.
|
||||
// Forward declarations. Definitions are below.
|
||||
struct CHCVerificationTarget;
|
||||
struct CHCQueryPlaceholder;
|
||||
void checkAssertTarget(ASTNode const* _scope, CHCVerificationTarget const& _target);
|
||||
void checkAndReportTarget(
|
||||
CHCVerificationTarget const& _target,
|
||||
std::vector<CHCQueryPlaceholder> const& _placeholders,
|
||||
langutil::ErrorId _errorReporterId,
|
||||
std::string _satMsg,
|
||||
std::string _unknownMsg = ""
|
||||
|
@ -83,6 +83,7 @@
|
||||
#include <utility>
|
||||
#include <map>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
@ -350,8 +351,22 @@ bool CompilerStack::parse()
|
||||
else
|
||||
{
|
||||
source.ast->annotation().path = path;
|
||||
|
||||
for (auto const& import: ASTNode::filteredNodes<ImportDirective>(source.ast->nodes()))
|
||||
{
|
||||
solAssert(!import->path().empty(), "Import path cannot be empty.");
|
||||
|
||||
// The current value of `path` is the absolute path as seen from this source file.
|
||||
// We first have to apply remappings before we can store the actual absolute path
|
||||
// as seen globally.
|
||||
import->annotation().absolutePath = applyRemapping(util::absolutePath(
|
||||
import->path(),
|
||||
path
|
||||
), path);
|
||||
}
|
||||
|
||||
if (m_stopAfter >= ParsedAndImported)
|
||||
for (auto const& newSource: loadMissingSources(*source.ast, path))
|
||||
for (auto const& newSource: loadMissingSources(*source.ast))
|
||||
{
|
||||
string const& newPath = newSource.first;
|
||||
string const& newContents = newSource.second;
|
||||
@ -1091,7 +1106,7 @@ string const& CompilerStack::Source::ipfsUrl() const
|
||||
return ipfsUrlCached;
|
||||
}
|
||||
|
||||
StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string const& _sourcePath)
|
||||
StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast)
|
||||
{
|
||||
solAssert(m_stackState < ParsedAndImported, "");
|
||||
StringMap newSources;
|
||||
@ -1100,14 +1115,8 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string
|
||||
for (auto const& node: _ast.nodes())
|
||||
if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get()))
|
||||
{
|
||||
solAssert(!import->path().empty(), "Import path cannot be empty.");
|
||||
string const& importPath = *import->annotation().absolutePath;
|
||||
|
||||
string importPath = util::absolutePath(import->path(), _sourcePath);
|
||||
// The current value of `path` is the absolute path as seen from this source file.
|
||||
// We first have to apply remappings before we can store the actual absolute path
|
||||
// as seen globally.
|
||||
importPath = applyRemapping(importPath, _sourcePath);
|
||||
import->annotation().absolutePath = importPath;
|
||||
if (m_sources.count(importPath) || newSources.count(importPath))
|
||||
continue;
|
||||
|
||||
@ -1248,7 +1257,9 @@ void CompilerStack::assemble(
|
||||
m_errorReporter.warning(
|
||||
5574_error,
|
||||
_contract.location(),
|
||||
"Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). "
|
||||
"Contract code size is "s +
|
||||
to_string(compiledContract.runtimeObject.bytecode.size()) +
|
||||
" bytes and exceeds 24576 bytes (a limit introduced in Spurious Dragon). "
|
||||
"This contract may not be deployable on mainnet. "
|
||||
"Consider enabling the optimizer (with a low \"runs\" value!), "
|
||||
"turning off revert strings, or using libraries."
|
||||
|
@ -392,9 +392,9 @@ private:
|
||||
void findAndReportCyclicContractDependencies();
|
||||
|
||||
/// Loads the missing sources from @a _ast (named @a _path) using the callback
|
||||
/// @a m_readFile and stores the absolute paths of all imports in the AST annotations.
|
||||
/// @a m_readFile
|
||||
/// @returns the newly loaded sources.
|
||||
StringMap loadMissingSources(SourceUnit const& _ast, std::string const& _path);
|
||||
StringMap loadMissingSources(SourceUnit const& _ast);
|
||||
std::string applyRemapping(std::string const& _path, std::string const& _context);
|
||||
void resolveImports();
|
||||
|
||||
|
@ -40,6 +40,8 @@ add_library(yul
|
||||
Dialect.cpp
|
||||
Dialect.h
|
||||
Exceptions.h
|
||||
FunctionReferenceResolver.cpp
|
||||
FunctionReferenceResolver.h
|
||||
Object.cpp
|
||||
Object.h
|
||||
ObjectParser.cpp
|
||||
@ -140,8 +142,6 @@ add_library(yul
|
||||
optimiser/FullInliner.h
|
||||
optimiser/FunctionCallFinder.cpp
|
||||
optimiser/FunctionCallFinder.h
|
||||
optimiser/FunctionDefinitionCollector.cpp
|
||||
optimiser/FunctionDefinitionCollector.h
|
||||
optimiser/FunctionGrouper.cpp
|
||||
optimiser/FunctionGrouper.h
|
||||
optimiser/FunctionHoister.cpp
|
||||
|
@ -18,10 +18,9 @@
|
||||
|
||||
#include <libyul/ControlFlowSideEffectsCollector.h>
|
||||
|
||||
#include <libyul/optimiser/FunctionDefinitionCollector.h>
|
||||
|
||||
#include <libyul/AST.h>
|
||||
#include <libyul/Dialect.h>
|
||||
#include <libyul/FunctionReferenceResolver.h>
|
||||
|
||||
#include <libsolutil/Common.h>
|
||||
#include <libsolutil/CommonData.h>
|
||||
@ -37,16 +36,15 @@ using namespace solidity::yul;
|
||||
|
||||
ControlFlowBuilder::ControlFlowBuilder(Block const& _ast)
|
||||
{
|
||||
for (auto const& statement: _ast.statements)
|
||||
if (auto const* function = get_if<FunctionDefinition>(&statement))
|
||||
(*this)(*function);
|
||||
m_currentNode = newNode();
|
||||
(*this)(_ast);
|
||||
}
|
||||
|
||||
void ControlFlowBuilder::operator()(FunctionCall const& _functionCall)
|
||||
{
|
||||
walkVector(_functionCall.arguments | ranges::views::reverse);
|
||||
newConnectedNode();
|
||||
m_currentNode->functionCall = _functionCall.functionName.name;
|
||||
m_currentNode->functionCall = &_functionCall;
|
||||
}
|
||||
|
||||
void ControlFlowBuilder::operator()(If const& _if)
|
||||
@ -80,7 +78,9 @@ void ControlFlowBuilder::operator()(Switch const& _switch)
|
||||
void ControlFlowBuilder::operator()(FunctionDefinition const& _function)
|
||||
{
|
||||
ScopedSaveAndRestore currentNode(m_currentNode, nullptr);
|
||||
yulAssert(!m_leave && !m_break && !m_continue, "Function hoister has not been used.");
|
||||
ScopedSaveAndRestore leave(m_leave, nullptr);
|
||||
ScopedSaveAndRestore _break(m_break, nullptr);
|
||||
ScopedSaveAndRestore _continue(m_continue, nullptr);
|
||||
|
||||
FunctionFlow flow;
|
||||
flow.exit = newNode();
|
||||
@ -92,7 +92,7 @@ void ControlFlowBuilder::operator()(FunctionDefinition const& _function)
|
||||
|
||||
m_currentNode->successors.emplace_back(flow.exit);
|
||||
|
||||
m_functionFlows[_function.name] = move(flow);
|
||||
m_functionFlows[&_function] = move(flow);
|
||||
|
||||
m_leave = nullptr;
|
||||
}
|
||||
@ -166,14 +166,17 @@ ControlFlowSideEffectsCollector::ControlFlowSideEffectsCollector(
|
||||
Block const& _ast
|
||||
):
|
||||
m_dialect(_dialect),
|
||||
m_cfgBuilder(_ast)
|
||||
m_cfgBuilder(_ast),
|
||||
m_functionReferences(FunctionReferenceResolver{_ast}.references())
|
||||
{
|
||||
for (auto&& [name, flow]: m_cfgBuilder.functionFlows())
|
||||
for (auto&& [function, flow]: m_cfgBuilder.functionFlows())
|
||||
{
|
||||
yulAssert(!flow.entry->functionCall);
|
||||
m_processedNodes[name] = {};
|
||||
m_pendingNodes[name].push_front(flow.entry);
|
||||
m_functionSideEffects[name] = {false, false, false};
|
||||
yulAssert(function);
|
||||
m_processedNodes[function] = {};
|
||||
m_pendingNodes[function].push_front(flow.entry);
|
||||
m_functionSideEffects[function] = {false, false, false};
|
||||
m_functionCalls[function] = {};
|
||||
}
|
||||
|
||||
// Process functions while we have progress. For now, we are only interested
|
||||
@ -182,8 +185,8 @@ ControlFlowSideEffectsCollector::ControlFlowSideEffectsCollector(
|
||||
while (progress)
|
||||
{
|
||||
progress = false;
|
||||
for (auto const& functionName: m_pendingNodes | ranges::views::keys)
|
||||
if (processFunction(functionName))
|
||||
for (FunctionDefinition const* function: m_pendingNodes | ranges::views::keys)
|
||||
if (processFunction(*function))
|
||||
progress = true;
|
||||
}
|
||||
|
||||
@ -192,57 +195,64 @@ ControlFlowSideEffectsCollector::ControlFlowSideEffectsCollector(
|
||||
// If we have not set `canContinue` by now, the function's exit
|
||||
// is not reachable.
|
||||
|
||||
for (auto&& [functionName, calls]: m_functionCalls)
|
||||
// Now it is sufficient to handle the reachable function calls (`m_functionCalls`),
|
||||
// we do not have to consider the control-flow graph anymore.
|
||||
for (auto&& [function, calls]: m_functionCalls)
|
||||
{
|
||||
ControlFlowSideEffects& sideEffects = m_functionSideEffects[functionName];
|
||||
auto _visit = [&, visited = std::set<YulString>{}](YulString _function, auto&& _recurse) mutable {
|
||||
if (sideEffects.canTerminate && sideEffects.canRevert)
|
||||
yulAssert(function);
|
||||
ControlFlowSideEffects& functionSideEffects = m_functionSideEffects[function];
|
||||
auto _visit = [&, visited = std::set<FunctionDefinition const*>{}](FunctionDefinition const& _function, auto&& _recurse) mutable {
|
||||
// Worst side-effects already, stop searching.
|
||||
if (functionSideEffects.canTerminate && functionSideEffects.canRevert)
|
||||
return;
|
||||
if (!visited.insert(_function).second)
|
||||
if (!visited.insert(&_function).second)
|
||||
return;
|
||||
|
||||
ControlFlowSideEffects const* calledSideEffects = nullptr;
|
||||
if (BuiltinFunction const* f = _dialect.builtin(_function))
|
||||
calledSideEffects = &f->controlFlowSideEffects;
|
||||
else
|
||||
calledSideEffects = &m_functionSideEffects.at(_function);
|
||||
for (FunctionCall const* call: m_functionCalls.at(&_function))
|
||||
{
|
||||
ControlFlowSideEffects const& calledSideEffects = sideEffects(*call);
|
||||
if (calledSideEffects.canTerminate)
|
||||
functionSideEffects.canTerminate = true;
|
||||
if (calledSideEffects.canRevert)
|
||||
functionSideEffects.canRevert = true;
|
||||
|
||||
if (calledSideEffects->canTerminate)
|
||||
sideEffects.canTerminate = true;
|
||||
if (calledSideEffects->canRevert)
|
||||
sideEffects.canRevert = true;
|
||||
|
||||
set<YulString> emptySet;
|
||||
for (YulString callee: util::valueOrDefault(m_functionCalls, _function, emptySet))
|
||||
_recurse(callee, _recurse);
|
||||
if (m_functionReferences.count(call))
|
||||
_recurse(*m_functionReferences.at(call), _recurse);
|
||||
}
|
||||
};
|
||||
for (auto const& call: calls)
|
||||
_visit(call, _visit);
|
||||
_visit(*function, _visit);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool ControlFlowSideEffectsCollector::processFunction(YulString _name)
|
||||
map<YulString, ControlFlowSideEffects> ControlFlowSideEffectsCollector::functionSideEffectsNamed() const
|
||||
{
|
||||
map<YulString, ControlFlowSideEffects> result;
|
||||
for (auto&& [function, sideEffects]: m_functionSideEffects)
|
||||
yulAssert(result.insert({function->name, sideEffects}).second);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ControlFlowSideEffectsCollector::processFunction(FunctionDefinition const& _function)
|
||||
{
|
||||
bool progress = false;
|
||||
while (ControlFlowNode const* node = nextProcessableNode(_name))
|
||||
while (ControlFlowNode const* node = nextProcessableNode(_function))
|
||||
{
|
||||
if (node == m_cfgBuilder.functionFlows().at(_name).exit)
|
||||
if (node == m_cfgBuilder.functionFlows().at(&_function).exit)
|
||||
{
|
||||
m_functionSideEffects[_name].canContinue = true;
|
||||
m_functionSideEffects[&_function].canContinue = true;
|
||||
return true;
|
||||
}
|
||||
for (ControlFlowNode const* s: node->successors)
|
||||
recordReachabilityAndQueue(_name, s);
|
||||
recordReachabilityAndQueue(_function, s);
|
||||
|
||||
progress = true;
|
||||
}
|
||||
return progress;
|
||||
}
|
||||
|
||||
ControlFlowNode const* ControlFlowSideEffectsCollector::nextProcessableNode(YulString _functionName)
|
||||
ControlFlowNode const* ControlFlowSideEffectsCollector::nextProcessableNode(FunctionDefinition const& _function)
|
||||
{
|
||||
std::list<ControlFlowNode const*>& nodes = m_pendingNodes[_functionName];
|
||||
std::list<ControlFlowNode const*>& nodes = m_pendingNodes[&_function];
|
||||
auto it = ranges::find_if(nodes, [this](ControlFlowNode const* _node) {
|
||||
return !_node->functionCall || sideEffects(*_node->functionCall).canContinue;
|
||||
});
|
||||
@ -254,22 +264,22 @@ ControlFlowNode const* ControlFlowSideEffectsCollector::nextProcessableNode(YulS
|
||||
return node;
|
||||
}
|
||||
|
||||
ControlFlowSideEffects const& ControlFlowSideEffectsCollector::sideEffects(YulString _functionName) const
|
||||
ControlFlowSideEffects const& ControlFlowSideEffectsCollector::sideEffects(FunctionCall const& _call) const
|
||||
{
|
||||
if (auto const* builtin = m_dialect.builtin(_functionName))
|
||||
if (auto const* builtin = m_dialect.builtin(_call.functionName.name))
|
||||
return builtin->controlFlowSideEffects;
|
||||
else
|
||||
return m_functionSideEffects.at(_functionName);
|
||||
return m_functionSideEffects.at(m_functionReferences.at(&_call));
|
||||
}
|
||||
|
||||
void ControlFlowSideEffectsCollector::recordReachabilityAndQueue(
|
||||
YulString _functionName,
|
||||
FunctionDefinition const& _function,
|
||||
ControlFlowNode const* _node
|
||||
)
|
||||
{
|
||||
if (_node->functionCall)
|
||||
m_functionCalls[_functionName].insert(*_node->functionCall);
|
||||
if (m_processedNodes[_functionName].insert(_node).second)
|
||||
m_pendingNodes.at(_functionName).push_front(_node);
|
||||
m_functionCalls[&_function].insert(_node->functionCall);
|
||||
if (m_processedNodes[&_function].insert(_node).second)
|
||||
m_pendingNodes.at(&_function).push_front(_node);
|
||||
}
|
||||
|
||||
|
@ -34,8 +34,8 @@ struct Dialect;
|
||||
struct ControlFlowNode
|
||||
{
|
||||
std::vector<ControlFlowNode const*> successors;
|
||||
/// Name of the called function if the node calls a function.
|
||||
std::optional<YulString> functionCall;
|
||||
/// Function call AST node, if present.
|
||||
FunctionCall const* functionCall = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -56,7 +56,7 @@ public:
|
||||
/// Computes the control-flows of all function defined in the block.
|
||||
/// Assumes the functions are hoisted to the topmost block.
|
||||
explicit ControlFlowBuilder(Block const& _ast);
|
||||
std::map<YulString, FunctionFlow> const& functionFlows() const { return m_functionFlows; }
|
||||
std::map<FunctionDefinition const*, FunctionFlow> const& functionFlows() const { return m_functionFlows; }
|
||||
|
||||
private:
|
||||
using ASTWalker::operator();
|
||||
@ -79,12 +79,14 @@ private:
|
||||
ControlFlowNode const* m_break = nullptr;
|
||||
ControlFlowNode const* m_continue = nullptr;
|
||||
|
||||
std::map<YulString, FunctionFlow> m_functionFlows;
|
||||
std::map<FunctionDefinition const*, FunctionFlow> m_functionFlows;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Requires: Disambiguator, Function Hoister.
|
||||
* Computes control-flow side-effects for user-defined functions.
|
||||
* Source does not have to be disambiguated, unless you want the side-effects
|
||||
* based on function names.
|
||||
*/
|
||||
class ControlFlowSideEffectsCollector
|
||||
{
|
||||
@ -94,36 +96,43 @@ public:
|
||||
Block const& _ast
|
||||
);
|
||||
|
||||
std::map<YulString, ControlFlowSideEffects> const& functionSideEffects() const
|
||||
std::map<FunctionDefinition const*, ControlFlowSideEffects> const& functionSideEffects() const
|
||||
{
|
||||
return m_functionSideEffects;
|
||||
}
|
||||
/// Returns the side effects by function name, requires unique function names.
|
||||
std::map<YulString, ControlFlowSideEffects> functionSideEffectsNamed() const;
|
||||
private:
|
||||
|
||||
/// @returns false if nothing could be processed.
|
||||
bool processFunction(YulString _name);
|
||||
bool processFunction(FunctionDefinition const& _function);
|
||||
|
||||
/// @returns the next pending node of the function that is not
|
||||
/// a function call to a function that might not continue.
|
||||
/// De-queues the node or returns nullptr if no such node is found.
|
||||
ControlFlowNode const* nextProcessableNode(YulString _functionName);
|
||||
ControlFlowNode const* nextProcessableNode(FunctionDefinition const& _function);
|
||||
|
||||
/// @returns the side-effects of either a builtin call or a user defined function
|
||||
/// call (as far as already computed).
|
||||
ControlFlowSideEffects const& sideEffects(YulString _functionName) const;
|
||||
ControlFlowSideEffects const& sideEffects(FunctionCall const& _call) const;
|
||||
|
||||
/// Queues the given node to be processed (if not already visited)
|
||||
/// and if it is a function call, records that `_functionName` calls
|
||||
/// `*_node->functionCall`.
|
||||
void recordReachabilityAndQueue(YulString _functionName, ControlFlowNode const* _node);
|
||||
void recordReachabilityAndQueue(FunctionDefinition const& _function, ControlFlowNode const* _node);
|
||||
|
||||
Dialect const& m_dialect;
|
||||
ControlFlowBuilder m_cfgBuilder;
|
||||
std::map<YulString, ControlFlowSideEffects> m_functionSideEffects;
|
||||
std::map<YulString, std::list<ControlFlowNode const*>> m_pendingNodes;
|
||||
std::map<YulString, std::set<ControlFlowNode const*>> m_processedNodes;
|
||||
/// `x` is in `m_functionCalls[y]` if a direct call to `x` is reachable inside `y`
|
||||
std::map<YulString, std::set<YulString>> m_functionCalls;
|
||||
/// Function references, but only for calls to user-defined functions.
|
||||
std::map<FunctionCall const*, FunctionDefinition const*> m_functionReferences;
|
||||
/// Side effects of user-defined functions, is being constructod.
|
||||
std::map<FunctionDefinition const*, ControlFlowSideEffects> m_functionSideEffects;
|
||||
/// Control flow nodes still to process, per function.
|
||||
std::map<FunctionDefinition const*, std::list<ControlFlowNode const*>> m_pendingNodes;
|
||||
/// Control flow nodes already processed, per function.
|
||||
std::map<FunctionDefinition const*, std::set<ControlFlowNode const*>> m_processedNodes;
|
||||
/// Set of reachable function calls nodes in each function (including calls to builtins).
|
||||
std::map<FunctionDefinition const*, std::set<FunctionCall const*>> m_functionCalls;
|
||||
};
|
||||
|
||||
|
||||
|
60
libyul/FunctionReferenceResolver.cpp
Normal file
60
libyul/FunctionReferenceResolver.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
#include <libyul/FunctionReferenceResolver.h>
|
||||
|
||||
#include <libyul/AST.h>
|
||||
#include <libsolutil/CommonData.h>
|
||||
|
||||
#include <range/v3/view/reverse.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::yul;
|
||||
using namespace solidity::util;
|
||||
|
||||
FunctionReferenceResolver::FunctionReferenceResolver(Block const& _ast)
|
||||
{
|
||||
(*this)(_ast);
|
||||
yulAssert(m_scopes.empty());
|
||||
}
|
||||
|
||||
void FunctionReferenceResolver::operator()(FunctionCall const& _functionCall)
|
||||
{
|
||||
for (auto&& scope: m_scopes | ranges::views::reverse)
|
||||
if (FunctionDefinition const** function = util::valueOrNullptr(scope, _functionCall.functionName.name))
|
||||
{
|
||||
m_functionReferences[&_functionCall] = *function;
|
||||
break;
|
||||
}
|
||||
|
||||
// If we did not find anything, it was a builtin call.
|
||||
|
||||
ASTWalker::operator()(_functionCall);
|
||||
}
|
||||
|
||||
void FunctionReferenceResolver::operator()(Block const& _block)
|
||||
{
|
||||
m_scopes.emplace_back();
|
||||
for (auto const& statement: _block.statements)
|
||||
if (auto const* function = get_if<FunctionDefinition>(&statement))
|
||||
m_scopes.back()[function->name] = function;
|
||||
|
||||
ASTWalker::operator()(_block);
|
||||
|
||||
m_scopes.pop_back();
|
||||
}
|
@ -14,31 +14,35 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* AST walker that finds all function definitions and stores them into a map indexed by the function names.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libyul/optimiser/ASTWalker.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace solidity::yul
|
||||
{
|
||||
|
||||
/**
|
||||
* AST walker that finds all function definitions and stores them into a map indexed by the function names.
|
||||
* Resolves references to user-defined functions in function calls.
|
||||
* Assumes the code is correct, i.e. does not check for references to be valid or unique.
|
||||
*
|
||||
* Prerequisite: Disambiguator
|
||||
* Be careful not to iterate over the result - it is not deterministic.
|
||||
*/
|
||||
class FunctionDefinitionCollector: ASTWalker
|
||||
class FunctionReferenceResolver: private ASTWalker
|
||||
{
|
||||
public:
|
||||
static std::map<YulString, FunctionDefinition const*> run(Block const& _block);
|
||||
explicit FunctionReferenceResolver(Block const& _ast);
|
||||
std::map<FunctionCall const*, FunctionDefinition const*> const& references() const { return m_functionReferences; }
|
||||
|
||||
private:
|
||||
using ASTWalker::operator();
|
||||
void operator()(FunctionDefinition const& _functionDefinition) override;
|
||||
std::map<YulString, FunctionDefinition const*> m_functionDefinitions;
|
||||
void operator()(FunctionCall const& _functionCall) override;
|
||||
void operator()(Block const& _block) override;
|
||||
|
||||
std::map<FunctionCall const*, FunctionDefinition const*> m_functionReferences;
|
||||
std::vector<std::map<YulString, FunctionDefinition const*>> m_scopes;
|
||||
};
|
||||
|
||||
|
||||
}
|
@ -176,9 +176,9 @@ StackSlot ControlFlowGraphBuilder::operator()(Expression const& _expression)
|
||||
|
||||
StackSlot ControlFlowGraphBuilder::operator()(FunctionCall const& _call)
|
||||
{
|
||||
CFG::Operation const& operation = visitFunctionCall(_call);
|
||||
yulAssert(operation.output.size() == 1, "");
|
||||
return operation.output.front();
|
||||
Stack const& output = visitFunctionCall(_call);
|
||||
yulAssert(output.size() == 1, "");
|
||||
return output.front();
|
||||
}
|
||||
|
||||
void ControlFlowGraphBuilder::operator()(VariableDeclaration const& _varDecl)
|
||||
@ -219,8 +219,8 @@ void ControlFlowGraphBuilder::operator()(ExpressionStatement const& _exprStmt)
|
||||
yulAssert(m_currentBlock, "");
|
||||
std::visit(util::GenericVisitor{
|
||||
[&](FunctionCall const& _call) {
|
||||
CFG::Operation const& operation = visitFunctionCall(_call);
|
||||
yulAssert(operation.output.empty(), "");
|
||||
Stack const& output = visitFunctionCall(_call);
|
||||
yulAssert(output.empty(), "");
|
||||
},
|
||||
[&](auto const&) { yulAssert(false, ""); }
|
||||
}, _exprStmt.expression);
|
||||
@ -239,6 +239,9 @@ void ControlFlowGraphBuilder::operator()(ExpressionStatement const& _exprStmt)
|
||||
void ControlFlowGraphBuilder::operator()(Block const& _block)
|
||||
{
|
||||
ScopedSaveAndRestore saveScope(m_scope, m_info.scopes.at(&_block).get());
|
||||
for (auto const& statement: _block.statements)
|
||||
if (auto const* function = get_if<FunctionDefinition>(&statement))
|
||||
registerFunction(*function);
|
||||
for (auto const& statement: _block.statements)
|
||||
std::visit(*this, statement);
|
||||
}
|
||||
@ -386,11 +389,26 @@ void ControlFlowGraphBuilder::operator()(FunctionDefinition const& _function)
|
||||
Scope::Function& function = std::get<Scope::Function>(m_scope->identifiers.at(_function.name));
|
||||
m_graph.functions.emplace_back(&function);
|
||||
|
||||
CFG::FunctionInfo& functionInfo = m_graph.functionInfo.at(&function);
|
||||
|
||||
ControlFlowGraphBuilder builder{m_graph, m_info, m_dialect};
|
||||
builder.m_currentFunction = &functionInfo;
|
||||
builder.m_currentBlock = functionInfo.entry;
|
||||
builder(_function.body);
|
||||
builder.m_currentBlock->exit = CFG::BasicBlock::FunctionReturn{debugDataOf(_function), &functionInfo};
|
||||
}
|
||||
|
||||
void ControlFlowGraphBuilder::registerFunction(FunctionDefinition const& _function)
|
||||
{
|
||||
yulAssert(m_scope, "");
|
||||
yulAssert(m_scope->identifiers.count(_function.name), "");
|
||||
Scope::Function& function = std::get<Scope::Function>(m_scope->identifiers.at(_function.name));
|
||||
|
||||
yulAssert(m_info.scopes.at(&_function.body), "");
|
||||
Scope* virtualFunctionScope = m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()).get();
|
||||
yulAssert(virtualFunctionScope, "");
|
||||
|
||||
auto&& [it, inserted] = m_graph.functionInfo.emplace(std::make_pair(&function, CFG::FunctionInfo{
|
||||
bool inserted = m_graph.functionInfo.emplace(std::make_pair(&function, CFG::FunctionInfo{
|
||||
_function.debugData,
|
||||
function,
|
||||
&m_graph.makeBlock(debugDataOf(_function.body)),
|
||||
@ -406,19 +424,11 @@ void ControlFlowGraphBuilder::operator()(FunctionDefinition const& _function)
|
||||
_retVar.debugData
|
||||
};
|
||||
}) | ranges::to<vector>
|
||||
}));
|
||||
yulAssert(inserted, "");
|
||||
CFG::FunctionInfo& functionInfo = it->second;
|
||||
|
||||
ControlFlowGraphBuilder builder{m_graph, m_info, m_dialect};
|
||||
builder.m_currentFunction = &functionInfo;
|
||||
builder.m_currentBlock = functionInfo.entry;
|
||||
builder(_function.body);
|
||||
builder.m_currentBlock->exit = CFG::BasicBlock::FunctionReturn{debugDataOf(_function), &functionInfo};
|
||||
})).second;
|
||||
yulAssert(inserted);
|
||||
}
|
||||
|
||||
|
||||
CFG::Operation const& ControlFlowGraphBuilder::visitFunctionCall(FunctionCall const& _call)
|
||||
Stack const& ControlFlowGraphBuilder::visitFunctionCall(FunctionCall const& _call)
|
||||
{
|
||||
yulAssert(m_scope, "");
|
||||
yulAssert(m_currentBlock, "");
|
||||
@ -439,7 +449,7 @@ CFG::Operation const& ControlFlowGraphBuilder::visitFunctionCall(FunctionCall co
|
||||
}) | ranges::to<Stack>,
|
||||
// operation
|
||||
move(builtinCall)
|
||||
});
|
||||
}).output;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -456,7 +466,7 @@ CFG::Operation const& ControlFlowGraphBuilder::visitFunctionCall(FunctionCall co
|
||||
}) | ranges::to<Stack>,
|
||||
// operation
|
||||
CFG::FunctionCall{_call.debugData, function, _call}
|
||||
});
|
||||
}).output;
|
||||
}
|
||||
}
|
||||
|
||||
@ -464,9 +474,9 @@ Stack ControlFlowGraphBuilder::visitAssignmentRightHandSide(Expression const& _e
|
||||
{
|
||||
return std::visit(util::GenericVisitor{
|
||||
[&](FunctionCall const& _call) -> Stack {
|
||||
CFG::Operation const& operation = visitFunctionCall(_call);
|
||||
yulAssert(_expectedSlotCount == operation.output.size(), "");
|
||||
return operation.output;
|
||||
Stack const& output = visitFunctionCall(_call);
|
||||
yulAssert(_expectedSlotCount == output.size(), "");
|
||||
return output;
|
||||
},
|
||||
[&](auto const& _identifierOrLiteral) -> Stack {
|
||||
yulAssert(_expectedSlotCount == 1, "");
|
||||
|
@ -57,7 +57,8 @@ private:
|
||||
AsmAnalysisInfo const& _analysisInfo,
|
||||
Dialect const& _dialect
|
||||
);
|
||||
CFG::Operation const& visitFunctionCall(FunctionCall const&);
|
||||
void registerFunction(FunctionDefinition const& _function);
|
||||
Stack const& visitFunctionCall(FunctionCall const&);
|
||||
Stack visitAssignmentRightHandSide(Expression const& _expression, size_t _expectedSlotCount);
|
||||
|
||||
Scope::Function const& lookupFunction(YulString _name) const;
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include <libyul/backends/evm/EVMCodeTransform.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
#include <libyul/backends/evm/OptimizedEVMCodeTransform.h>
|
||||
|
||||
#include <libyul/Object.h>
|
||||
#include <libyul/Exceptions.h>
|
||||
@ -62,19 +63,35 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize)
|
||||
|
||||
yulAssert(_object.analysisInfo, "No analysis info.");
|
||||
yulAssert(_object.code, "No code.");
|
||||
// We do not catch and re-throw the stack too deep exception here because it is a YulException,
|
||||
// which should be native to this part of the code.
|
||||
CodeTransform transform{
|
||||
m_assembly,
|
||||
*_object.analysisInfo,
|
||||
*_object.code,
|
||||
m_dialect,
|
||||
context,
|
||||
_optimize,
|
||||
{},
|
||||
CodeTransform::UseNamedLabels::ForFirstFunctionOfEachName
|
||||
};
|
||||
transform(*_object.code);
|
||||
if (!transform.stackErrors().empty())
|
||||
BOOST_THROW_EXCEPTION(transform.stackErrors().front());
|
||||
if (_optimize && m_dialect.evmVersion().canOverchargeGasForCall())
|
||||
{
|
||||
auto stackErrors = OptimizedEVMCodeTransform::run(
|
||||
m_assembly,
|
||||
*_object.analysisInfo,
|
||||
*_object.code,
|
||||
m_dialect,
|
||||
context,
|
||||
OptimizedEVMCodeTransform::UseNamedLabels::ForFirstFunctionOfEachName
|
||||
);
|
||||
if (!stackErrors.empty())
|
||||
BOOST_THROW_EXCEPTION(stackErrors.front());
|
||||
}
|
||||
else
|
||||
{
|
||||
// We do not catch and re-throw the stack too deep exception here because it is a YulException,
|
||||
// which should be native to this part of the code.
|
||||
CodeTransform transform{
|
||||
m_assembly,
|
||||
*_object.analysisInfo,
|
||||
*_object.code,
|
||||
m_dialect,
|
||||
context,
|
||||
_optimize,
|
||||
{},
|
||||
CodeTransform::UseNamedLabels::ForFirstFunctionOfEachName
|
||||
};
|
||||
transform(*_object.code);
|
||||
if (!transform.stackErrors().empty())
|
||||
BOOST_THROW_EXCEPTION(transform.stackErrors().front());
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ vector<StackTooDeepError> OptimizedEVMCodeTransform::run(
|
||||
Block const& _block,
|
||||
EVMDialect const& _dialect,
|
||||
BuiltinContext& _builtinContext,
|
||||
bool _useNamedLabelsForFunctions
|
||||
UseNamedLabels _useNamedLabelsForFunctions
|
||||
)
|
||||
{
|
||||
std::unique_ptr<CFG> dfg = ControlFlowGraphBuilder::build(_analysisInfo, _dialect, _block);
|
||||
@ -170,15 +170,35 @@ void OptimizedEVMCodeTransform::operator()(CFG::Assignment const& _assignment)
|
||||
OptimizedEVMCodeTransform::OptimizedEVMCodeTransform(
|
||||
AbstractAssembly& _assembly,
|
||||
BuiltinContext& _builtinContext,
|
||||
bool _useNamedLabelsForFunctions,
|
||||
UseNamedLabels _useNamedLabelsForFunctions,
|
||||
CFG const& _dfg,
|
||||
StackLayout const& _stackLayout
|
||||
):
|
||||
m_assembly(_assembly),
|
||||
m_builtinContext(_builtinContext),
|
||||
m_useNamedLabelsForFunctions(_useNamedLabelsForFunctions),
|
||||
m_dfg(_dfg),
|
||||
m_stackLayout(_stackLayout)
|
||||
m_stackLayout(_stackLayout),
|
||||
m_functionLabels([&](){
|
||||
map<CFG::FunctionInfo const*, AbstractAssembly::LabelID> functionLabels;
|
||||
set<YulString> assignedFunctionNames;
|
||||
for (Scope::Function const* function: m_dfg.functions)
|
||||
{
|
||||
CFG::FunctionInfo const& functionInfo = m_dfg.functionInfo.at(function);
|
||||
bool nameAlreadySeen = !assignedFunctionNames.insert(function->name).second;
|
||||
if (_useNamedLabelsForFunctions == UseNamedLabels::YesAndForceUnique)
|
||||
yulAssert(!nameAlreadySeen);
|
||||
bool useNamedLabel = _useNamedLabelsForFunctions != UseNamedLabels::Never && !nameAlreadySeen;
|
||||
functionLabels[&functionInfo] = useNamedLabel ?
|
||||
m_assembly.namedLabel(
|
||||
function->name.str(),
|
||||
function->arguments.size(),
|
||||
function->returns.size(),
|
||||
functionInfo.debugData ? functionInfo.debugData->astID : nullopt
|
||||
) :
|
||||
m_assembly.newLabelId();
|
||||
}
|
||||
return functionLabels;
|
||||
}())
|
||||
{
|
||||
}
|
||||
|
||||
@ -191,15 +211,7 @@ void OptimizedEVMCodeTransform::assertLayoutCompatibility(Stack const& _currentS
|
||||
|
||||
AbstractAssembly::LabelID OptimizedEVMCodeTransform::getFunctionLabel(Scope::Function const& _function)
|
||||
{
|
||||
CFG::FunctionInfo const& functionInfo = m_dfg.functionInfo.at(&_function);
|
||||
if (!m_functionLabels.count(&functionInfo))
|
||||
m_functionLabels[&functionInfo] = m_useNamedLabelsForFunctions ? m_assembly.namedLabel(
|
||||
functionInfo.function.name.str(),
|
||||
functionInfo.function.arguments.size(),
|
||||
functionInfo.function.returns.size(),
|
||||
{}
|
||||
) : m_assembly.newLabelId();
|
||||
return m_functionLabels[&functionInfo];
|
||||
return m_functionLabels.at(&m_dfg.functionInfo.at(&_function));
|
||||
}
|
||||
|
||||
void OptimizedEVMCodeTransform::validateSlot(StackSlot const& _slot, Expression const& _expression)
|
||||
|
@ -43,13 +43,17 @@ struct StackLayout;
|
||||
class OptimizedEVMCodeTransform
|
||||
{
|
||||
public:
|
||||
/// Use named labels for functions 1) Yes and check that the names are unique
|
||||
/// 2) For none of the functions 3) for the first function of each name.
|
||||
enum class UseNamedLabels { YesAndForceUnique, Never, ForFirstFunctionOfEachName };
|
||||
|
||||
[[nodiscard]] static std::vector<StackTooDeepError> run(
|
||||
AbstractAssembly& _assembly,
|
||||
AsmAnalysisInfo& _analysisInfo,
|
||||
Block const& _block,
|
||||
EVMDialect const& _dialect,
|
||||
BuiltinContext& _builtinContext,
|
||||
bool _useNamedLabelsForFunctions = false
|
||||
UseNamedLabels _useNamedLabelsForFunctions
|
||||
);
|
||||
|
||||
/// Generate code for the function call @a _call. Only public for using with std::visit.
|
||||
@ -62,7 +66,7 @@ private:
|
||||
OptimizedEVMCodeTransform(
|
||||
AbstractAssembly& _assembly,
|
||||
BuiltinContext& _builtinContext,
|
||||
bool _useNamedLabelsForFunctions,
|
||||
UseNamedLabels _useNamedLabelsForFunctions,
|
||||
CFG const& _dfg,
|
||||
StackLayout const& _stackLayout
|
||||
);
|
||||
@ -70,6 +74,7 @@ private:
|
||||
/// Assert that it is valid to transition from @a _currentStack to @a _desiredStack.
|
||||
/// That is @a _currentStack matches each slot in @a _desiredStack that is not a JunkSlot exactly.
|
||||
static void assertLayoutCompatibility(Stack const& _currentStack, Stack const& _desiredStack);
|
||||
|
||||
/// @returns The label of the entry point of the given @a _function.
|
||||
/// Creates and stores a new label, if none exists already.
|
||||
AbstractAssembly::LabelID getFunctionLabel(Scope::Function const& _function);
|
||||
@ -94,13 +99,12 @@ private:
|
||||
|
||||
AbstractAssembly& m_assembly;
|
||||
BuiltinContext& m_builtinContext;
|
||||
bool m_useNamedLabelsForFunctions = true;
|
||||
CFG const& m_dfg;
|
||||
StackLayout const& m_stackLayout;
|
||||
Stack m_stack;
|
||||
std::map<yul::FunctionCall const*, AbstractAssembly::LabelID> m_returnLabels;
|
||||
std::map<CFG::BasicBlock const*, AbstractAssembly::LabelID> m_blockLabels;
|
||||
std::map<CFG::FunctionInfo const*, AbstractAssembly::LabelID> m_functionLabels;
|
||||
std::map<CFG::FunctionInfo const*, AbstractAssembly::LabelID> const m_functionLabels;
|
||||
/// Set of blocks already generated. If any of the contained blocks is ever jumped to, m_blockLabels should
|
||||
/// contain a jump label for it.
|
||||
std::set<CFG::BasicBlock const*> m_generated;
|
||||
|
@ -102,4 +102,34 @@ protected:
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template <
|
||||
typename Node,
|
||||
typename Visitor,
|
||||
typename Base = std::conditional_t<std::is_const_v<Node>, ASTWalker, ASTModifier>
|
||||
>
|
||||
struct ForEach: Base
|
||||
{
|
||||
ForEach(Visitor& _visitor): visitor(_visitor) {}
|
||||
|
||||
using Base::operator();
|
||||
void operator()(Node& _node) override
|
||||
{
|
||||
visitor(_node);
|
||||
Base::operator()(_node);
|
||||
}
|
||||
|
||||
Visitor& visitor;
|
||||
};
|
||||
}
|
||||
|
||||
/// Helper function that traverses the AST and calls the visitor for each
|
||||
/// node of a specific type.
|
||||
template<typename Node, typename Entry, typename Visitor>
|
||||
void forEach(Entry&& _entry, Visitor&& _visitor)
|
||||
{
|
||||
detail::ForEach<Node, Visitor&>{_visitor}(_entry);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <libyul/optimiser/Semantics.h>
|
||||
#include <libyul/AST.h>
|
||||
#include <libyul/optimiser/NameCollector.h>
|
||||
#include <libyul/ControlFlowSideEffectsCollector.h>
|
||||
#include <libsolutil/CommonData.h>
|
||||
|
||||
using namespace std;
|
||||
@ -26,6 +27,14 @@ using namespace solidity;
|
||||
using namespace solidity::yul;
|
||||
using namespace solidity::util;
|
||||
|
||||
void ConditionalSimplifier::run(OptimiserStepContext& _context, Block& _ast)
|
||||
{
|
||||
ConditionalSimplifier{
|
||||
_context.dialect,
|
||||
ControlFlowSideEffectsCollector{_context.dialect, _ast}.functionSideEffectsNamed()
|
||||
}(_ast);
|
||||
}
|
||||
|
||||
void ConditionalSimplifier::operator()(Switch& _switch)
|
||||
{
|
||||
visit(*_switch.expression);
|
||||
@ -65,7 +74,7 @@ void ConditionalSimplifier::operator()(Block& _block)
|
||||
if (
|
||||
holds_alternative<Identifier>(*_if.condition) &&
|
||||
!_if.body.statements.empty() &&
|
||||
TerminationFinder(m_dialect).controlFlowKind(_if.body.statements.back()) !=
|
||||
TerminationFinder(m_dialect, &m_functionSideEffects).controlFlowKind(_if.body.statements.back()) !=
|
||||
TerminationFinder::ControlFlow::FlowOut
|
||||
)
|
||||
{
|
||||
|
@ -44,7 +44,6 @@ namespace solidity::yul
|
||||
*
|
||||
* Future features:
|
||||
* - allow replacements by "1"
|
||||
* - take termination of user-defined functions into account
|
||||
*
|
||||
* Works best with SSA form and if dead code removal has run before.
|
||||
*
|
||||
@ -54,20 +53,21 @@ class ConditionalSimplifier: public ASTModifier
|
||||
{
|
||||
public:
|
||||
static constexpr char const* name{"ConditionalSimplifier"};
|
||||
static void run(OptimiserStepContext& _context, Block& _ast)
|
||||
{
|
||||
ConditionalSimplifier{_context.dialect}(_ast);
|
||||
}
|
||||
static void run(OptimiserStepContext& _context, Block& _ast);
|
||||
|
||||
using ASTModifier::operator();
|
||||
void operator()(Switch& _switch) override;
|
||||
void operator()(Block& _block) override;
|
||||
|
||||
private:
|
||||
explicit ConditionalSimplifier(Dialect const& _dialect):
|
||||
m_dialect(_dialect)
|
||||
explicit ConditionalSimplifier(
|
||||
Dialect const& _dialect,
|
||||
std::map<YulString, ControlFlowSideEffects> _sideEffects
|
||||
):
|
||||
m_dialect(_dialect), m_functionSideEffects(move(_sideEffects))
|
||||
{}
|
||||
Dialect const& m_dialect;
|
||||
std::map<YulString, ControlFlowSideEffects> m_functionSideEffects;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <libyul/AST.h>
|
||||
#include <libyul/Utilities.h>
|
||||
#include <libyul/optimiser/NameCollector.h>
|
||||
#include <libyul/ControlFlowSideEffectsCollector.h>
|
||||
#include <libsolutil/CommonData.h>
|
||||
|
||||
using namespace std;
|
||||
@ -27,6 +28,14 @@ using namespace solidity;
|
||||
using namespace solidity::yul;
|
||||
using namespace solidity::util;
|
||||
|
||||
void ConditionalUnsimplifier::run(OptimiserStepContext& _context, Block& _ast)
|
||||
{
|
||||
ConditionalUnsimplifier{
|
||||
_context.dialect,
|
||||
ControlFlowSideEffectsCollector{_context.dialect, _ast}.functionSideEffectsNamed()
|
||||
}(_ast);
|
||||
}
|
||||
|
||||
void ConditionalUnsimplifier::operator()(Switch& _switch)
|
||||
{
|
||||
visit(*_switch.expression);
|
||||
@ -78,7 +87,7 @@ void ConditionalUnsimplifier::operator()(Block& _block)
|
||||
YulString condition = std::get<Identifier>(*_if.condition).name;
|
||||
if (
|
||||
holds_alternative<Assignment>(_stmt2) &&
|
||||
TerminationFinder(m_dialect).controlFlowKind(_if.body.statements.back()) !=
|
||||
TerminationFinder(m_dialect, &m_functionSideEffects).controlFlowKind(_if.body.statements.back()) !=
|
||||
TerminationFinder::ControlFlow::FlowOut
|
||||
)
|
||||
{
|
||||
|
@ -33,20 +33,21 @@ class ConditionalUnsimplifier: public ASTModifier
|
||||
{
|
||||
public:
|
||||
static constexpr char const* name{"ConditionalUnsimplifier"};
|
||||
static void run(OptimiserStepContext& _context, Block& _ast)
|
||||
{
|
||||
ConditionalUnsimplifier{_context.dialect}(_ast);
|
||||
}
|
||||
static void run(OptimiserStepContext& _context, Block& _ast);
|
||||
|
||||
using ASTModifier::operator();
|
||||
void operator()(Switch& _switch) override;
|
||||
void operator()(Block& _block) override;
|
||||
|
||||
private:
|
||||
explicit ConditionalUnsimplifier(Dialect const& _dialect):
|
||||
m_dialect(_dialect)
|
||||
explicit ConditionalUnsimplifier(
|
||||
Dialect const& _dialect,
|
||||
std::map<YulString, ControlFlowSideEffects> const& _sideEffects
|
||||
):
|
||||
m_dialect(_dialect), m_functionSideEffects(_sideEffects)
|
||||
{}
|
||||
Dialect const& m_dialect;
|
||||
std::map<YulString, ControlFlowSideEffects> const& m_functionSideEffects;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -45,9 +45,9 @@ DataFlowAnalyzer::DataFlowAnalyzer(
|
||||
Dialect const& _dialect,
|
||||
map<YulString, SideEffects> _functionSideEffects
|
||||
):
|
||||
m_dialect(_dialect),
|
||||
m_functionSideEffects(std::move(_functionSideEffects)),
|
||||
m_knowledgeBase(_dialect, m_value)
|
||||
m_dialect(_dialect),
|
||||
m_functionSideEffects(std::move(_functionSideEffects)),
|
||||
m_knowledgeBase(_dialect, m_value)
|
||||
{
|
||||
if (auto const* builtin = _dialect.memoryStoreFunction(YulString{}))
|
||||
m_storeFunctionName[static_cast<unsigned>(StoreLoadLocation::Memory)] = builtin->name;
|
||||
@ -123,9 +123,7 @@ void DataFlowAnalyzer::operator()(If& _if)
|
||||
|
||||
joinKnowledge(storage, memory);
|
||||
|
||||
Assignments assignments;
|
||||
assignments(_if.body);
|
||||
clearValues(assignments.names());
|
||||
clearValues(assignedVariableNames(_if.body));
|
||||
}
|
||||
|
||||
void DataFlowAnalyzer::operator()(Switch& _switch)
|
||||
@ -140,11 +138,10 @@ void DataFlowAnalyzer::operator()(Switch& _switch)
|
||||
(*this)(_case.body);
|
||||
joinKnowledge(storage, memory);
|
||||
|
||||
Assignments assignments;
|
||||
assignments(_case.body);
|
||||
assignedVariables += assignments.names();
|
||||
set<YulString> variables = assignedVariableNames(_case.body);
|
||||
assignedVariables += variables;
|
||||
// This is a little too destructive, we could retain the old values.
|
||||
clearValues(assignments.names());
|
||||
clearValues(variables);
|
||||
clearKnowledgeIfInvalidated(_case.body);
|
||||
}
|
||||
for (auto& _case: _switch.cases)
|
||||
@ -190,10 +187,9 @@ void DataFlowAnalyzer::operator()(ForLoop& _for)
|
||||
AssignmentsSinceContinue assignmentsSinceCont;
|
||||
assignmentsSinceCont(_for.body);
|
||||
|
||||
Assignments assignments;
|
||||
assignments(_for.body);
|
||||
assignments(_for.post);
|
||||
clearValues(assignments.names());
|
||||
set<YulString> assignedVariables =
|
||||
assignedVariableNames(_for.body) + assignedVariableNames(_for.post);
|
||||
clearValues(assignedVariables);
|
||||
|
||||
// break/continue are tricky for storage and thus we almost always clear here.
|
||||
clearKnowledgeIfInvalidated(*_for.condition);
|
||||
@ -205,7 +201,7 @@ void DataFlowAnalyzer::operator()(ForLoop& _for)
|
||||
clearValues(assignmentsSinceCont.names());
|
||||
clearKnowledgeIfInvalidated(_for.body);
|
||||
(*this)(_for.post);
|
||||
clearValues(assignments.names());
|
||||
clearValues(assignedVariables);
|
||||
clearKnowledgeIfInvalidated(*_for.condition);
|
||||
clearKnowledgeIfInvalidated(_for.post);
|
||||
clearKnowledgeIfInvalidated(_for.body);
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <libyul/optimiser/DeadCodeEliminator.h>
|
||||
#include <libyul/optimiser/Semantics.h>
|
||||
#include <libyul/optimiser/OptimiserStep.h>
|
||||
#include <libyul/ControlFlowSideEffectsCollector.h>
|
||||
#include <libyul/AST.h>
|
||||
|
||||
#include <libevmasm/SemanticInformation.h>
|
||||
@ -36,7 +37,11 @@ using namespace solidity::yul;
|
||||
|
||||
void DeadCodeEliminator::run(OptimiserStepContext& _context, Block& _ast)
|
||||
{
|
||||
DeadCodeEliminator{_context.dialect}(_ast);
|
||||
ControlFlowSideEffectsCollector sideEffects(_context.dialect, _ast);
|
||||
DeadCodeEliminator{
|
||||
_context.dialect,
|
||||
sideEffects.functionSideEffectsNamed()
|
||||
}(_ast);
|
||||
}
|
||||
|
||||
void DeadCodeEliminator::operator()(ForLoop& _for)
|
||||
@ -49,7 +54,7 @@ void DeadCodeEliminator::operator()(Block& _block)
|
||||
{
|
||||
TerminationFinder::ControlFlow controlFlowChange;
|
||||
size_t index;
|
||||
tie(controlFlowChange, index) = TerminationFinder{m_dialect}.firstUnconditionalControlFlowChange(_block.statements);
|
||||
tie(controlFlowChange, index) = TerminationFinder{m_dialect, &m_functionSideEffects}.firstUnconditionalControlFlowChange(_block.statements);
|
||||
|
||||
// Erase everything after the terminating statement that is not a function definition.
|
||||
if (controlFlowChange != TerminationFinder::ControlFlow::FlowOut && index != std::numeric_limits<size_t>::max())
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include <libyul/optimiser/ASTWalker.h>
|
||||
#include <libyul/YulString.h>
|
||||
#include <libyul/ControlFlowSideEffects.h>
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
@ -36,7 +37,9 @@ struct OptimiserStepContext;
|
||||
* Optimisation stage that removes unreachable code
|
||||
*
|
||||
* Unreachable code is any code within a block which is preceded by a
|
||||
* leave, return, invalid, break, continue, selfdestruct or revert.
|
||||
* leave, return, invalid, break, continue, selfdestruct or revert or
|
||||
* a call to a user-defined function that never returns (either due to
|
||||
* recursion or a call to return / revert / stop).
|
||||
*
|
||||
* Function definitions are retained as they might be called by earlier
|
||||
* code and thus are considered reachable.
|
||||
@ -57,9 +60,13 @@ public:
|
||||
void operator()(Block& _block) override;
|
||||
|
||||
private:
|
||||
DeadCodeEliminator(Dialect const& _dialect): m_dialect(_dialect) {}
|
||||
DeadCodeEliminator(
|
||||
Dialect const& _dialect,
|
||||
std::map<YulString, ControlFlowSideEffects> _sideEffects
|
||||
): m_dialect(_dialect), m_functionSideEffects(move(_sideEffects)) {}
|
||||
|
||||
Dialect const& m_dialect;
|
||||
std::map<YulString, ControlFlowSideEffects> m_functionSideEffects;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,36 +0,0 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <libyul/optimiser/FunctionDefinitionCollector.h>
|
||||
#include <libyul/AST.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::yul;
|
||||
|
||||
map<YulString, FunctionDefinition const*> FunctionDefinitionCollector::run(Block const& _block)
|
||||
{
|
||||
FunctionDefinitionCollector functionDefinitionCollector;
|
||||
functionDefinitionCollector(_block);
|
||||
return functionDefinitionCollector.m_functionDefinitions;
|
||||
}
|
||||
|
||||
void FunctionDefinitionCollector::operator()(FunctionDefinition const& _functionDefinition)
|
||||
{
|
||||
m_functionDefinitions[_functionDefinition.name] = &_functionDefinition;
|
||||
ASTWalker::operator()(_functionDefinition);
|
||||
}
|
@ -78,13 +78,6 @@ map<YulString, size_t> ReferencesCounter::countReferences(Expression const& _exp
|
||||
return counter.references();
|
||||
}
|
||||
|
||||
void Assignments::operator()(Assignment const& _assignment)
|
||||
{
|
||||
for (auto const& var: _assignment.variableNames)
|
||||
m_names.emplace(var.name);
|
||||
}
|
||||
|
||||
|
||||
void AssignmentsSinceContinue::operator()(ForLoop const& _forLoop)
|
||||
{
|
||||
m_forLoopDepth++;
|
||||
@ -109,3 +102,22 @@ void AssignmentsSinceContinue::operator()(FunctionDefinition const&)
|
||||
{
|
||||
yulAssert(false, "");
|
||||
}
|
||||
|
||||
std::set<YulString> solidity::yul::assignedVariableNames(Block const& _code)
|
||||
{
|
||||
std::set<YulString> names;
|
||||
forEach<Assignment const>(_code, [&](Assignment const& _assignment) {
|
||||
for (auto const& var: _assignment.variableNames)
|
||||
names.emplace(var.name);
|
||||
});
|
||||
return names;
|
||||
}
|
||||
|
||||
map<YulString, FunctionDefinition const*> solidity::yul::allFunctionDefinitions(Block const& _block)
|
||||
{
|
||||
std::map<YulString, FunctionDefinition const*> result;
|
||||
forEach<FunctionDefinition const>(_block, [&](FunctionDefinition const& _function) {
|
||||
result[_function.name] = &_function;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
@ -91,20 +91,6 @@ private:
|
||||
std::map<YulString, size_t> m_references;
|
||||
};
|
||||
|
||||
/**
|
||||
* Specific AST walker that finds all variables that are assigned to.
|
||||
*/
|
||||
class Assignments: public ASTWalker
|
||||
{
|
||||
public:
|
||||
using ASTWalker::operator ();
|
||||
void operator()(Assignment const& _assignment) override;
|
||||
|
||||
std::set<YulString> const& names() const { return m_names; }
|
||||
private:
|
||||
std::set<YulString> m_names;
|
||||
};
|
||||
|
||||
/**
|
||||
* Collects all names from a given continue statement on onwards.
|
||||
*
|
||||
@ -130,4 +116,12 @@ private:
|
||||
std::set<YulString> m_names;
|
||||
};
|
||||
|
||||
/// @returns the names of all variables that are assigned to inside @a _code.
|
||||
/// (ignores variable declarations)
|
||||
std::set<YulString> assignedVariableNames(Block const& _code);
|
||||
|
||||
/// @returns all function definitions anywhere in the AST.
|
||||
/// Requires disambiguated source.
|
||||
std::map<YulString, FunctionDefinition const*> allFunctionDefinitions(Block const& _block);
|
||||
|
||||
}
|
||||
|
@ -196,12 +196,7 @@ void IntroduceControlFlowSSA::operator()(ForLoop& _for)
|
||||
{
|
||||
yulAssert(_for.pre.statements.empty(), "For loop init rewriter not run.");
|
||||
|
||||
Assignments assignments;
|
||||
assignments(_for.body);
|
||||
assignments(_for.post);
|
||||
|
||||
|
||||
for (auto const& var: assignments.names())
|
||||
for (auto const& var: assignedVariableNames(_for.body) + assignedVariableNames(_for.post))
|
||||
if (m_variablesInScope.count(var))
|
||||
m_variablesToReassign.insert(var);
|
||||
|
||||
@ -359,11 +354,7 @@ void PropagateValues::operator()(ForLoop& _for)
|
||||
{
|
||||
yulAssert(_for.pre.statements.empty(), "For loop init rewriter not run.");
|
||||
|
||||
Assignments assignments;
|
||||
assignments(_for.body);
|
||||
assignments(_for.post);
|
||||
|
||||
for (auto const& var: assignments.names())
|
||||
for (auto const& var: assignedVariableNames(_for.body) + assignedVariableNames(_for.post))
|
||||
m_currentVariableValues.erase(var);
|
||||
|
||||
visit(*_for.condition);
|
||||
@ -389,11 +380,10 @@ void PropagateValues::operator()(Block& _block)
|
||||
void SSATransform::run(OptimiserStepContext& _context, Block& _ast)
|
||||
{
|
||||
TypeInfo typeInfo(_context.dialect, _ast);
|
||||
Assignments assignments;
|
||||
assignments(_ast);
|
||||
IntroduceSSA{_context.dispenser, assignments.names(), typeInfo}(_ast);
|
||||
IntroduceControlFlowSSA{_context.dispenser, assignments.names(), typeInfo}(_ast);
|
||||
PropagateValues{assignments.names()}(_ast);
|
||||
set<YulString> assignedVariables = assignedVariableNames(_ast);
|
||||
IntroduceSSA{_context.dispenser, assignedVariables, typeInfo}(_ast);
|
||||
IntroduceControlFlowSSA{_context.dispenser, assignedVariables, typeInfo}(_ast);
|
||||
PropagateValues{assignedVariables}(_ast);
|
||||
}
|
||||
|
||||
|
||||
|
@ -182,8 +182,19 @@ pair<TerminationFinder::ControlFlow, size_t> TerminationFinder::firstUncondition
|
||||
TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement const& _statement)
|
||||
{
|
||||
if (
|
||||
holds_alternative<VariableDeclaration>(_statement) &&
|
||||
std::get<VariableDeclaration>(_statement).value &&
|
||||
containsNonContinuingFunctionCall(*std::get<VariableDeclaration>(_statement).value)
|
||||
)
|
||||
return ControlFlow::Terminate;
|
||||
else if (
|
||||
holds_alternative<Assignment>(_statement) &&
|
||||
containsNonContinuingFunctionCall(*std::get<Assignment>(_statement).value)
|
||||
)
|
||||
return ControlFlow::Terminate;
|
||||
else if (
|
||||
holds_alternative<ExpressionStatement>(_statement) &&
|
||||
isTerminatingBuiltin(std::get<ExpressionStatement>(_statement))
|
||||
containsNonContinuingFunctionCall(std::get<ExpressionStatement>(_statement).expression)
|
||||
)
|
||||
return ControlFlow::Terminate;
|
||||
else if (holds_alternative<Break>(_statement))
|
||||
@ -196,10 +207,18 @@ TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement cons
|
||||
return ControlFlow::FlowOut;
|
||||
}
|
||||
|
||||
bool TerminationFinder::isTerminatingBuiltin(ExpressionStatement const& _exprStmnt)
|
||||
bool TerminationFinder::containsNonContinuingFunctionCall(Expression const& _expr)
|
||||
{
|
||||
if (holds_alternative<FunctionCall>(_exprStmnt.expression))
|
||||
if (auto instruction = toEVMInstruction(m_dialect, std::get<FunctionCall>(_exprStmnt.expression).functionName.name))
|
||||
return evmasm::SemanticInformation::terminatesControlFlow(*instruction);
|
||||
if (auto functionCall = std::get_if<FunctionCall>(&_expr))
|
||||
{
|
||||
for (auto const& arg: functionCall->arguments)
|
||||
if (containsNonContinuingFunctionCall(arg))
|
||||
return true;
|
||||
|
||||
if (auto builtin = m_dialect.builtin(functionCall->functionName.name))
|
||||
return !builtin->controlFlowSideEffects.canContinue;
|
||||
else if (m_functionSideEffects && m_functionSideEffects->count(functionCall->functionName.name))
|
||||
return !m_functionSideEffects->at(functionCall->functionName.name).canContinue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -205,22 +205,31 @@ private:
|
||||
std::set<YulString> m_variableReferences;
|
||||
};
|
||||
|
||||
struct ControlFlowSideEffects;
|
||||
|
||||
/**
|
||||
* Helper class to find "irregular" control flow.
|
||||
* This includes termination, break and continue.
|
||||
* This includes termination, break, continue and leave.
|
||||
* In general, it is applied only to "simple" statements. The control-flow
|
||||
* of loops, switches and if statements is always "FlowOut" with the assumption
|
||||
* that the caller will descend into them.
|
||||
*/
|
||||
class TerminationFinder
|
||||
{
|
||||
public:
|
||||
// TODO check all uses of TerminationFinder!
|
||||
/// "Terminate" here means that there is no continuing control-flow.
|
||||
/// If this is applied to a function that can revert or stop, but can also
|
||||
/// exit regularly, the property is set to "FlowOut".
|
||||
enum class ControlFlow { FlowOut, Break, Continue, Terminate, Leave };
|
||||
|
||||
TerminationFinder(Dialect const& _dialect): m_dialect(_dialect) {}
|
||||
TerminationFinder(
|
||||
Dialect const& _dialect,
|
||||
std::map<YulString, ControlFlowSideEffects> const* _functionSideEffects = nullptr
|
||||
): m_dialect(_dialect), m_functionSideEffects(_functionSideEffects) {}
|
||||
|
||||
/// @returns the index of the first statement in the provided sequence
|
||||
/// that is an unconditional ``break``, ``continue``, ``leave`` or a
|
||||
/// call to a terminating builtin function.
|
||||
/// call to a terminating function.
|
||||
/// If control flow can continue at the end of the list,
|
||||
/// returns `FlowOut` and ``size_t(-1)``.
|
||||
/// The function might return ``FlowOut`` even though control
|
||||
@ -233,13 +242,14 @@ public:
|
||||
/// This function could return FlowOut even if control flow never continues.
|
||||
ControlFlow controlFlowKind(Statement const& _statement);
|
||||
|
||||
/// @returns true if the expression statement is a direct
|
||||
/// call to a builtin terminating function like
|
||||
/// ``stop``, ``revert`` or ``return``.
|
||||
bool isTerminatingBuiltin(ExpressionStatement const& _exprStmnt);
|
||||
/// @returns true if the expression contains a
|
||||
/// call to a terminating function, i.e. a function that does not have
|
||||
/// a regular "flow out" control-flow (it might also be recursive).
|
||||
bool containsNonContinuingFunctionCall(Expression const& _expr);
|
||||
|
||||
private:
|
||||
Dialect const& m_dialect;
|
||||
std::map<YulString, ControlFlowSideEffects> const* m_functionSideEffects;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -27,6 +27,13 @@
|
||||
#include <libyul/optimiser/Metrics.h>
|
||||
#include <libyul/optimiser/Semantics.h>
|
||||
|
||||
#include <libyul/backends/evm/ControlFlowGraphBuilder.h>
|
||||
#include <libyul/backends/evm/StackHelpers.h>
|
||||
#include <libyul/backends/evm/StackLayoutGenerator.h>
|
||||
|
||||
#include <libyul/AsmAnalysis.h>
|
||||
#include <libyul/AsmAnalysisInfo.h>
|
||||
|
||||
#include <libyul/CompilabilityChecker.h>
|
||||
|
||||
#include <libyul/AST.h>
|
||||
@ -162,6 +169,50 @@ void eliminateVariables(
|
||||
UnusedPruner::runUntilStabilised(_dialect, _node, _allowMSizeOptimization);
|
||||
}
|
||||
|
||||
void eliminateVariables(
|
||||
Dialect const& _dialect,
|
||||
Block& _block,
|
||||
vector<StackLayoutGenerator::StackTooDeep> const& _unreachables,
|
||||
bool _allowMSizeOptimization
|
||||
)
|
||||
{
|
||||
RematCandidateSelector selector{_dialect};
|
||||
selector(_block);
|
||||
std::map<YulString, size_t> candidates;
|
||||
for (auto [cost, candidatesWithCost]: selector.candidates())
|
||||
for (auto candidate: candidatesWithCost)
|
||||
candidates[get<0>(candidate)] = cost;
|
||||
|
||||
set<YulString> varsToEliminate;
|
||||
|
||||
// TODO: this currently ignores the fact that variables may reference other variables we want to eliminate.
|
||||
for (auto const& unreachable: _unreachables)
|
||||
{
|
||||
map<size_t, vector<YulString>> suitableCandidates;
|
||||
size_t neededSlots = unreachable.deficit;
|
||||
for (auto varName: unreachable.variableChoices)
|
||||
{
|
||||
if (varsToEliminate.count(varName))
|
||||
--neededSlots;
|
||||
else if (size_t* cost = util::valueOrNullptr(candidates, varName))
|
||||
if (!util::contains(suitableCandidates[*cost], varName))
|
||||
suitableCandidates[*cost].emplace_back(varName);
|
||||
}
|
||||
for (auto candidatesByCost: suitableCandidates)
|
||||
{
|
||||
for (auto candidate: candidatesByCost.second)
|
||||
if (neededSlots--)
|
||||
varsToEliminate.emplace(candidate);
|
||||
else
|
||||
break;
|
||||
if (!neededSlots)
|
||||
break;
|
||||
}
|
||||
}
|
||||
Rematerialiser::run(_dialect, _block, std::move(varsToEliminate), true);
|
||||
UnusedPruner::runUntilStabilised(_dialect, _block, _allowMSizeOptimization);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool StackCompressor::run(
|
||||
@ -176,39 +227,66 @@ bool StackCompressor::run(
|
||||
_object.code->statements.size() > 0 && holds_alternative<Block>(_object.code->statements.at(0)),
|
||||
"Need to run the function grouper before the stack compressor."
|
||||
);
|
||||
bool usesOptimizedCodeGenerator = false;
|
||||
if (auto evmDialect = dynamic_cast<EVMDialect const*>(&_dialect))
|
||||
usesOptimizedCodeGenerator =
|
||||
_optimizeStackAllocation &&
|
||||
evmDialect->evmVersion().canOverchargeGasForCall() &&
|
||||
evmDialect->providesObjectAccess();
|
||||
bool allowMSizeOptimzation = !MSizeFinder::containsMSize(_dialect, *_object.code);
|
||||
for (size_t iterations = 0; iterations < _maxIterations; iterations++)
|
||||
if (usesOptimizedCodeGenerator)
|
||||
{
|
||||
map<YulString, int> stackSurplus = CompilabilityChecker(_dialect, _object, _optimizeStackAllocation).stackDeficit;
|
||||
if (stackSurplus.empty())
|
||||
return true;
|
||||
|
||||
if (stackSurplus.count(YulString{}))
|
||||
{
|
||||
yulAssert(stackSurplus.at({}) > 0, "Invalid surplus value.");
|
||||
eliminateVariables(
|
||||
_dialect,
|
||||
std::get<Block>(_object.code->statements.at(0)),
|
||||
static_cast<size_t>(stackSurplus.at({})),
|
||||
allowMSizeOptimzation
|
||||
);
|
||||
}
|
||||
|
||||
yul::AsmAnalysisInfo analysisInfo = yul::AsmAnalyzer::analyzeStrictAssertCorrect(_dialect, _object);
|
||||
unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build(analysisInfo, _dialect, *_object.code);
|
||||
Block& mainBlock = std::get<Block>(_object.code->statements.at(0));
|
||||
if (
|
||||
auto stackTooDeepErrors = StackLayoutGenerator::reportStackTooDeep(*cfg, YulString{});
|
||||
!stackTooDeepErrors.empty()
|
||||
)
|
||||
eliminateVariables(_dialect, mainBlock, stackTooDeepErrors, allowMSizeOptimzation);
|
||||
for (size_t i = 1; i < _object.code->statements.size(); ++i)
|
||||
{
|
||||
auto& fun = std::get<FunctionDefinition>(_object.code->statements[i]);
|
||||
if (!stackSurplus.count(fun.name))
|
||||
continue;
|
||||
|
||||
yulAssert(stackSurplus.at(fun.name) > 0, "Invalid surplus value.");
|
||||
eliminateVariables(
|
||||
_dialect,
|
||||
fun,
|
||||
static_cast<size_t>(stackSurplus.at(fun.name)),
|
||||
allowMSizeOptimzation
|
||||
);
|
||||
if (
|
||||
auto stackTooDeepErrors = StackLayoutGenerator::reportStackTooDeep(*cfg, fun.name);
|
||||
!stackTooDeepErrors.empty()
|
||||
)
|
||||
eliminateVariables(_dialect, fun.body, stackTooDeepErrors, allowMSizeOptimzation);
|
||||
}
|
||||
}
|
||||
else
|
||||
for (size_t iterations = 0; iterations < _maxIterations; iterations++)
|
||||
{
|
||||
map<YulString, int> stackSurplus = CompilabilityChecker(_dialect, _object, _optimizeStackAllocation).stackDeficit;
|
||||
if (stackSurplus.empty())
|
||||
return true;
|
||||
|
||||
if (stackSurplus.count(YulString{}))
|
||||
{
|
||||
yulAssert(stackSurplus.at({}) > 0, "Invalid surplus value.");
|
||||
eliminateVariables(
|
||||
_dialect,
|
||||
std::get<Block>(_object.code->statements.at(0)),
|
||||
static_cast<size_t>(stackSurplus.at({})),
|
||||
allowMSizeOptimzation
|
||||
);
|
||||
}
|
||||
|
||||
for (size_t i = 1; i < _object.code->statements.size(); ++i)
|
||||
{
|
||||
auto& fun = std::get<FunctionDefinition>(_object.code->statements[i]);
|
||||
if (!stackSurplus.count(fun.name))
|
||||
continue;
|
||||
|
||||
yulAssert(stackSurplus.at(fun.name) > 0, "Invalid surplus value.");
|
||||
eliminateVariables(
|
||||
_dialect,
|
||||
fun,
|
||||
static_cast<size_t>(stackSurplus.at(fun.name)),
|
||||
allowMSizeOptimzation
|
||||
);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -18,17 +18,21 @@
|
||||
#include <libyul/optimiser/StackLimitEvader.h>
|
||||
#include <libyul/optimiser/CallGraphGenerator.h>
|
||||
#include <libyul/optimiser/FunctionCallFinder.h>
|
||||
#include <libyul/optimiser/FunctionDefinitionCollector.h>
|
||||
#include <libyul/optimiser/NameDispenser.h>
|
||||
#include <libyul/optimiser/NameCollector.h>
|
||||
#include <libyul/optimiser/StackToMemoryMover.h>
|
||||
#include <libyul/backends/evm/ControlFlowGraphBuilder.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
#include <libyul/AsmAnalysis.h>
|
||||
#include <libyul/AST.h>
|
||||
#include <libyul/CompilabilityChecker.h>
|
||||
#include <libyul/Exceptions.h>
|
||||
#include <libyul/Object.h>
|
||||
#include <libyul/Utilities.h>
|
||||
#include <libsolutil/Algorithms.h>
|
||||
#include <libsolutil/CommonData.h>
|
||||
|
||||
#include <range/v3/range/conversion.hpp>
|
||||
#include <range/v3/view/concat.hpp>
|
||||
#include <range/v3/view/take.hpp>
|
||||
|
||||
@ -114,6 +118,45 @@ u256 literalArgumentValue(FunctionCall const& _call)
|
||||
}
|
||||
}
|
||||
|
||||
void StackLimitEvader::run(
|
||||
OptimiserStepContext& _context,
|
||||
Object& _object
|
||||
)
|
||||
{
|
||||
auto const* evmDialect = dynamic_cast<EVMDialect const*>(&_context.dialect);
|
||||
yulAssert(
|
||||
evmDialect && evmDialect->providesObjectAccess(),
|
||||
"StackLimitEvader can only be run on objects using the EVMDialect with object access."
|
||||
);
|
||||
if (evmDialect && evmDialect->evmVersion().canOverchargeGasForCall())
|
||||
{
|
||||
yul::AsmAnalysisInfo analysisInfo = yul::AsmAnalyzer::analyzeStrictAssertCorrect(*evmDialect, _object);
|
||||
unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build(analysisInfo, *evmDialect, *_object.code);
|
||||
run(_context, _object, StackLayoutGenerator::reportStackTooDeep(*cfg));
|
||||
}
|
||||
else
|
||||
run(_context, _object, CompilabilityChecker{
|
||||
_context.dialect,
|
||||
_object,
|
||||
true
|
||||
}.unreachableVariables);
|
||||
|
||||
}
|
||||
|
||||
void StackLimitEvader::run(
|
||||
OptimiserStepContext& _context,
|
||||
Object& _object,
|
||||
map<YulString, vector<StackLayoutGenerator::StackTooDeep>> const& _stackTooDeepErrors
|
||||
)
|
||||
{
|
||||
map<YulString, set<YulString>> unreachableVariables;
|
||||
for (auto&& [function, stackTooDeepErrors]: _stackTooDeepErrors)
|
||||
// TODO: choose wisely.
|
||||
for (auto const& stackTooDeepError: stackTooDeepErrors)
|
||||
unreachableVariables[function] += stackTooDeepError.variableChoices | ranges::views::take(stackTooDeepError.deficit) | ranges::to<set<YulString>>;
|
||||
run(_context, _object, unreachableVariables);
|
||||
}
|
||||
|
||||
void StackLimitEvader::run(
|
||||
OptimiserStepContext& _context,
|
||||
Object& _object,
|
||||
@ -150,7 +193,7 @@ void StackLimitEvader::run(
|
||||
if (_unreachableVariables.count(function))
|
||||
return;
|
||||
|
||||
map<YulString, FunctionDefinition const*> functionDefinitions = FunctionDefinitionCollector::run(*_object.code);
|
||||
map<YulString, FunctionDefinition const*> functionDefinitions = allFunctionDefinitions(*_object.code);
|
||||
|
||||
MemoryOffsetAllocator memoryOffsetAllocator{_unreachableVariables, callGraph.functionCalls, functionDefinitions};
|
||||
uint64_t requiredSlots = memoryOffsetAllocator.run();
|
||||
|
@ -22,6 +22,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <libyul/optimiser/OptimiserStep.h>
|
||||
#include <libyul/backends/evm/StackLayoutGenerator.h>
|
||||
|
||||
namespace solidity::yul
|
||||
{
|
||||
@ -61,6 +62,25 @@ public:
|
||||
Object& _object,
|
||||
std::map<YulString, std::set<YulString>> const& _unreachableVariables
|
||||
);
|
||||
/// @a _stackTooDeepErrors can be determined by the StackLayoutGenerator.
|
||||
/// Can only be run on the EVM dialect with objects.
|
||||
/// Abort and do nothing, if no ``memoryguard`` call or several ``memoryguard`` calls
|
||||
/// with non-matching arguments are found, or if any of the @a _stackTooDeepErrors
|
||||
/// are contained in a recursive function.
|
||||
static void run(
|
||||
OptimiserStepContext& _context,
|
||||
Object& _object,
|
||||
std::map<YulString, std::vector<StackLayoutGenerator::StackTooDeep>> const& _stackTooDeepErrors
|
||||
);
|
||||
/// Determines stack too deep errors using the appropriate code generation backend.
|
||||
/// Can only be run on the EVM dialect with objects.
|
||||
/// Abort and do nothing, if no ``memoryguard`` call or several ``memoryguard`` calls
|
||||
/// with non-matching arguments are found, or if any of the unreachable variables
|
||||
/// are contained in a recursive function.
|
||||
static void run(
|
||||
OptimiserStepContext& _context,
|
||||
Object& _object
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <libyul/optimiser/StackToMemoryMover.h>
|
||||
#include <libyul/optimiser/FunctionDefinitionCollector.h>
|
||||
#include <libyul/optimiser/NameCollector.h>
|
||||
#include <libyul/optimiser/NameDispenser.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
|
||||
@ -87,7 +87,7 @@ void StackToMemoryMover::run(
|
||||
_context,
|
||||
memoryOffsetTracker,
|
||||
util::applyMap(
|
||||
FunctionDefinitionCollector::run(_block),
|
||||
allFunctionDefinitions(_block),
|
||||
util::mapTuple([](YulString _name, FunctionDefinition const* _funDef) {
|
||||
return make_pair(_name, _funDef->returnVariables);
|
||||
}),
|
||||
|
@ -95,6 +95,12 @@ void OptimiserSuite::run(
|
||||
set<YulString> const& _externallyUsedIdentifiers
|
||||
)
|
||||
{
|
||||
EVMDialect const* evmDialect = dynamic_cast<EVMDialect const*>(&_dialect);
|
||||
bool usesOptimizedCodeGenerator =
|
||||
_optimizeStackAllocation &&
|
||||
evmDialect &&
|
||||
evmDialect->evmVersion().canOverchargeGasForCall() &&
|
||||
evmDialect->providesObjectAccess();
|
||||
set<YulString> reservedIdentifiers = _externallyUsedIdentifiers;
|
||||
reservedIdentifiers += _dialect.fixedFunctionNames();
|
||||
|
||||
@ -105,7 +111,10 @@ void OptimiserSuite::run(
|
||||
)(*_object.code));
|
||||
Block& ast = *_object.code;
|
||||
|
||||
OptimiserSuite suite(_dialect, reservedIdentifiers, Debug::None, ast, _expectedExecutionsPerDeployment);
|
||||
NameDispenser dispenser{_dialect, ast, reservedIdentifiers};
|
||||
OptimiserStepContext context{_dialect, dispenser, reservedIdentifiers, _expectedExecutionsPerDeployment};
|
||||
|
||||
OptimiserSuite suite(context, Debug::None);
|
||||
|
||||
// Some steps depend on properties ensured by FunctionHoister, BlockFlattener, FunctionGrouper and
|
||||
// ForLoopInitRewriter. Run them first to be able to run arbitrary sequences safely.
|
||||
@ -121,24 +130,32 @@ void OptimiserSuite::run(
|
||||
|
||||
// We ignore the return value because we will get a much better error
|
||||
// message once we perform code generation.
|
||||
StackCompressor::run(
|
||||
_dialect,
|
||||
_object,
|
||||
_optimizeStackAllocation,
|
||||
stackCompressorMaxIterations
|
||||
);
|
||||
if (!usesOptimizedCodeGenerator)
|
||||
StackCompressor::run(
|
||||
_dialect,
|
||||
_object,
|
||||
_optimizeStackAllocation,
|
||||
stackCompressorMaxIterations
|
||||
);
|
||||
suite.runSequence("fDnTOc g", ast);
|
||||
|
||||
if (EVMDialect const* dialect = dynamic_cast<EVMDialect const*>(&_dialect))
|
||||
if (evmDialect)
|
||||
{
|
||||
yulAssert(_meter, "");
|
||||
ConstantOptimiser{*dialect, *_meter}(ast);
|
||||
if (dialect->providesObjectAccess() && _optimizeStackAllocation)
|
||||
StackLimitEvader::run(suite.m_context, _object, CompilabilityChecker{
|
||||
ConstantOptimiser{*evmDialect, *_meter}(ast);
|
||||
if (usesOptimizedCodeGenerator)
|
||||
{
|
||||
StackCompressor::run(
|
||||
_dialect,
|
||||
_object,
|
||||
_optimizeStackAllocation
|
||||
}.unreachableVariables);
|
||||
_optimizeStackAllocation,
|
||||
stackCompressorMaxIterations
|
||||
);
|
||||
if (evmDialect->providesObjectAccess())
|
||||
StackLimitEvader::run(suite.m_context, _object);
|
||||
}
|
||||
else if (evmDialect->providesObjectAccess() && _optimizeStackAllocation)
|
||||
StackLimitEvader::run(suite.m_context, _object);
|
||||
}
|
||||
else if (dynamic_cast<WasmDialect const*>(&_dialect))
|
||||
{
|
||||
@ -148,7 +165,7 @@ void OptimiserSuite::run(
|
||||
ast.statements.erase(ast.statements.begin());
|
||||
}
|
||||
|
||||
suite.m_dispenser.reset(ast);
|
||||
dispenser.reset(ast);
|
||||
NameSimplifier::run(suite.m_context, ast);
|
||||
VarNameCleaner::run(suite.m_context, ast);
|
||||
|
||||
|
@ -59,6 +59,8 @@ public:
|
||||
PrintStep,
|
||||
PrintChanges
|
||||
};
|
||||
OptimiserSuite(OptimiserStepContext& _context, Debug _debug = Debug::None): m_context(_context), m_debug(_debug) {}
|
||||
|
||||
/// The value nullopt for `_expectedExecutionsPerDeployment` represents creation code.
|
||||
static void run(
|
||||
Dialect const& _dialect,
|
||||
@ -82,20 +84,7 @@ public:
|
||||
static std::map<char, std::string> const& stepAbbreviationToNameMap();
|
||||
|
||||
private:
|
||||
OptimiserSuite(
|
||||
Dialect const& _dialect,
|
||||
std::set<YulString> const& _externallyUsedIdentifiers,
|
||||
Debug _debug,
|
||||
Block& _ast,
|
||||
std::optional<size_t> expectedExecutionsPerDeployment
|
||||
):
|
||||
m_dispenser{_dialect, _ast, _externallyUsedIdentifiers},
|
||||
m_context{_dialect, m_dispenser, _externallyUsedIdentifiers, expectedExecutionsPerDeployment},
|
||||
m_debug(_debug)
|
||||
{}
|
||||
|
||||
NameDispenser m_dispenser;
|
||||
OptimiserStepContext m_context;
|
||||
OptimiserStepContext& m_context;
|
||||
Debug m_debug;
|
||||
};
|
||||
|
||||
|
@ -6,8 +6,7 @@ MAINTAINER chriseth <chris@ethereum.org>
|
||||
WORKDIR /solidity
|
||||
|
||||
# Build dependencies
|
||||
ADD /scripts/install_deps.sh /solidity/scripts/install_deps.sh
|
||||
RUN ./scripts/install_deps.sh
|
||||
RUN apk update && apk add boost-dev boost-static build-base cmake git
|
||||
|
||||
#Copy working directory on travis to the image
|
||||
COPY / $WORKDIR
|
||||
|
@ -34,7 +34,7 @@ else
|
||||
BUILD_DIR="$1"
|
||||
fi
|
||||
|
||||
# solbuildpackpusher/solidity-buildpack-deps:emscripten-6
|
||||
# solbuildpackpusher/solidity-buildpack-deps:emscripten-7
|
||||
docker run -v "$(pwd):/root/project" -w /root/project \
|
||||
solbuildpackpusher/solidity-buildpack-deps@sha256:092da5817bc032c91a806b4f73db2a1a31e5cc4c066d94d43eedd9f365df7154 \
|
||||
solbuildpackpusher/solidity-buildpack-deps@sha256:9ffcd0944433fe100e9433f2aa9ba5c21e096e758ad8a05a4a76feaed3d1f463 \
|
||||
./scripts/ci/build_emscripten.sh "$BUILD_DIR"
|
||||
|
@ -35,7 +35,7 @@ then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function preparedGrep()
|
||||
function preparedGrep
|
||||
{
|
||||
git grep -nIE "$1" -- '*.h' '*.cpp' | grep -v "${EXCLUDE_FILES_JOINED}"
|
||||
return $?
|
||||
|
@ -68,10 +68,6 @@ emcmake cmake \
|
||||
-DTESTS=0 \
|
||||
..
|
||||
make soljson
|
||||
# Patch soljson.js for backwards compatibility.
|
||||
# TODO: remove this with 0.7.
|
||||
# "viiiii" encodes the signature of the callback function.
|
||||
sed -i -e 's/addFunction(func,sig){/addFunction(func,sig){sig=sig||"viiiii";/' libsolc/soljson.js
|
||||
|
||||
cd ..
|
||||
mkdir -p upload
|
||||
|
@ -5,7 +5,7 @@ ROOTDIR="/root/project"
|
||||
BUILDDIR="${ROOTDIR}/build"
|
||||
mkdir -p "${BUILDDIR}" && mkdir -p "$BUILDDIR/deps"
|
||||
|
||||
generate_protobuf_bindings()
|
||||
function generate_protobuf_bindings
|
||||
{
|
||||
cd "${ROOTDIR}"/test/tools/ossfuzz
|
||||
# Generate protobuf C++ bindings
|
||||
@ -15,7 +15,7 @@ generate_protobuf_bindings()
|
||||
done
|
||||
}
|
||||
|
||||
build_fuzzers()
|
||||
function build_fuzzers
|
||||
{
|
||||
cd "${BUILDDIR}"
|
||||
cmake .. -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-Release}" \
|
||||
|
@ -1,11 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
function error() {
|
||||
function error
|
||||
{
|
||||
echo >&2 "ERROR: ${1} Aborting." && false
|
||||
}
|
||||
|
||||
function warning() {
|
||||
function warning
|
||||
{
|
||||
echo >&2 "WARNING: ${1}"
|
||||
}
|
||||
|
||||
|
@ -29,15 +29,15 @@ _initial_work_dir=$(pwd)
|
||||
if [ "$CIRCLECI" ]
|
||||
then
|
||||
export TERM="${TERM:-xterm}"
|
||||
function printTask() { echo "$(tput bold)$(tput setaf 2)$1$(tput setaf 7)"; }
|
||||
function printError() { >&2 echo "$(tput setaf 1)$1$(tput setaf 7)"; }
|
||||
function printWarning() { >&2 echo "$(tput setaf 11)$1$(tput setaf 7)"; }
|
||||
function printLog() { echo "$(tput setaf 3)$1$(tput setaf 7)"; }
|
||||
function printTask { echo "$(tput bold)$(tput setaf 2)$1$(tput setaf 7)"; }
|
||||
function printError { >&2 echo "$(tput setaf 1)$1$(tput setaf 7)"; }
|
||||
function printWarning { >&2 echo "$(tput setaf 11)$1$(tput setaf 7)"; }
|
||||
function printLog { echo "$(tput setaf 3)$1$(tput setaf 7)"; }
|
||||
else
|
||||
function printTask() { echo "$(tput bold)$(tput setaf 2)$1$(tput sgr0)"; }
|
||||
function printError() { >&2 echo "$(tput setaf 1)$1$(tput sgr0)"; }
|
||||
function printWarning() { >&2 echo "$(tput setaf 11)$1$(tput sgr0)"; }
|
||||
function printLog() { echo "$(tput setaf 3)$1$(tput sgr0)"; }
|
||||
function printTask { echo "$(tput bold)$(tput setaf 2)$1$(tput sgr0)"; }
|
||||
function printError { >&2 echo "$(tput setaf 1)$1$(tput sgr0)"; }
|
||||
function printWarning { >&2 echo "$(tput setaf 11)$1$(tput sgr0)"; }
|
||||
function printLog { echo "$(tput setaf 3)$1$(tput sgr0)"; }
|
||||
fi
|
||||
|
||||
function printStackTrace
|
||||
@ -78,7 +78,7 @@ function printStackTrace
|
||||
done
|
||||
}
|
||||
|
||||
function fail()
|
||||
function fail
|
||||
{
|
||||
printError "$@"
|
||||
|
||||
@ -86,7 +86,7 @@ function fail()
|
||||
return 1
|
||||
}
|
||||
|
||||
function assertFail()
|
||||
function assertFail
|
||||
{
|
||||
printError ""
|
||||
(( $# == 0 )) && printError "Assertion failed."
|
||||
@ -97,7 +97,7 @@ function assertFail()
|
||||
exit 2
|
||||
}
|
||||
|
||||
function msg_on_error()
|
||||
function msg_on_error
|
||||
{
|
||||
local error_message
|
||||
local no_stdout=false
|
||||
@ -171,7 +171,7 @@ function msg_on_error()
|
||||
fi
|
||||
}
|
||||
|
||||
safe_kill()
|
||||
function safe_kill
|
||||
{
|
||||
local PID=${1}
|
||||
local NAME=${2:-${1}}
|
||||
|
@ -22,7 +22,8 @@
|
||||
YULARGS=(--strict-assembly)
|
||||
FULLARGS=(--optimize --combined-json "abi,asm,ast,bin,bin-runtime,devdoc,hashes,metadata,opcodes,srcmap,srcmap-runtime,userdoc")
|
||||
OLDARGS=(--optimize --combined-json "abi,asm,ast,bin,bin-runtime,devdoc,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc")
|
||||
function compileFull()
|
||||
|
||||
function compileFull
|
||||
{
|
||||
local expected_exit_code=0
|
||||
local expect_output='none'
|
||||
|
@ -26,10 +26,14 @@
|
||||
# contains a Makefile in the docker/ subdirectory that can be used to create the
|
||||
# required base image using:
|
||||
#
|
||||
# make version=2.0.12 build
|
||||
# make version=2.0.33 build
|
||||
#
|
||||
FROM emscripten/emsdk:2.0.12 AS base
|
||||
LABEL version="6"
|
||||
# Note that emscripten is supposed to automatically install to $(em-config CACHE)/sysroot, but
|
||||
# apparently this currently breaks due to conflicting compatibility headers.
|
||||
# Using $(em-config CACHE)/sysroot/usr seems to work, though, and still has cmake find the
|
||||
# dependencies automatically.
|
||||
FROM emscripten/emsdk:2.0.33 AS base
|
||||
LABEL version="7"
|
||||
|
||||
ADD emscripten.jam /usr/src
|
||||
RUN set -ex; \
|
||||
@ -39,8 +43,8 @@ RUN set -ex; \
|
||||
mkdir build; \
|
||||
cd build; \
|
||||
emcmake cmake \
|
||||
-DCMAKE_INSTALL_PREFIX=$(em-config CACHE)/sysroot/usr \
|
||||
-DCMAKE_BUILD_TYPE=MinSizeRel \
|
||||
-DCMAKE_INSTALL_PREFIX=/emsdk/upstream/emscripten/system \
|
||||
-DZ3_BUILD_LIBZ3_SHARED=OFF \
|
||||
-DZ3_ENABLE_EXAMPLE_TARGETS=OFF \
|
||||
-DZ3_BUILD_TEST_EXECUTABLES=OFF \
|
||||
@ -63,5 +67,5 @@ RUN set -ex; \
|
||||
./b2 toolset=emscripten link=static variant=release threading=single runtime-link=static \
|
||||
--with-system --with-filesystem --with-test --with-program_options \
|
||||
cxxflags="-s DISABLE_EXCEPTION_CATCHING=0 -Wno-unused-local-typedef -Wno-variadic-macros -Wno-c99-extensions -Wno-all" \
|
||||
--prefix=/emsdk/upstream/emscripten/system install; \
|
||||
--prefix=$(em-config CACHE)/sysroot/usr install; \
|
||||
rm -r /usr/src/boost_1_75_0
|
||||
|
@ -28,7 +28,7 @@ else
|
||||
date -u +"nightly.%Y.%-m.%-d" > prerelease.txt
|
||||
fi
|
||||
|
||||
tag_and_push()
|
||||
function tag_and_push
|
||||
{
|
||||
docker tag "$image:$1" "$image:$2"
|
||||
docker push "$image:$2"
|
||||
|
@ -36,7 +36,7 @@ source "${REPO_ROOT}/scripts/common_cmdline.sh"
|
||||
|
||||
developmentVersion=$("$REPO_ROOT/scripts/get_version.sh")
|
||||
|
||||
function versionGreater()
|
||||
function versionGreater
|
||||
{
|
||||
v1=$1
|
||||
v2=$2
|
||||
@ -58,7 +58,7 @@ function versionGreater()
|
||||
return 1
|
||||
}
|
||||
|
||||
function versionEqual()
|
||||
function versionEqual
|
||||
{
|
||||
if [[ "$1" == "$2" ]]
|
||||
then
|
||||
@ -67,7 +67,7 @@ function versionEqual()
|
||||
return 1
|
||||
}
|
||||
|
||||
function getAllAvailableVersions()
|
||||
function getAllAvailableVersions
|
||||
{
|
||||
allVersions=()
|
||||
local allListedVersions
|
||||
@ -85,7 +85,7 @@ function getAllAvailableVersions()
|
||||
done
|
||||
}
|
||||
|
||||
function findMinimalVersion()
|
||||
function findMinimalVersion
|
||||
{
|
||||
local f=$1
|
||||
local greater=false
|
||||
|
@ -1,61 +0,0 @@
|
||||
@ECHO OFF
|
||||
|
||||
REM ---------------------------------------------------------------------------
|
||||
REM Batch file for installing pre-requisite packages for solidity on
|
||||
REM Windows platforms. That is implemented using CMake targets which
|
||||
REM extract pre-built ZIPs hosted on GitHub into "deps\install_deps".
|
||||
REM
|
||||
REM See https://github.com/ethereum/cpp-dependencies
|
||||
REM
|
||||
REM The CMake files then point into that directory as an alternative
|
||||
REM to the Homebrew, PPA or other global package server locations
|
||||
REM available on Linux and UNIX platforms.
|
||||
REM
|
||||
REM The lack of a standard C++ packaging system for Windows is problematic
|
||||
REM for us, and we have considered various options for improving the
|
||||
REM situation, such as the following:
|
||||
REM
|
||||
REM See "Windows - Add support for Chocolatey packages"
|
||||
REM https://github.com/ethereum/webthree-umbrella/issues/345
|
||||
REM
|
||||
REM See "Windows - Try to use NuGet C++ packages"
|
||||
REM https://github.com/ethereum/webthree-umbrella/issues/509
|
||||
REM
|
||||
REM See "CM - Can we switch to NuGet delivery for our external dependencies"
|
||||
REM https://github.com/ethereum/webthree-umbrella/issues/376
|
||||
REM
|
||||
REM Another possible option, which would benefit build robustness on
|
||||
REM multiple platforms, not just Windows, is to add dependencies as
|
||||
REM git-submodules (or downloading on demand) so that we aren'targets
|
||||
REM depend on platform-specific packaging systems at all. We have
|
||||
REM already done just that for LLVM within evmjit. The downside of
|
||||
REM that approach is that those dependencies then need to be
|
||||
REM built-from-source, which adds time to the build process. It
|
||||
REM gives us an unbeatable degree of control, though, because we
|
||||
REM then perfectly control versioning and build flags for the binaries
|
||||
REM for those packages.
|
||||
REM
|
||||
REM The documentation for solidity is hosted at:
|
||||
REM
|
||||
REM https://docs.soliditylang.org
|
||||
REM
|
||||
REM ---------------------------------------------------------------------------
|
||||
REM This file is part of solidity.
|
||||
REM
|
||||
REM solidity is free software: you can redistribute it and/or modify
|
||||
REM it under the terms of the GNU General Public License as published by
|
||||
REM the Free Software Foundation, either version 3 of the License, or
|
||||
REM (at your option) any later version.
|
||||
REM
|
||||
REM solidity is distributed in the hope that it will be useful,
|
||||
REM but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
REM MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
REM GNU General Public License for more details.
|
||||
REM
|
||||
REM You should have received a copy of the GNU General Public License
|
||||
REM along with solidity. If not, see <http://www.gnu.org/licenses/>
|
||||
REM
|
||||
REM Copyright (c) 2016 solidity contributors.
|
||||
REM ---------------------------------------------------------------------------
|
||||
|
||||
cmake -P scripts\install_deps.cmake
|
@ -1,99 +0,0 @@
|
||||
get_filename_component(ROOT_DIR "${CMAKE_CURRENT_LIST_DIR}/../deps" ABSOLUTE)
|
||||
|
||||
set(CACHE_DIR "${ROOT_DIR}/cache")
|
||||
set(PACKAGES_DIR "${ROOT_DIR}/packages")
|
||||
|
||||
function(download URL DST_FILE STATUS)
|
||||
set(TMP_FILE "${DST_FILE}.part")
|
||||
|
||||
get_filename_component(FILE_NAME ${DST_FILE} NAME)
|
||||
if (NOT EXISTS ${DST_FILE})
|
||||
message("Downloading ${FILE_NAME}")
|
||||
file(DOWNLOAD ${URL} ${TMP_FILE} SHOW_PROGRESS STATUS DOWNLOAD_STATUS)
|
||||
list(GET DOWNLOAD_STATUS 0 STATUS_CODE)
|
||||
if (STATUS_CODE EQUAL 0)
|
||||
file(RENAME ${TMP_FILE} ${DST_FILE})
|
||||
else()
|
||||
file(REMOVE ${TMP_FILE})
|
||||
list(GET DOWNLOAD_STATUS 1 ERROR_MSG)
|
||||
|
||||
message("ERROR! Downloading '${FILE_NAME}' failed.")
|
||||
message(STATUS "URL: ${URL}")
|
||||
message(STATUS "Error: ${STATUS_CODE} ${ERROR_MSG}")
|
||||
set(STATUS FALSE PARENT_SCOPE)
|
||||
return()
|
||||
endif()
|
||||
else()
|
||||
message("Using cached ${FILE_NAME}")
|
||||
endif()
|
||||
set(STATUS TRUE PARENT_SCOPE)
|
||||
endfunction(download)
|
||||
|
||||
function(download_and_unpack PACKAGE_URL DST_DIR)
|
||||
get_filename_component(FILE_NAME ${PACKAGE_URL} NAME)
|
||||
|
||||
set(DST_FILE "${CACHE_DIR}/${FILE_NAME}")
|
||||
set(TMP_FILE "${DST_FILE}.part")
|
||||
|
||||
file(MAKE_DIRECTORY ${CACHE_DIR})
|
||||
file(MAKE_DIRECTORY ${DST_DIR})
|
||||
|
||||
download(${PACKAGE_URL} ${DST_FILE} STATUS)
|
||||
|
||||
if (STATUS)
|
||||
message("Unpacking ${FILE_NAME} to ${DST_DIR}")
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E tar -xf ${DST_FILE}
|
||||
WORKING_DIRECTORY ${DST_DIR})
|
||||
endif()
|
||||
endfunction(download_and_unpack)
|
||||
|
||||
# Packs installed package binaries and headers into an archive.
|
||||
function(create_package NAME DIR)
|
||||
message("Creating package ${NAME}")
|
||||
file(MAKE_DIRECTORY ${PACKAGES_DIR})
|
||||
|
||||
# To create an archive without addicional top level directory
|
||||
# (like package-X.Y.Z) we need to know all top level files/dirs.
|
||||
# Usually it is just "win64" dir.
|
||||
file(GLOB TOP_FILES RELATIVE ${DIR} "${DIR}/*")
|
||||
|
||||
set(PACKAGE_FILE "${PACKAGES_DIR}/${NAME}.tar.gz")
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E
|
||||
tar -czf ${PACKAGE_FILE} ${TOP_FILES}
|
||||
WORKING_DIRECTORY ${DIR})
|
||||
endfunction(create_package)
|
||||
|
||||
# Downloads the source code of the package and unpacks it to dedicated 'src'
|
||||
# dir. Also creates 'build' and 'install' dir to be used by a build script.
|
||||
function(prepare_package_source NAME VERSION URL)
|
||||
set(PACKAGE_NAME "${NAME}-${VERSION}")
|
||||
|
||||
set(PACKAGE_DIR "${CACHE_DIR}/${PACKAGE_NAME}")
|
||||
set(SOURCE_DIR "${PACKAGE_DIR}/src")
|
||||
set(BUILD_DIR "${PACKAGE_DIR}/build")
|
||||
set(INSTALL_DIR "${PACKAGE_DIR}/install")
|
||||
|
||||
if (NOT EXISTS ${SOURCE_DIR})
|
||||
download_and_unpack(${URL} ${PACKAGE_DIR} STATUS)
|
||||
file(GLOB ORIG_SOURCE_DIR_NAME "${PACKAGE_DIR}/*")
|
||||
file(RENAME ${ORIG_SOURCE_DIR_NAME} ${SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
file(MAKE_DIRECTORY ${BUILD_DIR})
|
||||
file(MAKE_DIRECTORY ${INSTALL_DIR})
|
||||
|
||||
# Export names and dirs to be used by a package-specific build script.
|
||||
set(PACKAGE_NAME ${PACKAGE_NAME} PARENT_SCOPE)
|
||||
set(SOURCE_DIR ${SOURCE_DIR} PARENT_SCOPE)
|
||||
set(BUILD_DIR ${BUILD_DIR} PARENT_SCOPE)
|
||||
set(INSTALL_DIR ${INSTALL_DIR} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
set(INSTALL_DIR "${ROOT_DIR}/install")
|
||||
set(SERVER "https://github.com/ethereum/cpp-dependencies/releases/download/vs2017/")
|
||||
|
||||
function(download_and_install PACKAGE_NAME)
|
||||
download_and_unpack("${SERVER}${PACKAGE_NAME}.tar.gz" ${INSTALL_DIR})
|
||||
endfunction(download_and_install)
|
||||
|
||||
download_and_install("boost-1.67.0")
|
@ -1,400 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Shell script for installing pre-requisite packages for solidity on a
|
||||
# variety of Linux and other UNIX-derived platforms.
|
||||
#
|
||||
# This is an "infrastucture-as-code" alternative to the manual build
|
||||
# instructions pages which we previously maintained at:
|
||||
# https://docs.soliditylang.org/en/latest/installing-solidity.html
|
||||
#
|
||||
# The aim of this script is to simplify things down to the following basic
|
||||
# flow for all supported operating systems:
|
||||
#
|
||||
# - git clone --recursive
|
||||
# - ./scripts/install_deps.sh
|
||||
# - cmake && make
|
||||
#
|
||||
# TODO - There is no support here yet for cross-builds in any form, only
|
||||
# native builds. Expanding the functionality here to cover the mobile,
|
||||
# wearable and SBC platforms covered by doublethink and EthEmbedded would
|
||||
# also bring in support for Android, iOS, watchOS, tvOS, Tizen, Sailfish,
|
||||
# Maemo, MeeGo and Yocto.
|
||||
#
|
||||
# The documentation for solidity is hosted at:
|
||||
#
|
||||
# https://docs.soliditylang.org
|
||||
#
|
||||
# ------------------------------------------------------------------------------
|
||||
# This file is part of solidity.
|
||||
#
|
||||
# solidity is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# solidity is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with solidity. If not, see <http://www.gnu.org/licenses/>
|
||||
#
|
||||
# (c) 2016 solidity contributors.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
set -e
|
||||
|
||||
# Check for 'uname' and abort if it is not available.
|
||||
uname -v > /dev/null 2>&1 || { echo >&2 "ERROR - solidity requires 'uname' to identify the platform."; exit 1; }
|
||||
|
||||
# See http://unix.stackexchange.com/questions/92199/how-can-i-reliably-get-the-operating-systems-name
|
||||
detect_linux_distro() {
|
||||
if [ "$(command -v lsb_release)" ]; then
|
||||
DISTRO=$(lsb_release -is)
|
||||
elif [ -f /etc/os-release ]; then
|
||||
# extract 'foo' from NAME=foo, only on the line with NAME=foo
|
||||
DISTRO=$(sed -n -e 's/^NAME="\?\([^"]*\)"\?$/\1/p' /etc/os-release)
|
||||
elif [ -f /etc/centos-release ]; then
|
||||
DISTRO=CentOS
|
||||
else
|
||||
DISTRO=''
|
||||
fi
|
||||
echo "$DISTRO"
|
||||
}
|
||||
|
||||
case $(uname -s) in
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# macOS
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
Darwin)
|
||||
case $(sw_vers -productVersion | awk -F . '{print $1"."$2}') in
|
||||
10.9)
|
||||
echo "Installing solidity dependencies on OS X 10.9 Mavericks."
|
||||
;;
|
||||
10.10)
|
||||
echo "Installing solidity dependencies on OS X 10.10 Yosemite."
|
||||
;;
|
||||
10.11)
|
||||
echo "Installing solidity dependencies on OS X 10.11 El Capitan."
|
||||
;;
|
||||
10.12)
|
||||
echo "Installing solidity dependencies on macOS 10.12 Sierra."
|
||||
;;
|
||||
10.13)
|
||||
echo "Installing solidity dependencies on macOS 10.13 High Sierra."
|
||||
;;
|
||||
10.14)
|
||||
echo "Installing solidity dependencies on macOS 10.14 Mojave."
|
||||
;;
|
||||
10.15)
|
||||
echo "Installing solidity dependencies on macOS 10.15 Catalina."
|
||||
;;
|
||||
11.0 | 11.1 | 11.2 | 11.3 | 11.4 | 11.5)
|
||||
echo "Installing solidity dependencies on macOS 11.0 / 11.1 / 11.2 / 11.3 / 11.4 / 11.5 Big Sur."
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported macOS version."
|
||||
echo "We only support Mavericks, Yosemite, El Capitan, Sierra, High Sierra, Mojave, Catalina, and Big Sur."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Check for Homebrew install and abort if it is not installed.
|
||||
brew -v > /dev/null 2>&1 || { echo >&2 "ERROR - solidity requires a Homebrew install. See https://brew.sh."; exit 1; }
|
||||
brew update
|
||||
brew install boost
|
||||
brew install cmake
|
||||
if [ "$CI" = true ]; then
|
||||
brew upgrade cmake
|
||||
else
|
||||
brew upgrade
|
||||
fi
|
||||
|
||||
;;
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# FreeBSD
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
FreeBSD)
|
||||
echo "Installing solidity dependencies on FreeBSD."
|
||||
echo "ERROR - 'install_deps.sh' doesn't have FreeBSD support yet."
|
||||
echo "Please let us know if you see this error message, and we can work out what is missing."
|
||||
echo "Drop us a message at https://gitter.im/ethereum/solidity-dev."
|
||||
exit 1
|
||||
;;
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Linux
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
Linux)
|
||||
case $(detect_linux_distro) in
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Arch Linux
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
Arch*|ManjaroLinux)
|
||||
#Arch
|
||||
echo "Installing solidity dependencies on Arch Linux."
|
||||
|
||||
# All our dependencies can be found in the Arch Linux official repositories.
|
||||
# See https://wiki.archlinux.org/index.php/Official_repositories
|
||||
sudo pacman -Syu \
|
||||
base-devel \
|
||||
boost \
|
||||
cmake \
|
||||
git \
|
||||
cvc4
|
||||
;;
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Alpine Linux
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
"Alpine Linux")
|
||||
#Alpine
|
||||
echo "Installing solidity dependencies on Alpine Linux."
|
||||
|
||||
# All our dependencies can be found in the Alpine Linux official repositories.
|
||||
# See https://pkgs.alpinelinux.org/
|
||||
|
||||
apk update
|
||||
apk add boost-dev boost-static build-base cmake git
|
||||
|
||||
;;
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Debian
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
Debian*|Raspbian)
|
||||
#Debian
|
||||
# shellcheck disable=SC1091
|
||||
. /etc/os-release
|
||||
install_z3=""
|
||||
case $VERSION_ID in
|
||||
8)
|
||||
#jessie
|
||||
echo "Installing solidity dependencies on Debian Jesse (8.x)."
|
||||
;;
|
||||
9)
|
||||
#stretch
|
||||
echo "Installing solidity dependencies on Debian Stretch (9.x)."
|
||||
install_z3="libz3-dev"
|
||||
;;
|
||||
10)
|
||||
#buster
|
||||
echo "Installing solidity dependencies on Debian Buster (10.x)."
|
||||
install_z3="libz3-dev"
|
||||
;;
|
||||
*)
|
||||
#other Debian
|
||||
echo "Installing solidity dependencies on unknown Debian version."
|
||||
echo "ERROR - This might not work, but we are trying anyway."
|
||||
echo "Drop us a message at https://gitter.im/ethereum/solidity-dev"
|
||||
install_z3="libz3-dev"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Install "normal packages"
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install \
|
||||
build-essential \
|
||||
cmake \
|
||||
g++ \
|
||||
gcc \
|
||||
git \
|
||||
libboost-all-dev \
|
||||
unzip \
|
||||
"$install_z3"
|
||||
|
||||
|
||||
;;
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Fedora
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
Fedora)
|
||||
#Fedora
|
||||
echo "Installing solidity dependencies on Fedora."
|
||||
|
||||
# Install "normal packages"
|
||||
# See https://fedoraproject.org/wiki/Package_management_system.
|
||||
dnf install \
|
||||
autoconf \
|
||||
automake \
|
||||
boost-devel \
|
||||
boost-static \
|
||||
cmake \
|
||||
gcc \
|
||||
gcc-c++ \
|
||||
git \
|
||||
libtool
|
||||
|
||||
;;
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# OpenSUSE
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
"openSUSE project")
|
||||
#openSUSE
|
||||
echo "Installing solidity dependencies on openSUSE."
|
||||
echo "ERROR - 'install_deps.sh' doesn't have openSUSE support yet."
|
||||
echo "See https://docs.soliditylang.org/en/latest/installing-solidity.html for manual instructions."
|
||||
echo "If you would like to get 'install_deps.sh' working for openSUSE, that would be fantastic."
|
||||
echo "See https://github.com/ethereum/webthree-umbrella/issues/552."
|
||||
exit 1
|
||||
;;
|
||||
#------------------------------------------------------------------------------
|
||||
# Ubuntu
|
||||
#
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
Ubuntu|LinuxMint|Pop)
|
||||
#LinuxMint is a distro on top of Ubuntu.
|
||||
#Ubuntu
|
||||
install_z3=""
|
||||
case $(lsb_release -cs) in
|
||||
trusty|qiana|rebecca|rafaela|rosa)
|
||||
echo "Installing solidity dependencies on Ubuntu Trusty Tahr (14.04)."
|
||||
echo "Or, you may also be running Linux Mint Qiana / Rebecca / Rafaela / Rosa (base: Ubuntu Trusty Tahr (14.04).)"
|
||||
;;
|
||||
xenial|sarah|serena|sonya|sylvia)
|
||||
echo "Installing solidity dependencies on Ubuntu Xenial Xerus (16.04)."
|
||||
echo "Or, you may also be running Linux Mint Sarah / Serena / Sonya / Sylvia (base: Ubuntu Xenial Xerus (16.04).)"
|
||||
install_z3="libz3-dev"
|
||||
;;
|
||||
bionic)
|
||||
echo "Installing solidity dependencies."
|
||||
install_z3="libz3-dev"
|
||||
;;
|
||||
focal)
|
||||
echo "Installing solidity dependencies."
|
||||
install_z3="libz3-dev"
|
||||
;;
|
||||
hirsute)
|
||||
echo "Installing solidity dependencies."
|
||||
install_z3="libz3-dev"
|
||||
;;
|
||||
betsy)
|
||||
#do not try anything for betsy.
|
||||
echo "Linux Mint Betsy is not supported at the moment as it runs off of Debian."
|
||||
echo "We only support Sylvia, Sonya, Serena, Sarah, Rosa, Rafaela, Rebecca, and Qiana."
|
||||
echo "See https://docs.soliditylang.org/en/latest/installing-solidity.html for manual instructions."
|
||||
echo "If you would like to get your distro working, that would be fantastic."
|
||||
echo "Drop us a message at https://gitter.im/ethereum/solidity-dev."
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
#other Ubuntu
|
||||
echo "ERROR - Unknown or unsupported Ubuntu version ($(lsb_release -cs))"
|
||||
echo "ERROR - This might not work, but we are trying anyway."
|
||||
echo "Please drop us a message at https://gitter.im/ethereum/solidity-dev."
|
||||
echo "We only support Trusty, Xenial, Bionic, Focal, and Hirsute."
|
||||
install_z3="libz3-dev"
|
||||
;;
|
||||
esac
|
||||
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install \
|
||||
build-essential \
|
||||
cmake \
|
||||
git \
|
||||
libboost-all-dev \
|
||||
"$install_z3"
|
||||
if [ "$CI" = true ]; then
|
||||
# install Z3 from PPA if the distribution does not provide it
|
||||
if ! dpkg -l libz3-dev > /dev/null 2>&1
|
||||
then
|
||||
sudo apt-add-repository -y ppa:hvr/z3
|
||||
sudo apt-get -y update
|
||||
sudo apt-get -y install libz3-dev
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# CentOS
|
||||
# CentOS needs some more testing. This is the general idea of packages
|
||||
# needed, but some tweaking/improvements can definitely happen
|
||||
#------------------------------------------------------------------------------
|
||||
CentOS*)
|
||||
echo "Attention: CentOS 7 is currently not supported!";
|
||||
# FIXME: read -p and [[ ]] are bash features but our shebang says we're using sh
|
||||
# shellcheck disable=SC2039
|
||||
read -p "This script will heavily modify your system in order to allow for compilation of Solidity. Are you sure? [Y/N]" -n 1 -r
|
||||
# shellcheck disable=SC2039
|
||||
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
||||
# Make Sure we have the EPEL repos
|
||||
sudo yum -y install epel-release
|
||||
# Get g++ 4.8
|
||||
sudo rpm --import http://linuxsoft.cern.ch/cern/slc6X/i386/RPM-GPG-KEY-cern
|
||||
wget -O /etc/yum.repos.d/slc6-devtoolset.repo http://linuxsoft.cern.ch/cern/devtoolset/slc6-devtoolset.repo
|
||||
sudo yum -y install devtoolset-2-gcc devtoolset-2-gcc-c++ devtoolset-2-binutils
|
||||
|
||||
# Enable the devtoolset2 usage so global gcc/g++ become the 4.8 one.
|
||||
# As per https://gist.github.com/stephenturner/e3bc5cfacc2dc67eca8b, what you should do afterwards is
|
||||
# to add this line:
|
||||
# source /opt/rh/devtoolset-2/enable
|
||||
# to your bashrc so that this happens automatically at login
|
||||
scl enable devtoolset-2 bash
|
||||
|
||||
# Get cmake
|
||||
sudo yum -y remove cmake
|
||||
sudo yum -y install cmake3
|
||||
sudo ln -s /usr/bin/cmake3 /usr/bin/cmake
|
||||
|
||||
# Get latest boost thanks to this guy: http://vicendominguez.blogspot.de/2014/04/boost-c-library-rpm-packages-for-centos.html
|
||||
sudo yum -y remove boost-devel
|
||||
sudo wget https://bintray.com/vicendominguez/CentOS6/rpm -O /etc/yum.repos.d/bintray-vicendominguez-CentOS6.repo
|
||||
sudo yum install boost-devel
|
||||
else
|
||||
echo "Aborted CentOS Solidity Dependency Installation";
|
||||
exit 1
|
||||
fi
|
||||
|
||||
;;
|
||||
|
||||
|
||||
|
||||
|
||||
*)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Other (unknown) Linux
|
||||
# Major and medium distros which we are missing would include Mint, CentOS,
|
||||
# RHEL, Raspbian, Cygwin, OpenWrt, gNewSense, Trisquel and SteamOS.
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#other Linux
|
||||
echo "ERROR - Unsupported or unidentified Linux distro."
|
||||
echo "See https://docs.soliditylang.org/en/latest/installing-solidity.html for manual instructions."
|
||||
echo "If you would like to get your distro working, that would be fantastic."
|
||||
echo "Drop us a message at https://gitter.im/ethereum/solidity-dev."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Other platform (not Linux, FreeBSD or macOS).
|
||||
# Not sure what might end up here?
|
||||
# Maybe OpenBSD, NetBSD, AIX, Solaris, HP-UX?
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
*)
|
||||
#other
|
||||
echo "ERROR - Unsupported or unidentified operating system."
|
||||
echo "See https://docs.soliditylang.org/en/latest/installing-solidity.html for manual instructions."
|
||||
echo "If you would like to get your operating system working, that would be fantastic."
|
||||
echo "Drop us a message at https://gitter.im/ethereum/solidity-dev."
|
||||
;;
|
||||
esac
|
@ -1,11 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eu
|
||||
|
||||
git clone --depth 1 --branch z3-4.8.7 https://github.com/Z3Prover/z3.git
|
||||
cd z3
|
||||
mkdir build
|
||||
cd build
|
||||
LDFLAGS="-static" cmake -DZ3_BUILD_LIBZ3_SHARED=OFF ..
|
||||
make -j 4
|
||||
make install
|
@ -134,7 +134,7 @@ def extract_and_write(path, language):
|
||||
if __name__ == '__main__':
|
||||
script_description = (
|
||||
"Reads Solidity, C++ or RST source files and extracts compilable solidity and yul code blocks from them. "
|
||||
"Can be used to generate test cases to validade code examples. "
|
||||
"Can be used to generate test cases to validate code examples. "
|
||||
)
|
||||
|
||||
parser = ArgumentParser(description=script_description)
|
||||
|
@ -50,14 +50,14 @@
|
||||
# FIXME: Can't use set -u because the old Bash on macOS treats empty arrays as unbound variables
|
||||
set -eo pipefail
|
||||
|
||||
die()
|
||||
function die
|
||||
{
|
||||
# shellcheck disable=SC2059
|
||||
>&2 printf "ERROR: $1\n" "${@:2}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
get_reported_solc_version()
|
||||
function get_reported_solc_version
|
||||
{
|
||||
local solc_binary="$1"
|
||||
|
||||
@ -70,7 +70,7 @@ get_reported_solc_version()
|
||||
echo "$version_banner" | tail -n 1 | sed -n -E 's/^Version: (.*)$/\1/p'
|
||||
}
|
||||
|
||||
validate_reported_version()
|
||||
function validate_reported_version
|
||||
{
|
||||
local reported_version="$1"
|
||||
local expected_version_and_commit="$2"
|
||||
|
@ -8,7 +8,8 @@ BOOST_OPTIONS=()
|
||||
SOLTEST_OPTIONS=()
|
||||
SOLIDITY_BUILD_DIR=${SOLIDITY_BUILD_DIR:-${REPO_ROOT}/build}
|
||||
|
||||
usage() {
|
||||
function usage
|
||||
{
|
||||
echo 2>&1 "
|
||||
Usage: $0 [options] [soltest-options]
|
||||
Runs BOOST C++ unit test program, soltest.
|
||||
|
@ -20,7 +20,7 @@ SGR_BLUE="\033[34m"
|
||||
vt_cursor_up() { echo -ne "\033[A"; }
|
||||
vt_cursor_begin_of_line() { echo -ne "\r"; }
|
||||
|
||||
download_antlr4()
|
||||
function download_antlr4
|
||||
{
|
||||
if [[ ! -e "$ANTLR_JAR" ]]
|
||||
then
|
||||
@ -28,7 +28,7 @@ download_antlr4()
|
||||
fi
|
||||
}
|
||||
|
||||
prepare_workdir()
|
||||
function prepare_workdir
|
||||
{
|
||||
mkdir -p "${ROOT_DIR}/build/deps"
|
||||
mkdir -p "${WORKDIR}"
|
||||
@ -51,7 +51,7 @@ javac -classpath "${ANTLR_JAR}" "${WORKDIR}/src/"*.java -d "${WORKDIR}/target/"
|
||||
|
||||
# Run tests
|
||||
failed_count=0
|
||||
test_file()
|
||||
function test_file
|
||||
{
|
||||
local SOL_FILE
|
||||
SOL_FILE="$(${READLINK} -m "${1}")"
|
||||
|
@ -38,7 +38,8 @@ source "${REPO_ROOT}/scripts/common.sh"
|
||||
WORKDIR=$(mktemp -d)
|
||||
CMDLINE_PID=
|
||||
|
||||
cleanup() {
|
||||
function cleanup
|
||||
{
|
||||
# ensure failing commands don't cause termination during cleanup (especially within safe_kill)
|
||||
set +e
|
||||
|
||||
|
@ -38,7 +38,8 @@ ORANGE='\033[0;33m'
|
||||
CYAN='\033[0;36m'
|
||||
RESET='\033[0m'
|
||||
|
||||
function generate_bytecode_report() {
|
||||
function generate_bytecode_report
|
||||
{
|
||||
rm -rf /tmp/report.txt
|
||||
|
||||
local EXIT_STATUS
|
||||
@ -74,7 +75,8 @@ function generate_bytecode_report() {
|
||||
echo -e "${RED}FAILURE${RESET}"
|
||||
fi
|
||||
}
|
||||
function clean_git_checkout() {
|
||||
function clean_git_checkout
|
||||
{
|
||||
git submodule deinit --all -q
|
||||
git reset --hard HEAD --quiet
|
||||
git clean -f -d -x --quiet
|
||||
@ -82,7 +84,8 @@ function clean_git_checkout() {
|
||||
git submodule init -q
|
||||
git submodule update -q
|
||||
}
|
||||
function process_tag() {
|
||||
function process_tag
|
||||
{
|
||||
local TAG=$1
|
||||
cd /src
|
||||
# Checkout the historic commit instead of the tag directly.
|
||||
|
@ -83,7 +83,8 @@ for arg in "$@"; do
|
||||
esac
|
||||
done
|
||||
|
||||
show_output_if() {
|
||||
function show_output_if
|
||||
{
|
||||
local VAR=${1}
|
||||
if [ -n "${VAR}" ]; then
|
||||
echo "${SOL_FILE}"
|
||||
@ -102,7 +103,8 @@ if [ ! -f "${SOLC}" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
test_file() {
|
||||
function test_file
|
||||
{
|
||||
local SOL_FILE
|
||||
local OUTPUT
|
||||
SOL_FILE=${1}
|
||||
|
@ -23,6 +23,7 @@
|
||||
*/
|
||||
#include <solc/CommandLineInterface.h>
|
||||
|
||||
#include "license.h"
|
||||
#include "solidity/BuildInfo.h"
|
||||
|
||||
#include <libsolidity/interface/Version.h>
|
||||
@ -405,6 +406,13 @@ bool CommandLineInterface::readInputFiles()
|
||||
{
|
||||
solAssert(!m_standardJsonInput.has_value(), "");
|
||||
|
||||
if (
|
||||
m_options.input.mode == InputMode::Help ||
|
||||
m_options.input.mode == InputMode::License ||
|
||||
m_options.input.mode == InputMode::Version
|
||||
)
|
||||
return true;
|
||||
|
||||
m_fileReader.setBasePath(m_options.input.basePath);
|
||||
|
||||
if (m_fileReader.basePath() != "")
|
||||
@ -573,8 +581,18 @@ void CommandLineInterface::createJson(string const& _fileName, string const& _js
|
||||
|
||||
bool CommandLineInterface::parseArguments(int _argc, char const* const* _argv)
|
||||
{
|
||||
CommandLineParser parser(sout(/* _markAsUsed */ false), serr(/* _markAsUsed */ false));
|
||||
bool success = parser.parse(_argc, _argv, isatty(fileno(stdin)));
|
||||
CommandLineParser parser(serr(/* _markAsUsed */ false));
|
||||
|
||||
if (isatty(fileno(stdin)) && _argc == 1)
|
||||
{
|
||||
// If the terminal is taking input from the user, provide more user-friendly output.
|
||||
CommandLineParser::printHelp(sout());
|
||||
|
||||
// In this case we want to exit with an error but not display any error message.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = parser.parse(_argc, _argv);
|
||||
if (!success)
|
||||
return false;
|
||||
m_hasOutput = m_hasOutput || parser.hasOutput();
|
||||
@ -587,6 +605,15 @@ bool CommandLineInterface::processInput()
|
||||
{
|
||||
switch (m_options.input.mode)
|
||||
{
|
||||
case InputMode::Help:
|
||||
CommandLineParser::printHelp(sout());
|
||||
break;
|
||||
case InputMode::License:
|
||||
printLicense();
|
||||
break;
|
||||
case InputMode::Version:
|
||||
printVersion();
|
||||
break;
|
||||
case InputMode::StandardJson:
|
||||
{
|
||||
solAssert(m_standardJsonInput.has_value(), "");
|
||||
@ -594,21 +621,38 @@ bool CommandLineInterface::processInput()
|
||||
StandardCompiler compiler(m_fileReader.reader(), m_options.formatting.json);
|
||||
sout() << compiler.compile(move(m_standardJsonInput.value())) << endl;
|
||||
m_standardJsonInput.reset();
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case InputMode::Assembler:
|
||||
{
|
||||
return assemble(m_options.assembly.inputLanguage, m_options.assembly.targetMachine);
|
||||
}
|
||||
if (!assemble(m_options.assembly.inputLanguage, m_options.assembly.targetMachine))
|
||||
return false;
|
||||
break;
|
||||
case InputMode::Linker:
|
||||
return link();
|
||||
if (!link())
|
||||
return false;
|
||||
writeLinkedFiles();
|
||||
break;
|
||||
case InputMode::Compiler:
|
||||
case InputMode::CompilerWithASTImport:
|
||||
return compile();
|
||||
if (!compile())
|
||||
return false;
|
||||
outputCompilationResults();
|
||||
}
|
||||
|
||||
solAssert(false, "");
|
||||
return false;
|
||||
return !m_outputFailed;
|
||||
}
|
||||
|
||||
void CommandLineInterface::printVersion()
|
||||
{
|
||||
sout() << "solc, the solidity compiler commandline interface" << endl;
|
||||
sout() << "Version: " << solidity::frontend::VersionString << endl;
|
||||
}
|
||||
|
||||
void CommandLineInterface::printLicense()
|
||||
{
|
||||
sout() << otherLicenses << endl;
|
||||
// This is a static variable generated by cmake from LICENSE.txt
|
||||
sout() << licenseText << endl;
|
||||
}
|
||||
|
||||
bool CommandLineInterface::compile()
|
||||
@ -843,21 +887,6 @@ void CommandLineInterface::handleAst()
|
||||
}
|
||||
}
|
||||
|
||||
bool CommandLineInterface::actOnInput()
|
||||
{
|
||||
if (m_options.input.mode == InputMode::StandardJson || m_options.input.mode == InputMode::Assembler)
|
||||
// Already done in "processInput" phase.
|
||||
return true;
|
||||
else if (m_options.input.mode == InputMode::Linker)
|
||||
writeLinkedFiles();
|
||||
else
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
outputCompilationResults();
|
||||
}
|
||||
return !m_outputFailed;
|
||||
}
|
||||
|
||||
bool CommandLineInterface::link()
|
||||
{
|
||||
solAssert(m_options.input.mode == InputMode::Linker, "");
|
||||
@ -1013,34 +1042,52 @@ bool CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul:
|
||||
|
||||
yul::AssemblyStack& stack = assemblyStacks[src.first];
|
||||
|
||||
sout() << endl << "Pretty printed source:" << endl;
|
||||
sout() << stack.print() << endl;
|
||||
if (m_options.compiler.outputs.irOptimized)
|
||||
{
|
||||
// NOTE: This actually outputs unoptimized code when the optimizer is disabled but
|
||||
// 'ir' output in StandardCompiler works the same way.
|
||||
sout() << endl << "Pretty printed source:" << endl;
|
||||
sout() << stack.print() << endl;
|
||||
}
|
||||
|
||||
if (_language != yul::AssemblyStack::Language::Ewasm && _targetMachine == yul::AssemblyStack::Machine::Ewasm)
|
||||
{
|
||||
stack.translate(yul::AssemblyStack::Language::Ewasm);
|
||||
stack.optimize();
|
||||
|
||||
sout() << endl << "==========================" << endl;
|
||||
sout() << endl << "Translated source:" << endl;
|
||||
sout() << stack.print() << endl;
|
||||
if (m_options.compiler.outputs.ewasmIR)
|
||||
{
|
||||
sout() << endl << "==========================" << endl;
|
||||
sout() << endl << "Translated source:" << endl;
|
||||
sout() << stack.print() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
yul::MachineAssemblyObject object;
|
||||
object = stack.assemble(_targetMachine);
|
||||
object.bytecode->link(m_options.linker.libraries);
|
||||
|
||||
sout() << endl << "Binary representation:" << endl;
|
||||
if (object.bytecode)
|
||||
sout() << object.bytecode->toHex() << endl;
|
||||
else
|
||||
serr() << "No binary representation found." << endl;
|
||||
if (m_options.compiler.outputs.binary)
|
||||
{
|
||||
sout() << endl << "Binary representation:" << endl;
|
||||
if (object.bytecode)
|
||||
sout() << object.bytecode->toHex() << endl;
|
||||
else
|
||||
serr() << "No binary representation found." << endl;
|
||||
}
|
||||
|
||||
sout() << endl << "Text representation:" << endl;
|
||||
if (!object.assembly.empty())
|
||||
sout() << object.assembly << endl;
|
||||
else
|
||||
serr() << "No text representation found." << endl;
|
||||
solAssert(_targetMachine == yul::AssemblyStack::Machine::Ewasm || _targetMachine == yul::AssemblyStack::Machine::EVM, "");
|
||||
if (
|
||||
(_targetMachine == yul::AssemblyStack::Machine::EVM && m_options.compiler.outputs.asm_) ||
|
||||
(_targetMachine == yul::AssemblyStack::Machine::Ewasm && m_options.compiler.outputs.ewasm)
|
||||
)
|
||||
{
|
||||
sout() << endl << "Text representation:" << endl;
|
||||
if (!object.assembly.empty())
|
||||
sout() << object.assembly << endl;
|
||||
else
|
||||
serr() << "No text representation found." << endl;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -55,17 +55,16 @@ public:
|
||||
bool parseArguments(int _argc, char const* const* _argv);
|
||||
/// Read the content of all input files and initialize the file reader.
|
||||
bool readInputFiles();
|
||||
/// Parse the files and create source code objects
|
||||
/// Parse the files, create source code objects, print the output.
|
||||
bool processInput();
|
||||
/// Perform actions on the input depending on provided compiler arguments
|
||||
/// @returns true on success.
|
||||
bool actOnInput();
|
||||
|
||||
CommandLineOptions const& options() const { return m_options; }
|
||||
FileReader const& fileReader() const { return m_fileReader; }
|
||||
std::optional<std::string> const& standardJsonInput() const { return m_standardJsonInput; }
|
||||
|
||||
private:
|
||||
void printVersion();
|
||||
void printLicense();
|
||||
bool compile();
|
||||
bool link();
|
||||
void writeLinkedFiles();
|
||||
|
@ -16,8 +16,6 @@
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
#include "license.h"
|
||||
|
||||
#include <solc/CommandLineParser.h>
|
||||
#include <libyul/optimiser/Suite.h>
|
||||
#include <liblangutil/EVMVersion.h>
|
||||
@ -36,19 +34,12 @@ namespace po = boost::program_options;
|
||||
namespace solidity::frontend
|
||||
{
|
||||
|
||||
ostream& CommandLineParser::sout()
|
||||
{
|
||||
m_hasOutput = true;
|
||||
return m_sout;
|
||||
}
|
||||
|
||||
ostream& CommandLineParser::serr()
|
||||
{
|
||||
m_hasOutput = true;
|
||||
return m_serr;
|
||||
}
|
||||
|
||||
#define cout
|
||||
#define cerr
|
||||
|
||||
static string const g_strAllowPaths = "allow-paths";
|
||||
@ -147,26 +138,6 @@ static map<InputMode, string> const g_inputModeName = {
|
||||
{InputMode::Linker, "linker"},
|
||||
};
|
||||
|
||||
void CommandLineParser::printVersionAndExit()
|
||||
{
|
||||
sout() <<
|
||||
"solc, the solidity compiler commandline interface" <<
|
||||
endl <<
|
||||
"Version: " <<
|
||||
solidity::frontend::VersionString <<
|
||||
endl;
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
void CommandLineParser::printLicenseAndExit()
|
||||
{
|
||||
sout() << otherLicenses << endl;
|
||||
// This is a static variable generated by cmake from LICENSE.txt
|
||||
sout() << licenseText << endl;
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
bool CommandLineParser::checkMutuallyExclusive(vector<string> const& _optionNames)
|
||||
{
|
||||
if (countEnabledOptions(_optionNames) > 1)
|
||||
@ -297,11 +268,11 @@ OptimiserSettings CommandLineOptions::optimiserSettings() const
|
||||
return settings;
|
||||
}
|
||||
|
||||
bool CommandLineParser::parse(int _argc, char const* const* _argv, bool _interactiveTerminal)
|
||||
bool CommandLineParser::parse(int _argc, char const* const* _argv)
|
||||
{
|
||||
m_hasOutput = false;
|
||||
|
||||
if (!parseArgs(_argc, _argv, _interactiveTerminal))
|
||||
if (!parseArgs(_argc, _argv))
|
||||
return false;
|
||||
|
||||
return processArgs();
|
||||
@ -463,17 +434,30 @@ bool CommandLineParser::parseOutputSelection()
|
||||
{
|
||||
static auto outputSupported = [](InputMode _mode, string_view _outputName)
|
||||
{
|
||||
static set<string> const compilerModeOutputs =
|
||||
static set<string> const compilerModeOutputs = (
|
||||
CompilerOutputs::componentMap() |
|
||||
ranges::views::keys |
|
||||
ranges::to<set>();
|
||||
ranges::to<set>()
|
||||
) - set<string>{CompilerOutputs::componentName(&CompilerOutputs::ewasmIR)};
|
||||
static set<string> const assemblerModeOutputs = {
|
||||
CompilerOutputs::componentName(&CompilerOutputs::asm_),
|
||||
CompilerOutputs::componentName(&CompilerOutputs::binary),
|
||||
CompilerOutputs::componentName(&CompilerOutputs::irOptimized),
|
||||
CompilerOutputs::componentName(&CompilerOutputs::ewasm),
|
||||
CompilerOutputs::componentName(&CompilerOutputs::ewasmIR),
|
||||
};
|
||||
|
||||
switch (_mode)
|
||||
{
|
||||
case InputMode::Help:
|
||||
case InputMode::License:
|
||||
case InputMode::Version:
|
||||
solAssert(false);
|
||||
case InputMode::Compiler:
|
||||
case InputMode::CompilerWithASTImport:
|
||||
return contains(compilerModeOutputs, _outputName);
|
||||
case InputMode::Assembler:
|
||||
return contains(assemblerModeOutputs, _outputName);
|
||||
case InputMode::StandardJson:
|
||||
case InputMode::Linker:
|
||||
return false;
|
||||
@ -485,6 +469,17 @@ bool CommandLineParser::parseOutputSelection()
|
||||
for (auto&& [optionName, outputComponent]: CompilerOutputs::componentMap())
|
||||
m_options.compiler.outputs.*outputComponent = (m_args.count(optionName) > 0);
|
||||
|
||||
if (m_options.input.mode == InputMode::Assembler && m_options.compiler.outputs == CompilerOutputs{})
|
||||
{
|
||||
// In assembly mode keep the default outputs enabled for backwards-compatibility.
|
||||
// TODO: Remove this (must be done in a breaking release).
|
||||
m_options.compiler.outputs.asm_ = true;
|
||||
m_options.compiler.outputs.binary = true;
|
||||
m_options.compiler.outputs.irOptimized = true;
|
||||
m_options.compiler.outputs.ewasm = true;
|
||||
m_options.compiler.outputs.ewasmIR = true;
|
||||
}
|
||||
|
||||
vector<string> unsupportedOutputs;
|
||||
for (auto&& [optionName, outputComponent]: CompilerOutputs::componentMap())
|
||||
if (m_options.compiler.outputs.*outputComponent && !outputSupported(m_options.input.mode, optionName))
|
||||
@ -705,6 +700,7 @@ General Information)").c_str(),
|
||||
(CompilerOutputs::componentName(&CompilerOutputs::ir).c_str(), "Intermediate Representation (IR) of all contracts (EXPERIMENTAL).")
|
||||
(CompilerOutputs::componentName(&CompilerOutputs::irOptimized).c_str(), "Optimized intermediate Representation (IR) of all contracts (EXPERIMENTAL).")
|
||||
(CompilerOutputs::componentName(&CompilerOutputs::ewasm).c_str(), "Ewasm text representation of all contracts (EXPERIMENTAL).")
|
||||
(CompilerOutputs::componentName(&CompilerOutputs::ewasmIR).c_str(), "Intermediate representation (IR) converted to a form that can be translated directly into Ewasm text representation (EXPERIMENTAL).")
|
||||
(CompilerOutputs::componentName(&CompilerOutputs::signatureHashes).c_str(), "Function signature hashes of the contracts.")
|
||||
(CompilerOutputs::componentName(&CompilerOutputs::natspecUser).c_str(), "Natspec user documentation of all contracts.")
|
||||
(CompilerOutputs::componentName(&CompilerOutputs::natspecDev).c_str(), "Natspec developer documentation of all contracts.")
|
||||
@ -835,7 +831,7 @@ po::positional_options_description CommandLineParser::positionalOptionsDescripti
|
||||
return filesPositions;
|
||||
}
|
||||
|
||||
bool CommandLineParser::parseArgs(int _argc, char const* const* _argv, bool _interactiveTerminal)
|
||||
bool CommandLineParser::parseArgs(int _argc, char const* const* _argv)
|
||||
{
|
||||
po::options_description allOptions = optionsDescription();
|
||||
po::positional_options_description filesPositions = positionalOptionsDescription();
|
||||
@ -854,18 +850,6 @@ bool CommandLineParser::parseArgs(int _argc, char const* const* _argv, bool _int
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_args.count(g_strHelp) || (_interactiveTerminal && _argc == 1))
|
||||
{
|
||||
sout() << allOptions;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_args.count(g_strVersion))
|
||||
printVersionAndExit();
|
||||
|
||||
if (m_args.count(g_strLicense))
|
||||
printLicenseAndExit();
|
||||
|
||||
po::notify(m_args);
|
||||
|
||||
return true;
|
||||
@ -874,6 +858,9 @@ bool CommandLineParser::parseArgs(int _argc, char const* const* _argv, bool _int
|
||||
bool CommandLineParser::processArgs()
|
||||
{
|
||||
if (!checkMutuallyExclusive({
|
||||
g_strHelp,
|
||||
g_strLicense,
|
||||
g_strVersion,
|
||||
g_strStandardJSON,
|
||||
g_strLink,
|
||||
g_strAssemble,
|
||||
@ -883,7 +870,13 @@ bool CommandLineParser::processArgs()
|
||||
}))
|
||||
return false;
|
||||
|
||||
if (m_args.count(g_strStandardJSON) > 0)
|
||||
if (m_args.count(g_strHelp) > 0)
|
||||
m_options.input.mode = InputMode::Help;
|
||||
else if (m_args.count(g_strLicense) > 0)
|
||||
m_options.input.mode = InputMode::License;
|
||||
else if (m_args.count(g_strVersion) > 0)
|
||||
m_options.input.mode = InputMode::Version;
|
||||
else if (m_args.count(g_strStandardJSON) > 0)
|
||||
m_options.input.mode = InputMode::StandardJson;
|
||||
else if (m_args.count(g_strAssemble) > 0 || m_args.count(g_strStrictAssembly) > 0 || m_args.count(g_strYul) > 0)
|
||||
m_options.input.mode = InputMode::Assembler;
|
||||
@ -894,6 +887,13 @@ bool CommandLineParser::processArgs()
|
||||
else
|
||||
m_options.input.mode = InputMode::Compiler;
|
||||
|
||||
if (
|
||||
m_options.input.mode == InputMode::Help ||
|
||||
m_options.input.mode == InputMode::License ||
|
||||
m_options.input.mode == InputMode::Version
|
||||
)
|
||||
return true;
|
||||
|
||||
map<string, set<InputMode>> validOptionInputModeCombinations = {
|
||||
// TODO: This should eventually contain all options.
|
||||
{g_strErrorRecovery, {InputMode::Compiler, InputMode::CompilerWithASTImport}},
|
||||
@ -915,11 +915,12 @@ bool CommandLineParser::processArgs()
|
||||
if (!checkMutuallyExclusive({g_strColor, g_strNoColor}))
|
||||
return false;
|
||||
|
||||
array<string, 8> const conflictingWithStopAfter{
|
||||
array<string, 9> const conflictingWithStopAfter{
|
||||
CompilerOutputs::componentName(&CompilerOutputs::binary),
|
||||
CompilerOutputs::componentName(&CompilerOutputs::ir),
|
||||
CompilerOutputs::componentName(&CompilerOutputs::irOptimized),
|
||||
CompilerOutputs::componentName(&CompilerOutputs::ewasm),
|
||||
CompilerOutputs::componentName(&CompilerOutputs::ewasmIR),
|
||||
g_strGas,
|
||||
CompilerOutputs::componentName(&CompilerOutputs::asm_),
|
||||
CompilerOutputs::componentName(&CompilerOutputs::asmJson),
|
||||
|
@ -48,6 +48,9 @@ namespace solidity::frontend
|
||||
|
||||
enum class InputMode
|
||||
{
|
||||
Help,
|
||||
License,
|
||||
Version,
|
||||
Compiler,
|
||||
CompilerWithASTImport,
|
||||
StandardJson,
|
||||
@ -75,6 +78,7 @@ struct CompilerOutputs
|
||||
{"ir", &CompilerOutputs::ir},
|
||||
{"ir-optimized", &CompilerOutputs::irOptimized},
|
||||
{"ewasm", &CompilerOutputs::ewasm},
|
||||
{"ewasm-ir", &CompilerOutputs::ewasmIR},
|
||||
{"hashes", &CompilerOutputs::signatureHashes},
|
||||
{"userdoc", &CompilerOutputs::natspecUser},
|
||||
{"devdoc", &CompilerOutputs::natspecDev},
|
||||
@ -94,6 +98,7 @@ struct CompilerOutputs
|
||||
bool ir = false;
|
||||
bool irOptimized = false;
|
||||
bool ewasm = false;
|
||||
bool ewasmIR = false;
|
||||
bool signatureHashes = false;
|
||||
bool natspecUser = false;
|
||||
bool natspecDev = false;
|
||||
@ -230,34 +235,28 @@ struct CommandLineOptions
|
||||
|
||||
/// Parses the command-line arguments and produces a filled-out CommandLineOptions structure.
|
||||
/// Validates provided values and prints error messages in case of errors.
|
||||
///
|
||||
/// The class is also responsible for handling options that only result in printing informational
|
||||
/// text, without the need to invoke the compiler - printing usage banner, version or license.
|
||||
class CommandLineParser
|
||||
{
|
||||
public:
|
||||
explicit CommandLineParser(std::ostream& _sout, std::ostream& _serr):
|
||||
m_sout(_sout),
|
||||
explicit CommandLineParser(std::ostream& _serr):
|
||||
m_serr(_serr)
|
||||
{}
|
||||
|
||||
/// Parses the command-line arguments and fills out the internal CommandLineOptions structure.
|
||||
/// Performs validation and prints error messages. If requested, prints usage banner, version
|
||||
/// or license.
|
||||
/// @param interactiveTerminal specifies whether the terminal is taking input from the user.
|
||||
/// This is used to determine whether to provide more user-friendly output in some situations.
|
||||
/// E.g. whether to print help text when no arguments are provided.
|
||||
/// Performs validation and prints error messages.
|
||||
/// @return true if there were no validation errors when parsing options and the
|
||||
/// CommandLineOptions structure has been fully initialized. false if there were errors - in
|
||||
/// this case CommandLineOptions may be only partially filled out. May also return false if
|
||||
/// there is not further processing necessary and the program should just exit.
|
||||
bool parse(int _argc, char const* const* _argv, bool _interactiveTerminal);
|
||||
bool parse(int _argc, char const* const* _argv);
|
||||
|
||||
CommandLineOptions const& options() const { return m_options; }
|
||||
|
||||
/// Returns true if the parser has written anything to any of its output streams.
|
||||
bool hasOutput() const { return m_hasOutput; }
|
||||
|
||||
static void printHelp(std::ostream& _out) { _out << optionsDescription(); }
|
||||
|
||||
private:
|
||||
/// @returns a specification of all named command-line options accepted by the compiler.
|
||||
/// The object can be used to parse command-line arguments or to generate the help screen.
|
||||
@ -270,7 +269,7 @@ private:
|
||||
/// Uses boost::program_options to parse the command-line arguments and leaves the result in @a m_args.
|
||||
/// Also handles the arguments that result in information being printed followed by immediate exit.
|
||||
/// @returns false if parsing fails due to syntactical errors or the arguments not matching the description.
|
||||
bool parseArgs(int _argc, char const* const* _argv, bool _interactiveTerminal);
|
||||
bool parseArgs(int _argc, char const* const* _argv);
|
||||
|
||||
/// Validates parsed arguments stored in @a m_args and fills out the internal CommandLineOptions
|
||||
/// structure.
|
||||
@ -294,20 +293,13 @@ private:
|
||||
bool parseOutputSelection();
|
||||
|
||||
bool checkMutuallyExclusive(std::vector<std::string> const& _optionNames);
|
||||
[[noreturn]] void printVersionAndExit();
|
||||
[[noreturn]] void printLicenseAndExit();
|
||||
size_t countEnabledOptions(std::vector<std::string> const& _optionNames) const;
|
||||
static std::string joinOptionNames(std::vector<std::string> const& _optionNames, std::string _separator = ", ");
|
||||
|
||||
/// Returns the stream that should receive normal output. Sets m_hasOutput to true if the
|
||||
/// stream has ever been used.
|
||||
std::ostream& sout();
|
||||
|
||||
/// Returns the stream that should receive error output. Sets m_hasOutput to true if the
|
||||
/// stream has ever been used.
|
||||
std::ostream& serr();
|
||||
|
||||
std::ostream& m_sout;
|
||||
std::ostream& m_serr;
|
||||
bool m_hasOutput = false;
|
||||
|
||||
|
@ -65,8 +65,7 @@ int main(int argc, char** argv)
|
||||
bool success =
|
||||
cli.parseArguments(argc, argv) &&
|
||||
cli.readInputFiles() &&
|
||||
cli.processInput() &&
|
||||
cli.actOnInput();
|
||||
cli.processInput();
|
||||
|
||||
return success ? 0 : 1;
|
||||
}
|
||||
|
@ -154,7 +154,7 @@ function ask_expectation_update
|
||||
|
||||
# General helper function for testing SOLC behaviour, based on file name, compile opts, exit code, stdout and stderr.
|
||||
# An failure is expected.
|
||||
function test_solc_behaviour()
|
||||
function test_solc_behaviour
|
||||
{
|
||||
local filename="${1}"
|
||||
local solc_args
|
||||
@ -288,7 +288,7 @@ EOF
|
||||
}
|
||||
|
||||
|
||||
function test_solc_assembly_output()
|
||||
function test_solc_assembly_output
|
||||
{
|
||||
local input="${1}"
|
||||
local expected="${2}"
|
||||
|
@ -66,7 +66,7 @@ IR:
|
||||
object "C_6" {
|
||||
code {
|
||||
/// @src 0:60:101 "contract C {..."
|
||||
mstore(64, 128)
|
||||
mstore(64, memoryguard(128))
|
||||
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
|
||||
|
||||
constructor_C_6()
|
||||
@ -97,7 +97,7 @@ object "C_6" {
|
||||
object "C_6_deployed" {
|
||||
code {
|
||||
/// @src 0:60:101 "contract C {..."
|
||||
mstore(64, 128)
|
||||
mstore(64, memoryguard(128))
|
||||
|
||||
if iszero(lt(calldatasize(), 4))
|
||||
{
|
||||
@ -182,11 +182,12 @@ object "C_6" {
|
||||
code {
|
||||
{
|
||||
/// @src 0:60:101 "contract C {..."
|
||||
mstore(64, 128)
|
||||
let _1 := memoryguard(0x80)
|
||||
mstore(64, _1)
|
||||
if callvalue() { revert(0, 0) }
|
||||
let _1 := datasize("C_6_deployed")
|
||||
codecopy(128, dataoffset("C_6_deployed"), _1)
|
||||
return(128, _1)
|
||||
let _2 := datasize("C_6_deployed")
|
||||
codecopy(_1, dataoffset("C_6_deployed"), _2)
|
||||
return(_1, _2)
|
||||
}
|
||||
}
|
||||
/// @use-src 0:"debug_info_in_yul_and_evm_asm_print_all/input.sol"
|
||||
@ -194,15 +195,16 @@ object "C_6" {
|
||||
code {
|
||||
{
|
||||
/// @src 0:60:101 "contract C {..."
|
||||
mstore(64, 128)
|
||||
let _1 := memoryguard(0x80)
|
||||
mstore(64, _1)
|
||||
if iszero(lt(calldatasize(), 4))
|
||||
{
|
||||
let _1 := 0
|
||||
if eq(0x26121ff0, shr(224, calldataload(_1)))
|
||||
let _2 := 0
|
||||
if eq(0x26121ff0, shr(224, calldataload(_2)))
|
||||
{
|
||||
if callvalue() { revert(_1, _1) }
|
||||
if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) }
|
||||
return(128, _1)
|
||||
if callvalue() { revert(_2, _2) }
|
||||
if slt(add(calldatasize(), not(3)), _2) { revert(_2, _2) }
|
||||
return(_1, _2)
|
||||
}
|
||||
}
|
||||
revert(0, 0)
|
||||
|
@ -66,7 +66,7 @@ IR:
|
||||
object "C_6" {
|
||||
code {
|
||||
/// @src 0:60:101
|
||||
mstore(64, 128)
|
||||
mstore(64, memoryguard(128))
|
||||
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
|
||||
|
||||
constructor_C_6()
|
||||
@ -97,7 +97,7 @@ object "C_6" {
|
||||
object "C_6_deployed" {
|
||||
code {
|
||||
/// @src 0:60:101
|
||||
mstore(64, 128)
|
||||
mstore(64, memoryguard(128))
|
||||
|
||||
if iszero(lt(calldatasize(), 4))
|
||||
{
|
||||
@ -181,11 +181,12 @@ object "C_6" {
|
||||
code {
|
||||
{
|
||||
/// @src 0:60:101
|
||||
mstore(64, 128)
|
||||
let _1 := memoryguard(0x80)
|
||||
mstore(64, _1)
|
||||
if callvalue() { revert(0, 0) }
|
||||
let _1 := datasize("C_6_deployed")
|
||||
codecopy(128, dataoffset("C_6_deployed"), _1)
|
||||
return(128, _1)
|
||||
let _2 := datasize("C_6_deployed")
|
||||
codecopy(_1, dataoffset("C_6_deployed"), _2)
|
||||
return(_1, _2)
|
||||
}
|
||||
}
|
||||
/// @use-src 0:"debug_info_in_yul_and_evm_asm_print_location_only/input.sol"
|
||||
@ -193,15 +194,16 @@ object "C_6" {
|
||||
code {
|
||||
{
|
||||
/// @src 0:60:101
|
||||
mstore(64, 128)
|
||||
let _1 := memoryguard(0x80)
|
||||
mstore(64, _1)
|
||||
if iszero(lt(calldatasize(), 4))
|
||||
{
|
||||
let _1 := 0
|
||||
if eq(0x26121ff0, shr(224, calldataload(_1)))
|
||||
let _2 := 0
|
||||
if eq(0x26121ff0, shr(224, calldataload(_2)))
|
||||
{
|
||||
if callvalue() { revert(_1, _1) }
|
||||
if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) }
|
||||
return(128, _1)
|
||||
if callvalue() { revert(_2, _2) }
|
||||
if slt(add(calldatasize(), not(3)), _2) { revert(_2, _2) }
|
||||
return(_1, _2)
|
||||
}
|
||||
}
|
||||
revert(0, 0)
|
||||
|
@ -63,7 +63,7 @@ IR:
|
||||
object "C_6" {
|
||||
code {
|
||||
|
||||
mstore(64, 128)
|
||||
mstore(64, memoryguard(128))
|
||||
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
|
||||
|
||||
constructor_C_6()
|
||||
@ -90,7 +90,7 @@ object "C_6" {
|
||||
object "C_6_deployed" {
|
||||
code {
|
||||
|
||||
mstore(64, 128)
|
||||
mstore(64, memoryguard(128))
|
||||
|
||||
if iszero(lt(calldatasize(), 4))
|
||||
{
|
||||
@ -171,26 +171,28 @@ Optimized IR:
|
||||
object "C_6" {
|
||||
code {
|
||||
{
|
||||
mstore(64, 128)
|
||||
let _1 := memoryguard(0x80)
|
||||
mstore(64, _1)
|
||||
if callvalue() { revert(0, 0) }
|
||||
let _1 := datasize("C_6_deployed")
|
||||
codecopy(128, dataoffset("C_6_deployed"), _1)
|
||||
return(128, _1)
|
||||
let _2 := datasize("C_6_deployed")
|
||||
codecopy(_1, dataoffset("C_6_deployed"), _2)
|
||||
return(_1, _2)
|
||||
}
|
||||
}
|
||||
/// @use-src 0:"debug_info_in_yul_and_evm_asm_print_none/input.sol"
|
||||
object "C_6_deployed" {
|
||||
code {
|
||||
{
|
||||
mstore(64, 128)
|
||||
let _1 := memoryguard(0x80)
|
||||
mstore(64, _1)
|
||||
if iszero(lt(calldatasize(), 4))
|
||||
{
|
||||
let _1 := 0
|
||||
if eq(0x26121ff0, shr(224, calldataload(_1)))
|
||||
let _2 := 0
|
||||
if eq(0x26121ff0, shr(224, calldataload(_2)))
|
||||
{
|
||||
if callvalue() { revert(_1, _1) }
|
||||
if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) }
|
||||
return(128, _1)
|
||||
if callvalue() { revert(_2, _2) }
|
||||
if slt(add(calldatasize(), not(3)), _2) { revert(_2, _2) }
|
||||
return(_1, _2)
|
||||
}
|
||||
}
|
||||
revert(0, 0)
|
||||
|
@ -11,7 +11,7 @@ IR:
|
||||
object "C_2" {
|
||||
code {
|
||||
/// @src 0:265:278 "contract C {}"
|
||||
mstore(64, 128)
|
||||
mstore(64, memoryguard(128))
|
||||
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
|
||||
|
||||
constructor_C_2()
|
||||
@ -42,7 +42,7 @@ object "C_2" {
|
||||
object "C_2_deployed" {
|
||||
code {
|
||||
/// @src 0:265:278 "contract C {}"
|
||||
mstore(64, 128)
|
||||
mstore(64, memoryguard(128))
|
||||
|
||||
revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74()
|
||||
|
||||
@ -82,11 +82,12 @@ object "C_2" {
|
||||
code {
|
||||
{
|
||||
/// @src 0:265:278 "contract C {}"
|
||||
mstore(64, 128)
|
||||
let _1 := memoryguard(0x80)
|
||||
mstore(64, _1)
|
||||
if callvalue() { revert(0, 0) }
|
||||
let _1 := datasize("C_2_deployed")
|
||||
codecopy(128, dataoffset("C_2_deployed"), _1)
|
||||
return(128, _1)
|
||||
let _2 := datasize("C_2_deployed")
|
||||
codecopy(_1, dataoffset("C_2_deployed"), _2)
|
||||
return(_1, _2)
|
||||
}
|
||||
}
|
||||
/// @use-src 0:"debug_info_in_yul_snippet_escaping/input.sol"
|
||||
@ -94,7 +95,7 @@ object "C_2" {
|
||||
code {
|
||||
{
|
||||
/// @src 0:265:278 "contract C {}"
|
||||
mstore(64, 128)
|
||||
mstore(64, memoryguard(0x80))
|
||||
revert(0, 0)
|
||||
}
|
||||
}
|
||||
@ -115,7 +116,7 @@ IR:
|
||||
object "D_27" {
|
||||
code {
|
||||
/// @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..."
|
||||
mstore(64, 128)
|
||||
mstore(64, memoryguard(128))
|
||||
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
|
||||
|
||||
constructor_D_27()
|
||||
@ -146,7 +147,7 @@ object "D_27" {
|
||||
object "D_27_deployed" {
|
||||
code {
|
||||
/// @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..."
|
||||
mstore(64, 128)
|
||||
mstore(64, memoryguard(128))
|
||||
|
||||
if iszero(lt(calldatasize(), 4))
|
||||
{
|
||||
@ -372,7 +373,7 @@ object "D_27" {
|
||||
object "C_2" {
|
||||
code {
|
||||
/// @src 0:265:278 "contract C {}"
|
||||
mstore(64, 128)
|
||||
mstore(64, memoryguard(128))
|
||||
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
|
||||
|
||||
constructor_C_2()
|
||||
@ -403,7 +404,7 @@ object "D_27" {
|
||||
object "C_2_deployed" {
|
||||
code {
|
||||
/// @src 0:265:278 "contract C {}"
|
||||
mstore(64, 128)
|
||||
mstore(64, memoryguard(128))
|
||||
|
||||
revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74()
|
||||
|
||||
@ -448,11 +449,12 @@ object "D_27" {
|
||||
code {
|
||||
{
|
||||
/// @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..."
|
||||
mstore(64, 128)
|
||||
let _1 := memoryguard(0x80)
|
||||
mstore(64, _1)
|
||||
if callvalue() { revert(0, 0) }
|
||||
let _1 := datasize("D_27_deployed")
|
||||
codecopy(128, dataoffset("D_27_deployed"), _1)
|
||||
return(128, _1)
|
||||
let _2 := datasize("D_27_deployed")
|
||||
codecopy(_1, dataoffset("D_27_deployed"), _2)
|
||||
return(_1, _2)
|
||||
}
|
||||
}
|
||||
/// @use-src 0:"debug_info_in_yul_snippet_escaping/input.sol"
|
||||
@ -460,26 +462,25 @@ object "D_27" {
|
||||
code {
|
||||
{
|
||||
/// @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..."
|
||||
mstore(64, 128)
|
||||
let _1 := memoryguard(0x80)
|
||||
mstore(64, _1)
|
||||
if iszero(lt(calldatasize(), 4))
|
||||
{
|
||||
let _1 := 0
|
||||
if eq(0x26121ff0, shr(224, calldataload(_1)))
|
||||
let _2 := 0
|
||||
if eq(0x26121ff0, shr(224, calldataload(_2)))
|
||||
{
|
||||
if callvalue() { revert(_1, _1) }
|
||||
if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) }
|
||||
if callvalue() { revert(_2, _2) }
|
||||
if slt(add(calldatasize(), not(3)), _2) { revert(_2, _2) }
|
||||
/// @src 0:446:491 "new /// @src 0:149:156 \"new C()\"..."
|
||||
let _2 := datasize("C_2")
|
||||
let _3 := add(/** @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." */ 128, /** @src 0:446:491 "new /// @src 0:149:156 \"new C()\"..." */ _2)
|
||||
if or(gt(_3, 0xffffffffffffffff), lt(_3, /** @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." */ 128))
|
||||
/// @src 0:446:491 "new /// @src 0:149:156 \"new C()\"..."
|
||||
{ panic_error_0x41() }
|
||||
datacopy(/** @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." */ 128, /** @src 0:446:491 "new /// @src 0:149:156 \"new C()\"..." */ dataoffset("C_2"), _2)
|
||||
if iszero(create(/** @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." */ _1, 128, /** @src 0:446:491 "new /// @src 0:149:156 \"new C()\"..." */ _2))
|
||||
let _3 := datasize("C_2")
|
||||
let _4 := add(_1, _3)
|
||||
if or(gt(_4, 0xffffffffffffffff), lt(_4, _1)) { panic_error_0x41() }
|
||||
datacopy(_1, dataoffset("C_2"), _3)
|
||||
if iszero(create(/** @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." */ _2, /** @src 0:446:491 "new /// @src 0:149:156 \"new C()\"..." */ _1, sub(_4, _1)))
|
||||
{
|
||||
/// @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..."
|
||||
let pos := mload(64)
|
||||
returndatacopy(pos, _1, returndatasize())
|
||||
returndatacopy(pos, _2, returndatasize())
|
||||
revert(pos, returndatasize())
|
||||
}
|
||||
mstore(add(allocate_memory_array_string(), 32), "/*")
|
||||
@ -539,11 +540,12 @@ object "D_27" {
|
||||
code {
|
||||
{
|
||||
/// @src 0:265:278 "contract C {}"
|
||||
mstore(64, 128)
|
||||
let _1 := memoryguard(0x80)
|
||||
mstore(64, _1)
|
||||
if callvalue() { revert(0, 0) }
|
||||
let _1 := datasize("C_2_deployed")
|
||||
codecopy(128, dataoffset("C_2_deployed"), _1)
|
||||
return(128, _1)
|
||||
let _2 := datasize("C_2_deployed")
|
||||
codecopy(_1, dataoffset("C_2_deployed"), _2)
|
||||
return(_1, _2)
|
||||
}
|
||||
}
|
||||
/// @use-src 0:"debug_info_in_yul_snippet_escaping/input.sol"
|
||||
@ -551,7 +553,7 @@ object "D_27" {
|
||||
code {
|
||||
{
|
||||
/// @src 0:265:278 "contract C {}"
|
||||
mstore(64, 128)
|
||||
mstore(64, memoryguard(0x80))
|
||||
revert(0, 0)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1 @@
|
||||
--assemble --optimize --yul-dialect evm --machine ewasm --asm
|
@ -0,0 +1 @@
|
||||
Warning: Yul is still experimental. Please use the output with care.
|
@ -0,0 +1,4 @@
|
||||
{
|
||||
let x := 42
|
||||
sstore(0, x)
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
|
||||
======= evm_to_wasm_output_selection_asm_only/input.yul (Ewasm) =======
|
@ -0,0 +1 @@
|
||||
--assemble --optimize --yul-dialect evm --machine ewasm --ewasm-ir
|
@ -0,0 +1 @@
|
||||
Warning: Yul is still experimental. Please use the output with care.
|
@ -0,0 +1,4 @@
|
||||
{
|
||||
let x := 42
|
||||
sstore(0, x)
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
|
||||
======= evm_to_wasm_output_selection_ewasm_ir_only/input.yul (Ewasm) =======
|
||||
|
||||
==========================
|
||||
|
||||
Translated source:
|
||||
object "object" {
|
||||
code {
|
||||
function main()
|
||||
{
|
||||
let hi := i64.shl(i64.extend_i32_u(bswap32(i32.wrap_i64(0))), 32)
|
||||
let y := i64.or(hi, i64.extend_i32_u(bswap32(i32.wrap_i64(i64.shr_u(0, 32)))))
|
||||
i64.store(0:i32, y)
|
||||
i64.store(i32.add(0:i32, 8:i32), y)
|
||||
i64.store(i32.add(0:i32, 16:i32), y)
|
||||
i64.store(i32.add(0:i32, 24:i32), y)
|
||||
i64.store(32:i32, y)
|
||||
i64.store(i32.add(32:i32, 8:i32), y)
|
||||
i64.store(i32.add(32:i32, 16:i32), y)
|
||||
let hi_1 := i64.shl(i64.extend_i32_u(bswap32(i32.wrap_i64(42))), 32)
|
||||
i64.store(i32.add(32:i32, 24:i32), i64.or(hi_1, i64.extend_i32_u(bswap32(i32.wrap_i64(i64.shr_u(42, 32))))))
|
||||
eth.storageStore(0:i32, 32:i32)
|
||||
}
|
||||
function bswap16(x:i32) -> y:i32
|
||||
{
|
||||
y := i32.or(i32.and(i32.shl(x, 8:i32), 0xff00:i32), i32.and(i32.shr_u(x, 8:i32), 0xff:i32))
|
||||
}
|
||||
function bswap32(x:i32) -> y:i32
|
||||
{
|
||||
let hi:i32 := i32.shl(bswap16(x), 16:i32)
|
||||
y := i32.or(hi, bswap16(i32.shr_u(x, 16:i32)))
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
--assemble --optimize --yul-dialect evm --machine ewasm --ewasm
|
@ -0,0 +1 @@
|
||||
Warning: Yul is still experimental. Please use the output with care.
|
@ -0,0 +1,4 @@
|
||||
{
|
||||
let x := 42
|
||||
sstore(0, x)
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
|
||||
======= evm_to_wasm_output_selection_ewasm_only/input.yul (Ewasm) =======
|
||||
|
||||
Text representation:
|
||||
(module
|
||||
(import "ethereum" "storageStore" (func $eth.storageStore (param i32 i32)))
|
||||
(memory $memory (export "memory") 1)
|
||||
(export "main" (func $main))
|
||||
|
||||
(func $main
|
||||
(local $hi i64)
|
||||
(local $y i64)
|
||||
(local $hi_1 i64)
|
||||
(block $label_
|
||||
(local.set $hi (i64.shl (i64.extend_i32_u (call $bswap32 (i32.wrap_i64 (i64.const 0)))) (i64.const 32)))
|
||||
(local.set $y (i64.or (local.get $hi) (i64.extend_i32_u (call $bswap32 (i32.wrap_i64 (i64.shr_u (i64.const 0) (i64.const 32)))))))
|
||||
(i64.store (i32.const 0) (local.get $y))
|
||||
(i64.store (i32.add (i32.const 0) (i32.const 8)) (local.get $y))
|
||||
(i64.store (i32.add (i32.const 0) (i32.const 16)) (local.get $y))
|
||||
(i64.store (i32.add (i32.const 0) (i32.const 24)) (local.get $y))
|
||||
(i64.store (i32.const 32) (local.get $y))
|
||||
(i64.store (i32.add (i32.const 32) (i32.const 8)) (local.get $y))
|
||||
(i64.store (i32.add (i32.const 32) (i32.const 16)) (local.get $y))
|
||||
(local.set $hi_1 (i64.shl (i64.extend_i32_u (call $bswap32 (i32.wrap_i64 (i64.const 42)))) (i64.const 32)))
|
||||
(i64.store (i32.add (i32.const 32) (i32.const 24)) (i64.or (local.get $hi_1) (i64.extend_i32_u (call $bswap32 (i32.wrap_i64 (i64.shr_u (i64.const 42) (i64.const 32)))))))
|
||||
(call $eth.storageStore (i32.const 0) (i32.const 32))
|
||||
)
|
||||
)
|
||||
|
||||
(func $bswap16
|
||||
(param $x i32)
|
||||
(result i32)
|
||||
(local $y i32)
|
||||
(block $label__1
|
||||
(local.set $y (i32.or (i32.and (i32.shl (local.get $x) (i32.const 8)) (i32.const 65280)) (i32.and (i32.shr_u (local.get $x) (i32.const 8)) (i32.const 255))))
|
||||
|
||||
)
|
||||
(local.get $y)
|
||||
)
|
||||
|
||||
(func $bswap32
|
||||
(param $x i32)
|
||||
(result i32)
|
||||
(local $y i32)
|
||||
(local $hi i32)
|
||||
(block $label__2
|
||||
(local.set $hi (i32.shl (call $bswap16 (local.get $x)) (i32.const 16)))
|
||||
(local.set $y (i32.or (local.get $hi) (call $bswap16 (i32.shr_u (local.get $x) (i32.const 16)))))
|
||||
|
||||
)
|
||||
(local.get $y)
|
||||
)
|
||||
|
||||
)
|
@ -11,7 +11,7 @@ IR:
|
||||
object "C_81" {
|
||||
code {
|
||||
/// @src 0:82:370 "contract C {..."
|
||||
mstore(64, 128)
|
||||
mstore(64, memoryguard(128))
|
||||
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
|
||||
|
||||
constructor_C_81()
|
||||
@ -42,7 +42,7 @@ object "C_81" {
|
||||
object "C_81_deployed" {
|
||||
code {
|
||||
/// @src 0:82:370 "contract C {..."
|
||||
mstore(64, 128)
|
||||
mstore(64, memoryguard(128))
|
||||
|
||||
if iszero(lt(calldatasize(), 4))
|
||||
{
|
||||
|
@ -13,7 +13,7 @@
|
||||
},
|
||||
"calldata_array_index_access_uint256_dyn_calldata":
|
||||
{
|
||||
"entryPoint": 168,
|
||||
"entryPoint": 152,
|
||||
"parameterSlots": 2,
|
||||
"returnSlots": 1
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user