Merge remote-tracking branch 'origin/develop' into breaking

This commit is contained in:
chriseth 2021-11-09 18:26:34 +01:00
commit 88cc42230f
409 changed files with 4885 additions and 3597 deletions

View File

@ -21,8 +21,8 @@ parameters:
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:c26a7ffc9fc243a4ec3105b9dc1edcdd964ad0e9665c83172b7ebda74bbf3021" default: "solbuildpackpusher/solidity-buildpack-deps@sha256:c26a7ffc9fc243a4ec3105b9dc1edcdd964ad0e9665c83172b7ebda74bbf3021"
emscripten-docker-image: emscripten-docker-image:
type: string type: string
# solbuildpackpusher/solidity-buildpack-deps:emscripten-6 # solbuildpackpusher/solidity-buildpack-deps:emscripten-7
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:092da5817bc032c91a806b4f73db2a1a31e5cc4c066d94d43eedd9f365df7154" default: "solbuildpackpusher/solidity-buildpack-deps@sha256:9ffcd0944433fe100e9433f2aa9ba5c21e096e758ad8a05a4a76feaed3d1f463"
evm-version: evm-version:
type: string type: string
default: london default: london
@ -31,7 +31,7 @@ orbs:
win: circleci/windows@2.2.0 win: circleci/windows@2.2.0
commands: commands:
gitter_notify: gitter_notify_unless_pr:
description: "Posts a notification to the main room on Gitter (if not running on a PR)." description: "Posts a notification to the main room on Gitter (if not running on a PR)."
parameters: parameters:
event: event:
@ -44,6 +44,8 @@ commands:
name: "Gitter notification" name: "Gitter notification"
when: << parameters.condition >> when: << parameters.condition >>
command: | 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 >>" == "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." [[ "<< 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}" \ --header "Authorization: Bearer ${GITTER_API_TOKEN}" \
--data "{\"text\":\"${message}\"}" --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)." description: "Posts a failure notification to the main room on Gitter (if not running on a PR)."
steps: steps:
- gitter_notify: - gitter_notify_unless_pr:
event: failure event: failure
condition: on_fail 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)." description: "Posts a success notification to the main room on Gitter (if not running on a PR)."
steps: steps:
- gitter_notify: - gitter_notify_unless_pr:
event: success event: success
condition: on_success condition: on_success
@ -170,7 +172,7 @@ defaults:
destination: test_results/ destination: test_results/
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------
# Tests Templates # Step Templates
# store_test_results helper # store_test_results helper
- store_test_results: &store_test_results - store_test_results: &store_test_results
@ -181,9 +183,13 @@ defaults:
- checkout - checkout
- attach_workspace: - attach_workspace:
at: build 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 - run: *run_soltest
- store_test_results: *store_test_results - store_test_results: *store_test_results
- store_artifacts: *artifacts_test_results - store_artifacts: *artifacts_test_results
- gitter_notify_failure_unless_pr
- steps_soltest_all: &steps_soltest_all - steps_soltest_all: &steps_soltest_all
steps: steps:
@ -193,6 +199,7 @@ defaults:
- run: *run_soltest_all - run: *run_soltest_all
- store_test_results: *store_test_results - store_test_results: *store_test_results
- store_artifacts: *artifacts_test_results - store_artifacts: *artifacts_test_results
- gitter_notify_failure_unless_pr
- steps_cmdline_tests: &steps_cmdline_tests - steps_cmdline_tests: &steps_cmdline_tests
steps: steps:
@ -202,31 +209,101 @@ defaults:
- run: *run_cmdline_tests - run: *run_cmdline_tests
- store_test_results: *store_test_results - store_test_results: *store_test_results
- store_artifacts: *artifacts_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: docker:
- image: << pipeline.parameters.ubuntu-1604-clang-ossfuzz-docker-image >> - 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: docker:
- image: << pipeline.parameters.ubuntu-2004-clang-docker-image >> - 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: docker:
- image: << pipeline.parameters.ubuntu-2004-docker-image >> - image: << pipeline.parameters.ubuntu-2004-docker-image >>
parallelism: 6 environment:
<<: *steps_soltest_all TERM: xterm
- test_asan: &test_asan - base_ubuntu2004_xlarge: &base_ubuntu2004_xlarge
<<: *test_ubuntu2004 <<: *base_ubuntu2004
<<: *steps_soltest resource_class: xlarge
environment:
TERM: xterm
MAKEFLAGS: -j 10
- test_ubuntu2004_clang_cli: &test_ubuntu2004_clang_cli - base_buildpack_focal: &base_buildpack_focal
docker: docker:
- image: << pipeline.parameters.ubuntu-2004-clang-docker-image >> - image: buildpack-deps:focal
<<: *steps_cmdline_tests 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 # Workflow Templates
@ -310,10 +387,7 @@ defaults:
jobs: jobs:
chk_spelling: chk_spelling:
docker: <<: *base_python
- image: circleci/python:3.6
environment:
TERM: xterm
steps: steps:
- checkout - checkout
- attach_workspace: - attach_workspace:
@ -325,12 +399,10 @@ jobs:
- run: - run:
name: Check spelling name: Check spelling
command: ~/.local/bin/codespell -S "*.enc,.git,Dockerfile*" -I ./scripts/codespell_whitelist.txt command: ~/.local/bin/codespell -S "*.enc,.git,Dockerfile*" -I ./scripts/codespell_whitelist.txt
- gitter_notify_failure_unless_pr
chk_docs_examples: chk_docs_examples:
docker: <<: *base_node_latest
- image: circleci/node
environment:
TERM: xterm
steps: steps:
- checkout - checkout
- attach_workspace: - attach_workspace:
@ -341,10 +413,10 @@ jobs:
- run: - run:
name: Test Docs examples name: Test Docs examples
command: ./test/docsCodeStyle.sh command: ./test/docsCodeStyle.sh
- gitter_notify_failure_unless_pr
chk_coding_style: chk_coding_style:
docker: <<: *base_buildpack_focal
- image: buildpack-deps:focal
steps: steps:
- checkout - checkout
- run: - run:
@ -359,19 +431,19 @@ jobs:
- run: - run:
name: Check for broken symlinks name: Check for broken symlinks
command: ./scripts/check_symlinks.sh command: ./scripts/check_symlinks.sh
- gitter_notify_failure_unless_pr
chk_errorcodes: chk_errorcodes:
docker: <<: *base_python
- image: circleci/python:3.6
steps: steps:
- checkout - checkout
- run: - run:
name: Check for error codes name: Check for error codes
command: ./scripts/error_codes.py --check command: ./scripts/error_codes.py --check
- gitter_notify_failure_unless_pr
chk_pylint: chk_pylint:
docker: <<: *base_buildpack_focal
- image: buildpack-deps:focal
steps: steps:
- checkout - checkout
- run: - run:
@ -384,10 +456,10 @@ jobs:
- run: - run:
name: Linting Python Scripts name: Linting Python Scripts
command: ./scripts/pylint_all.py command: ./scripts/pylint_all.py
- gitter_notify_failure_unless_pr
chk_antlr_grammar: chk_antlr_grammar:
docker: <<: *base_buildpack_focal
- image: buildpack-deps:focal
steps: steps:
- checkout - checkout
- run: - run:
@ -396,12 +468,10 @@ jobs:
- run: - run:
name: Run tests name: Run tests
command: ./scripts/test_antlr_grammar.sh command: ./scripts/test_antlr_grammar.sh
- gitter_notify_failure_unless_pr
chk_buglist: chk_buglist:
docker: <<: *base_node_latest
- image: circleci/node
environment:
TERM: xterm
steps: steps:
- checkout - checkout
- run: - run:
@ -413,12 +483,10 @@ jobs:
- run: - run:
name: Test buglist name: Test buglist
command: ./test/buglistTests.js command: ./test/buglistTests.js
- gitter_notify_failure_unless_pr
chk_proofs: chk_proofs:
docker: <<: *base_buildpack_latest
- image: buildpack-deps:latest
environment:
TERM: xterm
steps: steps:
- checkout - checkout
- run: - run:
@ -428,42 +496,36 @@ jobs:
apt-get -qy install python3-pip apt-get -qy install python3-pip
pip3 install --user z3-solver pip3 install --user z3-solver
- run: *run_proofs - run: *run_proofs
- gitter_notify_failure_unless_pr
chk_docs_pragma_min_version: chk_docs_pragma_min_version:
docker: <<: *base_ubuntu2004
- image: << pipeline.parameters.ubuntu-2004-docker-image >>
environment:
TERM: xterm
steps: steps:
- checkout - checkout
- run: *run_docs_pragma_min_version - run: *run_docs_pragma_min_version
- gitter_notify_failure_unless_pr
t_pyscripts_ubu: t_ubu_pyscripts:
docker: <<: *base_ubuntu2004
- image: << pipeline.parameters.ubuntu-2004-docker-image >>
steps: steps:
- checkout - checkout
- run: - run:
name: Python unit tests name: Python unit tests
command: python3 test/pyscriptTests.py command: python3 test/pyscriptTests.py
- gitter_notify_failure_unless_pr
t_pyscripts_win: t_win_pyscripts:
executor: <<: *base_win_powershell
name: win/default
shell: powershell.exe
steps: steps:
- run: git config --global core.autocrlf false - run: git config --global core.autocrlf false
- checkout - checkout
- run: - run:
name: Python unit tests name: Python unit tests
command: python.exe test/pyscriptTests.py command: python.exe test/pyscriptTests.py
- gitter_notify_failure_unless_pr
b_ubu: &b_ubu b_ubu: &b_ubu
resource_class: xlarge <<: *base_ubuntu2004_xlarge
docker:
- image: << pipeline.parameters.ubuntu-2004-docker-image >>
environment:
MAKEFLAGS: -j 10
steps: steps:
- checkout - checkout
- run: *run_build - run: *run_build
@ -471,10 +533,11 @@ jobs:
- store_artifacts: *artifact_solidity_upgrade - store_artifacts: *artifact_solidity_upgrade
- store_artifacts: *artifact_yul_phaser - store_artifacts: *artifact_yul_phaser
- persist_to_workspace: *artifacts_executables - persist_to_workspace: *artifacts_executables
- gitter_notify_failure_unless_pr
# x64 ASAN build, for testing for memory related bugs # x64 ASAN build, for testing for memory related bugs
b_ubu_asan: &b_ubu_asan b_ubu_asan: &b_ubu_asan
<<: *b_ubu <<: *base_ubuntu2004_xlarge
environment: environment:
CMAKE_OPTIONS: -DSANITIZE=address CMAKE_OPTIONS: -DSANITIZE=address
MAKEFLAGS: -j 10 MAKEFLAGS: -j 10
@ -484,24 +547,19 @@ jobs:
- run: *run_build - run: *run_build
- store_artifacts: *artifacts_solc - store_artifacts: *artifacts_solc
- persist_to_workspace: *artifacts_executables - persist_to_workspace: *artifacts_executables
- gitter_notify_failure_unless_pr
b_ubu_clang: &b_ubu_clang b_ubu_clang: &b_ubu_clang
resource_class: xlarge <<: *base_ubuntu2004_clang_xlarge
docker:
- image: << pipeline.parameters.ubuntu-2004-clang-docker-image >>
environment:
CC: clang
CXX: clang++
MAKEFLAGS: -j 10
steps: steps:
- checkout - checkout
- run: *run_build - run: *run_build
- store_artifacts: *artifacts_solc - store_artifacts: *artifacts_solc
- persist_to_workspace: *artifacts_executables - persist_to_workspace: *artifacts_executables
- gitter_notify_failure_unless_pr
b_ubu_asan_clang: &b_ubu_asan_clang b_ubu_asan_clang: &b_ubu_asan_clang
docker: <<: *base_ubuntu2004_clang
- image: << pipeline.parameters.ubuntu-2004-clang-docker-image >>
environment: environment:
CC: clang CC: clang
CXX: clang++ CXX: clang++
@ -512,10 +570,10 @@ jobs:
- run: *run_build - run: *run_build
- store_artifacts: *artifacts_solc - store_artifacts: *artifacts_solc
- persist_to_workspace: *artifacts_executables - persist_to_workspace: *artifacts_executables
- gitter_notify_failure_unless_pr
b_ubu_ubsan_clang: &b_ubu_ubsan_clang b_ubu_ubsan_clang: &b_ubu_ubsan_clang
docker: <<: *base_ubuntu2004_clang
- image: << pipeline.parameters.ubuntu-2004-clang-docker-image >>
environment: environment:
CC: clang CC: clang
CXX: clang++ CXX: clang++
@ -524,9 +582,9 @@ jobs:
steps: steps:
- checkout - checkout
- run: *run_build - run: *run_build
- gitter_notify_failure
- store_artifacts: *artifacts_solc - store_artifacts: *artifacts_solc
- persist_to_workspace: *artifacts_executables - persist_to_workspace: *artifacts_executables
- gitter_notify_failure_unless_pr
b_ubu_release: &b_ubu_release b_ubu_release: &b_ubu_release
<<: *b_ubu <<: *b_ubu
@ -535,7 +593,7 @@ jobs:
MAKEFLAGS: -j 10 MAKEFLAGS: -j 10
b_ubu_static: b_ubu_static:
<<: *b_ubu <<: *base_ubuntu2004_xlarge
environment: environment:
MAKEFLAGS: -j 10 MAKEFLAGS: -j 10
CMAKE_OPTIONS: -DCMAKE_BUILD_TYPE=Release -DUSE_Z3_DLOPEN=ON -DUSE_CVC4=OFF -DSOLC_STATIC_STDLIBS=ON 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 name: strip binary
command: strip build/solc/solc command: strip build/solc/solc
- store_artifacts: *artifacts_solc - store_artifacts: *artifacts_solc
- gitter_notify_failure_unless_pr
b_ubu_codecov: b_ubu_codecov:
<<: *b_ubu <<: *base_ubuntu2004_xlarge
environment: environment:
COVERAGE: ON COVERAGE: ON
CMAKE_BUILD_TYPE: Debug CMAKE_BUILD_TYPE: Debug
@ -557,9 +616,11 @@ jobs:
- checkout - checkout
- run: *run_build - run: *run_build
- persist_to_workspace: *artifacts_executables - persist_to_workspace: *artifacts_executables
- gitter_notify_failure_unless_pr
t_ubu_codecov: t_ubu_codecov:
<<: *test_ubuntu2004 <<: *base_ubuntu2004
parallelism: 6
environment: environment:
EVM: << pipeline.parameters.evm-version >> EVM: << pipeline.parameters.evm-version >>
OPTIMIZE: 1 OPTIMIZE: 1
@ -578,11 +639,12 @@ jobs:
name: "Coverage: All" name: "Coverage: All"
command: codecov --flags all --gcov-root build command: codecov --flags all --gcov-root build
- store_artifacts: *artifacts_test_results - store_artifacts: *artifacts_test_results
- gitter_notify_failure_unless_pr
# Builds in C++20 mode and uses debug build in order to speed up. # 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. # Do *NOT* store any artifacts or workspace as we don't run tests on this build.
b_ubu_cxx20: b_ubu_cxx20:
<<: *b_ubu <<: *base_ubuntu2004_xlarge
environment: environment:
CMAKE_BUILD_TYPE: Debug CMAKE_BUILD_TYPE: Debug
CMAKE_OPTIONS: -DCMAKE_CXX_STANDARD=20 -DUSE_CVC4=OFF CMAKE_OPTIONS: -DCMAKE_CXX_STANDARD=20 -DUSE_CVC4=OFF
@ -590,23 +652,19 @@ jobs:
steps: steps:
- checkout - checkout
- run: *run_build - run: *run_build
- gitter_notify_failure_unless_pr
b_ubu_ossfuzz: &b_ubu_ossfuzz b_ubu_ossfuzz: &b_ubu_ossfuzz
docker: <<: *base_ubuntu1604_clang
- image: << pipeline.parameters.ubuntu-1604-clang-ossfuzz-docker-image >>
environment:
CC: clang
CXX: clang++
TERM: xterm
MAKEFLAGS: -j 3
steps: steps:
- checkout - checkout
- run: *setup_prerelease_commit_hash - run: *setup_prerelease_commit_hash
- run: *run_build_ossfuzz - run: *run_build_ossfuzz
- persist_to_workspace: *artifacts_executables_ossfuzz - persist_to_workspace: *artifacts_executables_ossfuzz
- gitter_notify_failure_unless_pr
t_ubu_ossfuzz: &t_ubu_ossfuzz t_ubu_ossfuzz: &t_ubu_ossfuzz
<<: *test_ubuntu1604_clang <<: *base_ubuntu1604_clang
steps: steps:
- checkout - checkout
- attach_workspace: - attach_workspace:
@ -617,14 +675,13 @@ jobs:
git clone https://github.com/ethereum/solidity-fuzzing-corpus /tmp/solidity-fuzzing-corpus git clone https://github.com/ethereum/solidity-fuzzing-corpus /tmp/solidity-fuzzing-corpus
mkdir -p test_results mkdir -p test_results
scripts/regressions.py -o test_results scripts/regressions.py -o test_results
- gitter_notify_failure
- gitter_notify_success
- store_test_results: *store_test_results - store_test_results: *store_test_results
- store_artifacts: *artifacts_test_results - store_artifacts: *artifacts_test_results
- gitter_notify_failure_unless_pr
- gitter_notify_success_unless_pr
b_archlinux: b_archlinux:
docker: <<: *base_archlinux
- image: archlinux:base
environment: environment:
TERM: xterm TERM: xterm
MAKEFLAGS: -j 3 MAKEFLAGS: -j 3
@ -637,10 +694,10 @@ jobs:
- run: *run_build - run: *run_build
- store_artifacts: *artifacts_solc - store_artifacts: *artifacts_solc
- persist_to_workspace: *artifacts_executables - persist_to_workspace: *artifacts_executables
- gitter_notify_failure_unless_pr
b_osx: b_osx:
macos: <<: *base_osx
xcode: "11.0.0"
environment: environment:
TERM: xterm TERM: xterm
CMAKE_BUILD_TYPE: Release CMAKE_BUILD_TYPE: Release
@ -674,10 +731,10 @@ jobs:
- build/solc/solc - build/solc/solc
- build/test/soltest - build/test/soltest
- build/test/tools/solfuzzer - build/test/tools/solfuzzer
- gitter_notify_failure_unless_pr
t_osx_soltest: t_osx_soltest:
macos: <<: *base_osx
xcode: "11.0.0"
environment: environment:
EVM: << pipeline.parameters.evm-version >> EVM: << pipeline.parameters.evm-version >>
OPTIMIZE: 0 OPTIMIZE: 0
@ -692,12 +749,10 @@ jobs:
- run: *run_soltest - run: *run_soltest
- store_test_results: *store_test_results - store_test_results: *store_test_results
- store_artifacts: *artifacts_test_results - store_artifacts: *artifacts_test_results
- gitter_notify_failure_unless_pr
t_osx_cli: t_osx_cli:
macos: <<: *base_osx
xcode: "11.0.0"
environment:
TERM: xterm
steps: steps:
- checkout - checkout
- restore_cache: - restore_cache:
@ -707,14 +762,10 @@ jobs:
at: . at: .
- run: *run_cmdline_tests - run: *run_cmdline_tests
- store_artifacts: *artifacts_test_results - store_artifacts: *artifacts_test_results
- gitter_notify_failure_unless_pr
b_ems: b_ems:
resource_class: xlarge <<: *base_ems_xlarge
docker:
- image: << pipeline.parameters.emscripten-docker-image >>
environment:
MAKEFLAGS: -j 10
TERM: xterm
steps: steps:
- checkout - checkout
- run: - run:
@ -732,10 +783,10 @@ jobs:
paths: paths:
- soljson.js - soljson.js
- version.txt - version.txt
- gitter_notify_failure_unless_pr
b_docs: b_docs:
docker: <<: *base_ubuntu2004
- image: << pipeline.parameters.ubuntu-2004-docker-image >>
steps: steps:
- checkout - checkout
- run: *setup_prerelease_commit_hash - run: *setup_prerelease_commit_hash
@ -745,13 +796,15 @@ jobs:
- store_artifacts: - store_artifacts:
path: docs/_build/html/ path: docs/_build/html/
destination: docs-html destination: docs-html
- gitter_notify_failure_unless_pr
t_ubu_soltest: &t_ubu_soltest t_ubu_soltest_all: &t_ubu_soltest_all
<<: *test_ubuntu2004 <<: *base_ubuntu2004
parallelism: 6
<<: *steps_soltest_all
t_archlinux_soltest: &t_archlinux_soltest t_archlinux_soltest: &t_archlinux_soltest
docker: <<: *base_archlinux
- image: archlinux:base
environment: environment:
EVM: << pipeline.parameters.evm-version >> EVM: << pipeline.parameters.evm-version >>
OPTIMIZE: 0 OPTIMIZE: 0
@ -769,8 +822,7 @@ jobs:
<<: *steps_soltest <<: *steps_soltest
t_ubu_soltest_enforce_yul: &t_ubu_soltest_enforce_yul t_ubu_soltest_enforce_yul: &t_ubu_soltest_enforce_yul
docker: <<: *base_ubuntu2004
- image: << pipeline.parameters.ubuntu-2004-docker-image >>
environment: environment:
EVM: << pipeline.parameters.evm-version >> EVM: << pipeline.parameters.evm-version >>
SOLTEST_FLAGS: --enforce-via-yul SOLTEST_FLAGS: --enforce-via-yul
@ -778,74 +830,63 @@ jobs:
TERM: xterm TERM: xterm
<<: *steps_soltest <<: *steps_soltest
t_ubu_clang_soltest: &t_ubu_clang_soltest t_ubu_clang_soltest: &t_ubu_clang_soltest
<<: *test_ubuntu2004_clang <<: *base_ubuntu2004_clang
environment: environment:
EVM: << pipeline.parameters.evm-version >> EVM: << pipeline.parameters.evm-version >>
OPTIMIZE: 0 OPTIMIZE: 0
<<: *steps_soltest
t_ubu_release_soltest: &t_ubu_release_soltest t_ubu_release_soltest_all: &t_ubu_release_soltest_all
<<: *t_ubu_soltest # 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 t_ubu_cli: &t_ubu_cli
docker: <<: *base_ubuntu2004
- image: << pipeline.parameters.ubuntu-2004-docker-image >>
environment:
TERM: xterm
<<: *steps_cmdline_tests <<: *steps_cmdline_tests
t_ubu_release_cli: &t_ubu_release_cli t_ubu_release_cli: &t_ubu_release_cli
<<: *t_ubu_cli <<: *t_ubu_cli
t_ubu_asan_cli: t_ubu_asan_cli:
<<: *t_ubu_cli <<: *base_ubuntu2004
environment: environment:
TERM: xterm 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 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 <<: *steps_cmdline_tests
t_ubu_asan: t_ubu_asan_soltest:
<<: *test_asan <<: *base_ubuntu2004
parallelism: 6
environment: environment:
EVM: << pipeline.parameters.evm-version >> EVM: << pipeline.parameters.evm-version >>
OPTIMIZE: 0 OPTIMIZE: 0
SOLTEST_FLAGS: --no-smt 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 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
t_ubu_asan_clang:
<<: *test_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
t_ubu_ubsan_clang:
environment:
EVM: << pipeline.parameters.evm-version >>
docker:
- image: << pipeline.parameters.ubuntu-2004-clang-docker-image >>
steps:
- when:
condition: true
<<: *steps_soltest <<: *steps_soltest
- gitter_notify_failure
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_soltest:
<<: *base_ubuntu2004_clang
environment:
EVM: << pipeline.parameters.evm-version >>
<<: *steps_soltest
t_ubu_ubsan_clang_cli: t_ubu_ubsan_clang_cli:
docker: <<: *base_ubuntu2004_clang
- image: << pipeline.parameters.ubuntu-2004-clang-docker-image >>
steps:
- when:
condition: true
<<: *steps_cmdline_tests <<: *steps_cmdline_tests
- gitter_notify_failure
t_ems_solcjs: t_ems_solcjs:
docker: <<: *base_ubuntu2004
- image: << pipeline.parameters.ubuntu-2004-docker-image >>
environment:
TERM: xterm
steps: steps:
- checkout - checkout
- attach_workspace: - attach_workspace:
@ -862,10 +903,10 @@ jobs:
node --version node --version
npm --version npm --version
test/externalTests/solc-js/solc-js.sh /tmp/workspace/soljson.js $(cat /tmp/workspace/version.txt) 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: t_ems_ext_hardhat:
docker: <<: *base_node_latest
- image: circleci/node
environment: environment:
TERM: xterm TERM: xterm
HARDHAT_TESTS_SOLC_PATH: /tmp/workspace/soljson.js HARDHAT_TESTS_SOLC_PATH: /tmp/workspace/soljson.js
@ -888,6 +929,7 @@ jobs:
# NOTE: This is expected to work without running `yarn build` first. # NOTE: This is expected to work without running `yarn build` first.
cd hardhat/packages/hardhat-core cd hardhat/packages/hardhat-core
yarn test yarn test
- gitter_notify_failure_unless_pr
t_ems_ext: t_ems_ext:
parameters: parameters:
@ -899,9 +941,6 @@ jobs:
nodejs_version: nodejs_version:
type: integer type: integer
default: 14 default: 14
gitter_notify:
type: boolean
default: no
docker: docker:
- image: circleci/node:<<parameters.nodejs_version>> - image: circleci/node:<<parameters.nodejs_version>>
environment: environment:
@ -920,16 +959,10 @@ jobs:
name: External <<parameters.project>> tests name: External <<parameters.project>> tests
command: | command: |
test/externalTests/<<parameters.project>>.sh /tmp/workspace/soljson.js test/externalTests/<<parameters.project>>.sh /tmp/workspace/soljson.js
- when: - gitter_notify_failure_unless_pr
condition: <<parameters.gitter_notify>>
steps:
- gitter_notify_failure
- gitter_notify_success
b_win: &b_win b_win: &b_win
executor: <<: *base_win_powershell
name: win/default
shell: powershell.exe
steps: steps:
# NOTE: Not disabling git's core.autocrlf here because we want to build using the typical Windows config. # NOTE: Not disabling git's core.autocrlf here because we want to build using the typical Windows config.
- checkout - checkout
@ -957,16 +990,15 @@ jobs:
paths: paths:
- .\solc\*\solc.exe - .\solc\*\solc.exe
- .\test\*\soltest.exe - .\test\*\soltest.exe
- gitter_notify_failure_unless_pr
b_win_release: b_win_release:
<<: *b_win <<: *b_win
environment: environment:
FORCE_RELEASE: ON FORCE_RELEASE: ON
t_win: &t_win t_win_soltest: &t_win_soltest
executor: <<: *base_win_powershell
name: win/default
shell: powershell.exe
steps: steps:
# NOTE: Git's default core.autocrlf is fine for running soltest. We get additional coverage # NOTE: Git's default core.autocrlf is fine for running soltest. We get additional coverage
# for files using CRLF that way. # for files using CRLF that way.
@ -981,13 +1013,13 @@ jobs:
command: .circleci/soltest.ps1 command: .circleci/soltest.ps1
- store_test_results: *store_test_results - store_test_results: *store_test_results
- store_artifacts: *artifacts_test_results - store_artifacts: *artifacts_test_results
- gitter_notify_failure_unless_pr
t_win_release: t_win_release_soltest:
<<: *t_win <<: *t_win_soltest
b_bytecode_ubu: b_bytecode_ubu:
docker: <<: *base_ubuntu2004
- image: << pipeline.parameters.ubuntu-2004-docker-image >>
steps: steps:
- checkout - checkout
- attach_workspace: - attach_workspace:
@ -1005,12 +1037,10 @@ jobs:
paths: paths:
- bytecode-report-ubuntu-json.txt - bytecode-report-ubuntu-json.txt
- bytecode-report-ubuntu-cli.txt - bytecode-report-ubuntu-cli.txt
- gitter_notify_failure_unless_pr
b_bytecode_osx: b_bytecode_osx:
macos: <<: *base_osx
xcode: "11.0.0"
environment:
TERM: xterm
steps: steps:
- checkout - checkout
- attach_workspace: - attach_workspace:
@ -1028,11 +1058,10 @@ jobs:
paths: paths:
- bytecode-report-osx-json.txt - bytecode-report-osx-json.txt
- bytecode-report-osx-cli.txt - bytecode-report-osx-cli.txt
- gitter_notify_failure_unless_pr
b_bytecode_win: b_bytecode_win:
executor: <<: *base_win_cmd
name: win/default
shell: cmd.exe
steps: steps:
# NOTE: For bytecode generation we need the input files to be byte-for-byte identical on all # 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. # platforms so line ending conversions must absolutely be disabled.
@ -1053,10 +1082,10 @@ jobs:
paths: paths:
- bytecode-report-windows-json.txt - bytecode-report-windows-json.txt
- bytecode-report-windows-cli.txt - bytecode-report-windows-cli.txt
- gitter_notify_failure_unless_pr
b_bytecode_ems: b_bytecode_ems:
docker: <<: *base_node_latest
- image: circleci/node:16
environment: environment:
SOLC_EMSCRIPTEN: "On" SOLC_EMSCRIPTEN: "On"
steps: steps:
@ -1070,10 +1099,10 @@ jobs:
root: . root: .
paths: paths:
- bytecode-report-emscripten.txt - bytecode-report-emscripten.txt
- gitter_notify_failure_unless_pr
t_bytecode_compare: t_bytecode_compare:
docker: <<: *base_ubuntu2004
- image: << pipeline.parameters.ubuntu-2004-docker-image >>
environment: environment:
REPORT_FILES: | REPORT_FILES: |
bytecode-report-emscripten.txt bytecode-report-emscripten.txt
@ -1107,6 +1136,7 @@ jobs:
# NOTE: store_artifacts does not support the 'when' attribute. # 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. # Fortunately when the artifact does not exist it just says "No artifact files found" and ignores it.
path: all-bytecode-reports.zip path: all-bytecode-reports.zip
- gitter_notify_failure_unless_pr
workflows: workflows:
version: 2 version: 2
@ -1123,8 +1153,8 @@ workflows:
- chk_errorcodes: *workflow_trigger_on_tags - chk_errorcodes: *workflow_trigger_on_tags
- chk_antlr_grammar: *workflow_trigger_on_tags - chk_antlr_grammar: *workflow_trigger_on_tags
- chk_docs_pragma_min_version: *workflow_trigger_on_tags - chk_docs_pragma_min_version: *workflow_trigger_on_tags
- t_pyscripts_ubu: *workflow_trigger_on_tags - t_ubu_pyscripts: *workflow_trigger_on_tags
- t_pyscripts_win: *workflow_trigger_on_tags - t_win_pyscripts: *workflow_trigger_on_tags
# build-only # build-only
- b_docs: *workflow_trigger_on_tags - b_docs: *workflow_trigger_on_tags
@ -1146,7 +1176,7 @@ workflows:
# Ubuntu build and tests # Ubuntu build and tests
- b_ubu: *workflow_trigger_on_tags - b_ubu: *workflow_trigger_on_tags
- t_ubu_cli: *workflow_ubuntu2004 - t_ubu_cli: *workflow_ubuntu2004
- t_ubu_soltest: *workflow_ubuntu2004 - t_ubu_soltest_all: *workflow_ubuntu2004
- t_ubu_soltest_enforce_yul: *workflow_ubuntu2004 - t_ubu_soltest_enforce_yul: *workflow_ubuntu2004
- b_ubu_clang: *workflow_trigger_on_tags - b_ubu_clang: *workflow_trigger_on_tags
- t_ubu_clang_soltest: *workflow_ubuntu2004_clang - t_ubu_clang_soltest: *workflow_ubuntu2004_clang
@ -1154,7 +1184,7 @@ workflows:
# Ubuntu fake release build and tests # Ubuntu fake release build and tests
- b_ubu_release: *workflow_trigger_on_tags - b_ubu_release: *workflow_trigger_on_tags
- t_ubu_release_cli: *workflow_ubuntu2004_release - 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 # Emscripten build and tests that take 15 minutes or less
- b_ems: *workflow_trigger_on_tags - b_ems: *workflow_trigger_on_tags
@ -1217,8 +1247,8 @@ workflows:
# Windows build and tests # Windows build and tests
- b_win: *workflow_trigger_on_tags - b_win: *workflow_trigger_on_tags
- b_win_release: *workflow_trigger_on_tags - b_win_release: *workflow_trigger_on_tags
- t_win: *workflow_win - t_win_soltest: *workflow_win
- t_win_release: *workflow_win_release - t_win_release_soltest: *workflow_win_release
# Bytecode comparison: # Bytecode comparison:
- b_bytecode_ubu: - b_bytecode_ubu:
@ -1262,13 +1292,13 @@ workflows:
# ASan build and tests # ASan build and tests
- b_ubu_asan: *workflow_trigger_on_tags - b_ubu_asan: *workflow_trigger_on_tags
- b_ubu_asan_clang: *workflow_trigger_on_tags - b_ubu_asan_clang: *workflow_trigger_on_tags
- t_ubu_asan: *workflow_ubuntu2004_asan - t_ubu_asan_soltest: *workflow_ubuntu2004_asan
- t_ubu_asan_clang: *workflow_ubuntu2004_asan_clang - t_ubu_asan_clang_soltest: *workflow_ubuntu2004_asan_clang
- t_ubu_asan_cli: *workflow_ubuntu2004_asan - t_ubu_asan_cli: *workflow_ubuntu2004_asan
# UBSan build and tests # UBSan build and tests
- b_ubu_ubsan_clang: *workflow_trigger_on_tags - 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 - t_ubu_ubsan_clang_cli: *workflow_ubuntu2004_ubsan_clang
# Emscripten build and tests that take more than 15 minutes to execute # Emscripten build and tests that take more than 15 minutes to execute
@ -1277,4 +1307,3 @@ workflows:
<<: *workflow_emscripten <<: *workflow_emscripten
name: t_ems_test_ext_colony name: t_ems_test_ext_colony
project: colony project: colony
gitter_notify: yes

View File

@ -46,7 +46,7 @@ source "${REPODIR}/scripts/common.sh"
# Test result output directory (CircleCI is reading test results from here) # Test result output directory (CircleCI is reading test results from here)
mkdir -p test_results 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 ulimit -s 16384
get_logfile_basename() { get_logfile_basename() {

View File

@ -21,7 +21,7 @@ include(EthPolicy)
eth_policy() eth_policy()
# project name and version should be set after cmake_policy CMP0048 # 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 # OSX target needed in order to support std::visit
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14")
project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX)

View File

@ -7,20 +7,36 @@ Breaking changes:
* Commandline Interface: Remapping targets are not automatically added to allowed paths. * 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: Language Features:
* Inline Assembly: Support ``.address`` and ``.selector`` on external function pointers to access their address and function selector. * Inline Assembly: Support ``.address`` and ``.selector`` on external function pointers to access their address and function selector.
Compiler Features: 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: 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: 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. * 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: 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``. * 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: 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. * 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: Bugfixes:
@ -31,13 +47,23 @@ Bugfixes:
* Commandline Interface: Report output selection options unsupported by the selected input mode instead of ignoring them. * 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. * 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``). * 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 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. * TypeChecker: Improved error message for constant variables with (nested) mapping types.
* Yul Assembler: Fix internal error when function names are not unique. * Yul Assembler: Fix internal error when function names are not unique.
* Yul IR Generator: Do not output empty switches/if-bodies for empty contracts. * 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) ### 0.8.9 (2021-09-29)

View File

@ -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 # http://stackoverflow.com/questions/21617158/how-to-silence-unused-command-line-argument-error-with-clang-without-disabling-i
add_compile_options(-Qunused-arguments) add_compile_options(-Qunused-arguments)
elseif(EMSCRIPTEN) 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 # Leave only exported symbols as public and aggressively remove others
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -ffunction-sections -fvisibility=hidden") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -ffunction-sections -fvisibility=hidden")
# Optimisation level # Optimisation level
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
# Re-enable exception catching (optimisations above -O1 disable it) # Re-enable exception catching (optimisations above -O1 disable it)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0") 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) # 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 # 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 # 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() # 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 # 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 # Abort if linking results in any undefined symbols
# Note: this is on by default in the CMake Emscripten module which we aren't using # 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. # Disallow deprecated emscripten build options.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s STRICT=1") 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. # 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. # 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. # 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 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. # 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. # 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. # Disable warnings about not being pure asm.js due to memory growth.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-almost-asm") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-almost-asm")
endif() endif()

View File

@ -6,9 +6,34 @@ static std::string const otherLicenses{R"(Most of the code is licensed under GPL
parts are as follows: parts are as follows:
libkeccak-tiny: libkeccak-tiny:
The file libsolutil/Keccak256.cpp incorporates libkeccak-tiny.
A single-file implementation of SHA-3 and SHAKE implemented by David Leon Gil 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. 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: jsoncpp:
The JsonCpp library's source code, including accompanying documentation, The JsonCpp library's source code, including accompanying documentation,
tests and demonstration applications, are licensed under the following tests and demonstration applications, are licensed under the following

View File

@ -1544,6 +1544,10 @@
], ],
"released": "2021-01-27" "released": "2021-01-27"
}, },
"0.8.10": {
"bugs": [],
"released": "2021-11-09"
},
"0.8.2": { "0.8.2": {
"bugs": [ "bugs": [
"SignedImmutables", "SignedImmutables",

View File

@ -91,7 +91,7 @@ else:
# List of patterns, relative to source directory, that match files and # List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files. # 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 # The reST default role (used for this markup: `text`) to use for all
# documents. # documents.

View File

@ -222,6 +222,9 @@ Pure Functions
-------------- --------------
Functions can be declared ``pure`` in which case they promise not to read from or modify the state. 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:: .. note::
If the compiler's EVM target is Byzantium or newer (default) the opcode ``STATICCALL`` is used, If the compiler's EVM target is Byzantium or newer (default) the opcode ``STATICCALL`` is used,

View File

@ -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 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 always succeed, Solidity uses the ``extcodesize`` opcode to check that
the contract that is about to be called actually exists (it contains code) 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 Note that this check is not performed in case of :ref:`low-level calls <address_related>` which
operate on addresses rather than contract instances. 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 Function calls also cause exceptions if the called contract itself
throws an exception or goes out of gas. throws an exception or goes out of gas.

View File

@ -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>`_. `security fixes <https://github.com/ethereum/solidity/security/policy#supported-versions>`_.
Furthermore, breaking changes as well as Furthermore, breaking changes as well as
new features are introduced regularly. We currently use 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:: .. warning::
@ -136,6 +136,7 @@ Contents
using-the-compiler.rst using-the-compiler.rst
analysing-compilation-output.rst analysing-compilation-output.rst
ir-breaking-changes.rst
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2

View File

@ -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: https://www.visualstudio.com/vs/
.. _Visual Studio 2019 Build Tools: https://www.visualstudio.com/downloads/#build-tools-for-visual-studio-2019 .. _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:
--------------------------
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:
.. code-block:: bat .. code-block:: bat
scripts\install_deps.ps1 scripts\install_deps.ps1
Note that the latter command will install ``boost`` and ``cmake`` to the ``deps`` subdirectory, while the former command This will install ``boost`` and ``cmake`` to the ``deps`` subdirectory.
will attempt to install the dependencies globally.
Clone the Repository Clone the Repository
-------------------- --------------------

View File

@ -429,12 +429,10 @@ is transformed to
.. code-block:: text .. code-block:: text
{
Init... Init...
for {} C { Post... } { for {} C { Post... } {
Body... Body...
} }
}
This eases the rest of the optimization process because we can ignore This eases the rest of the optimization process because we can ignore
the complicated scoping rules of the for loop initialisation block. the complicated scoping rules of the for loop initialisation block.

View File

@ -565,3 +565,24 @@ contracts, the Ether is forever lost.
If you want to deactivate your contracts, you should instead **disable** them If you want to deactivate your contracts, you should instead **disable** them
by changing some internal state which causes all functions to revert. This by changing some internal state which causes all functions to revert. This
makes it impossible to use the contract, as it returns Ether immediately. 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).

View File

@ -5,8 +5,26 @@
Solidity IR-based Codegen Changes Solidity IR-based Codegen Changes
********************************* *********************************
This section highlights the main differences between the old and the IR-based codegen, Solidity can generate EVM bytecode in two different ways:
along with the reasoning behind the changes and how to update affected code. 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 Semantic Only Changes
===================== =====================
@ -14,8 +32,13 @@ Semantic Only Changes
This section lists the changes that are semantic-only, thus potentially This section lists the changes that are semantic-only, thus potentially
hiding new and different behavior in existing code. 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. - When storage structs are deleted, every storage slot that contains
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). 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 .. 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. 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. 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: This causes differences in some contracts, for example:
.. code-block:: solidity .. 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). Previously ``f()`` would return ``0x6465616462656566313564656164000000000000000000000000000000000010``
Now it is returning ``0x6465616462656566000000000000000000000000000000000000000000000010`` (it has correct length, and correct elements, but does not contain superfluous data). (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 .. index:: ! evaluation order; expression
@ -183,7 +211,8 @@ This causes differences in some contracts, for example:
.. index:: ! evaluation order; function arguments .. 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: For example:
.. code-block:: solidity .. code-block:: solidity
@ -227,11 +256,15 @@ This causes differences in some contracts, for example:
- Old code generator: ``aMod = 0`` and ``mMod = 2`` - Old code generator: ``aMod = 0`` and ``mMod = 2``
- New code generator: ``aMod = 4`` and ``mMod = 0`` - 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: For example:
.. code-block:: solidity .. code-block:: solidity
:force:
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
pragma solidity >0.8.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. 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). 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 calls through function pointers always have to use an internal dispatch function that uses the ``switch`` statement to select
the right function. 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 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 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: For example:

View File

@ -241,7 +241,7 @@ and to send Ether (in units of wei) to a payable address using the ``transfer``
.. code-block:: solidity .. code-block:: solidity
:force: :force:
address payable x = address(0x123); address payable x = payable(0x123);
address myAddress = address(this); address myAddress = address(this);
if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10); if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);

View File

@ -2,7 +2,7 @@ if (EMSCRIPTEN)
# Specify which functions to export in soljson.js. # Specify which functions to export in soljson.js.
# Note that additional Emscripten-generated methods needed by solc-js are # Note that additional Emscripten-generated methods needed by solc-js are
# defined to be exported in cmake/EthCompilerSettings.cmake. # 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) add_executable(soljson libsolc.cpp libsolc.h)
target_link_libraries(soljson PRIVATE solidity) target_link_libraries(soljson PRIVATE solidity)
else() else()

View File

@ -2629,11 +2629,23 @@ void ExpressionCompiler::appendExternalFunctionCall(
bool existenceChecked = false; bool existenceChecked = false;
// Check the target contract exists (has code) for non-low-level calls. // Check the target contract exists (has code) for non-low-level calls.
if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall) if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall)
{
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 << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO;
m_context.appendConditionalRevert(false, "Target contract does not contain code"); m_context.appendConditionalRevert(false, "Target contract does not contain code");
existenceChecked = true; existenceChecked = true;
} }
}
if (_functionType.gasSet()) if (_functionType.gasSet())
m_context << dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos)); m_context << dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));

View File

@ -438,7 +438,7 @@ string IRGenerator::generateModifier(
for (size_t i = 0; i < retParamsIn.size(); ++i) for (size_t i = 0; i < retParamsIn.size(); ++i)
{ {
retParams.emplace_back(m_context.newYulVariable()); 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("retParams", joinHumanReadable(retParams));
t("assignRetParams", assignRetParams); t("assignRetParams", assignRetParams);
@ -529,7 +529,7 @@ string IRGenerator::generateFunctionWithModifierInner(FunctionDefinition const&
for (size_t i = 0; i < retParams.size(); ++i) for (size_t i = 0; i < retParams.size(); ++i)
{ {
retParamsIn.emplace_back(m_context.newYulVariable()); retParamsIn.emplace_back(m_context.newYulVariable());
assignRetParams += retParams.back() + " := " + retParamsIn[i] + "\n"; assignRetParams += retParams.at(i) + " := " + retParamsIn.at(i) + "\n";
} }
vector<string> params = retParamsIn; vector<string> params = retParamsIn;
for (auto const& varDecl: _function.parameters()) for (auto const& varDecl: _function.parameters())
@ -1069,9 +1069,6 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
string IRGenerator::memoryInit(bool _useMemoryGuard) 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 // 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 // and thus can assume all memory to be zero, including the contents of
// the "zero memory area" (the position CompilerUtils::zeroPointer points to). // the "zero memory area" (the position CompilerUtils::zeroPointer points to).

View File

@ -2451,8 +2451,10 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
appendCode() << "mstore(add(" << m_utils.allocateUnboundedFunction() << "() , " << to_string(returnInfo.estimatedReturnSize) << "), 0)\n"; 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 // storage for arguments and returned data
let <pos> := <allocateUnbounded>() let <pos> := <allocateUnbounded>()
mstore(<pos>, <shl28>(<funSel>)) mstore(<pos>, <shl28>(<funSel>))
@ -2477,6 +2479,18 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
} }
)"); )");
templ("revertNoCode", m_utils.revertReasonIfDebugFunction("Target contract does not contain code")); 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("pos", m_context.newYulVariable());
templ("end", m_context.newYulVariable()); templ("end", m_context.newYulVariable());
if (_functionCall.annotation().tryCall) if (_functionCall.annotation().tryCall)
@ -2532,6 +2546,8 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10; u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10;
if (funType.valueSet()) if (funType.valueSet())
gasNeededByCaller += evmasm::GasCosts::callValueTransferGas; gasNeededByCaller += evmasm::GasCosts::callValueTransferGas;
if (!checkExtcodesize)
gasNeededByCaller += evmasm::GasCosts::callNewAccountGas; // we never know
templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")"); templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")");
} }
// Order is important here, STATICCALL might overlap with DELEGATECALL. // Order is important here, STATICCALL might overlap with DELEGATECALL.

View File

@ -791,7 +791,7 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall)
valueIndex = i; valueIndex = i;
break; break;
} }
solAssert(valueIndex, ""); if (valueIndex)
state().addBalance(state().thisAddress(), 0 - expr(*callOptions->options().at(*valueIndex))); state().addBalance(state().thisAddress(), 0 - expr(*callOptions->options().at(*valueIndex)));
} }
@ -1623,28 +1623,23 @@ void CHC::checkVerificationTargets()
// Also, all possible contexts in which an external function can be called has been recorded (m_queryPlaceholders). // 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 // 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. // 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) for (auto const& [function, placeholders]: m_queryPlaceholders)
{ {
auto functionTargets = transactionVerificationTargetsIds(function); auto functionTargets = transactionVerificationTargetsIds(function);
for (auto const& placeholder: placeholders) for (auto const& placeholder: placeholders)
for (unsigned id: functionTargets) for (unsigned id: functionTargets)
{ targetEntryPoints[id].push_back(placeholder);
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
});
}
} }
set<unsigned> checkedErrorIds; set<unsigned> checkedErrorIds;
for (auto const& target: verificationTargets) for (auto const& [targetId, placeholders]: targetEntryPoints)
{ {
string errorType; string errorType;
ErrorId errorReporterId; ErrorId errorReporterId;
auto const& target = m_verificationTargets.at(targetId);
if (target.type == VerificationTargetType::PopEmptyArray) if (target.type == VerificationTargetType::PopEmptyArray)
{ {
solAssert(dynamic_cast<FunctionCall const*>(target.errorNode), ""); solAssert(dynamic_cast<FunctionCall const*>(target.errorNode), "");
@ -1692,7 +1687,7 @@ void CHC::checkVerificationTargets()
else else
solAssert(false, ""); 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); checkedErrorIds.insert(target.errorId);
} }
@ -1750,7 +1745,7 @@ void CHC::checkVerificationTargets()
{ {
set<unsigned> seenErrors; set<unsigned> seenErrors;
msg += "<errorCode> = 0 -> no errors\n"; msg += "<errorCode> = 0 -> no errors\n";
for (auto const& target: verificationTargets) for (auto const& [id, target]: m_verificationTargets)
if (!seenErrors.count(target.errorId)) if (!seenErrors.count(target.errorId))
{ {
seenErrors.insert(target.errorId); seenErrors.insert(target.errorId);
@ -1785,6 +1780,7 @@ void CHC::checkVerificationTargets()
void CHC::checkAndReportTarget( void CHC::checkAndReportTarget(
CHCVerificationTarget const& _target, CHCVerificationTarget const& _target,
vector<CHCQueryPlaceholder> const& _placeholders,
ErrorId _errorReporterId, ErrorId _errorReporterId,
string _satMsg, string _satMsg,
string _unknownMsg string _unknownMsg
@ -1794,7 +1790,12 @@ void CHC::checkAndReportTarget(
return; return;
createErrorBlock(); 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 const& location = _target.errorNode->location();
auto [result, invariant, model] = query(error(), location); auto [result, invariant, model] = query(error(), location);
if (result == CheckResult::UNSATISFIABLE) if (result == CheckResult::UNSATISFIABLE)

View File

@ -252,11 +252,13 @@ private:
void verificationTargetEncountered(ASTNode const* const _errorNode, VerificationTargetType _type, smtutil::Expression const& _errorCondition); void verificationTargetEncountered(ASTNode const* const _errorNode, VerificationTargetType _type, smtutil::Expression const& _errorCondition);
void checkVerificationTargets(); void checkVerificationTargets();
// Forward declaration. Definition is below. // Forward declarations. Definitions are below.
struct CHCVerificationTarget; struct CHCVerificationTarget;
struct CHCQueryPlaceholder;
void checkAssertTarget(ASTNode const* _scope, CHCVerificationTarget const& _target); void checkAssertTarget(ASTNode const* _scope, CHCVerificationTarget const& _target);
void checkAndReportTarget( void checkAndReportTarget(
CHCVerificationTarget const& _target, CHCVerificationTarget const& _target,
std::vector<CHCQueryPlaceholder> const& _placeholders,
langutil::ErrorId _errorReporterId, langutil::ErrorId _errorReporterId,
std::string _satMsg, std::string _satMsg,
std::string _unknownMsg = "" std::string _unknownMsg = ""

View File

@ -83,6 +83,7 @@
#include <utility> #include <utility>
#include <map> #include <map>
#include <limits> #include <limits>
#include <string>
using namespace std; using namespace std;
using namespace solidity; using namespace solidity;
@ -350,8 +351,22 @@ bool CompilerStack::parse()
else else
{ {
source.ast->annotation().path = path; 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) 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& newPath = newSource.first;
string const& newContents = newSource.second; string const& newContents = newSource.second;
@ -1091,7 +1106,7 @@ string const& CompilerStack::Source::ipfsUrl() const
return ipfsUrlCached; return ipfsUrlCached;
} }
StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string const& _sourcePath) StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast)
{ {
solAssert(m_stackState < ParsedAndImported, ""); solAssert(m_stackState < ParsedAndImported, "");
StringMap newSources; StringMap newSources;
@ -1100,14 +1115,8 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string
for (auto const& node: _ast.nodes()) for (auto const& node: _ast.nodes())
if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get())) 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)) if (m_sources.count(importPath) || newSources.count(importPath))
continue; continue;
@ -1248,7 +1257,9 @@ void CompilerStack::assemble(
m_errorReporter.warning( m_errorReporter.warning(
5574_error, 5574_error,
_contract.location(), _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. " "This contract may not be deployable on mainnet. "
"Consider enabling the optimizer (with a low \"runs\" value!), " "Consider enabling the optimizer (with a low \"runs\" value!), "
"turning off revert strings, or using libraries." "turning off revert strings, or using libraries."

View File

@ -392,9 +392,9 @@ private:
void findAndReportCyclicContractDependencies(); void findAndReportCyclicContractDependencies();
/// Loads the missing sources from @a _ast (named @a _path) using the callback /// 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. /// @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); std::string applyRemapping(std::string const& _path, std::string const& _context);
void resolveImports(); void resolveImports();

View File

@ -40,6 +40,8 @@ add_library(yul
Dialect.cpp Dialect.cpp
Dialect.h Dialect.h
Exceptions.h Exceptions.h
FunctionReferenceResolver.cpp
FunctionReferenceResolver.h
Object.cpp Object.cpp
Object.h Object.h
ObjectParser.cpp ObjectParser.cpp
@ -140,8 +142,6 @@ add_library(yul
optimiser/FullInliner.h optimiser/FullInliner.h
optimiser/FunctionCallFinder.cpp optimiser/FunctionCallFinder.cpp
optimiser/FunctionCallFinder.h optimiser/FunctionCallFinder.h
optimiser/FunctionDefinitionCollector.cpp
optimiser/FunctionDefinitionCollector.h
optimiser/FunctionGrouper.cpp optimiser/FunctionGrouper.cpp
optimiser/FunctionGrouper.h optimiser/FunctionGrouper.h
optimiser/FunctionHoister.cpp optimiser/FunctionHoister.cpp

View File

@ -18,10 +18,9 @@
#include <libyul/ControlFlowSideEffectsCollector.h> #include <libyul/ControlFlowSideEffectsCollector.h>
#include <libyul/optimiser/FunctionDefinitionCollector.h>
#include <libyul/AST.h> #include <libyul/AST.h>
#include <libyul/Dialect.h> #include <libyul/Dialect.h>
#include <libyul/FunctionReferenceResolver.h>
#include <libsolutil/Common.h> #include <libsolutil/Common.h>
#include <libsolutil/CommonData.h> #include <libsolutil/CommonData.h>
@ -37,16 +36,15 @@ using namespace solidity::yul;
ControlFlowBuilder::ControlFlowBuilder(Block const& _ast) ControlFlowBuilder::ControlFlowBuilder(Block const& _ast)
{ {
for (auto const& statement: _ast.statements) m_currentNode = newNode();
if (auto const* function = get_if<FunctionDefinition>(&statement)) (*this)(_ast);
(*this)(*function);
} }
void ControlFlowBuilder::operator()(FunctionCall const& _functionCall) void ControlFlowBuilder::operator()(FunctionCall const& _functionCall)
{ {
walkVector(_functionCall.arguments | ranges::views::reverse); walkVector(_functionCall.arguments | ranges::views::reverse);
newConnectedNode(); newConnectedNode();
m_currentNode->functionCall = _functionCall.functionName.name; m_currentNode->functionCall = &_functionCall;
} }
void ControlFlowBuilder::operator()(If const& _if) void ControlFlowBuilder::operator()(If const& _if)
@ -80,7 +78,9 @@ void ControlFlowBuilder::operator()(Switch const& _switch)
void ControlFlowBuilder::operator()(FunctionDefinition const& _function) void ControlFlowBuilder::operator()(FunctionDefinition const& _function)
{ {
ScopedSaveAndRestore currentNode(m_currentNode, nullptr); 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; FunctionFlow flow;
flow.exit = newNode(); flow.exit = newNode();
@ -92,7 +92,7 @@ void ControlFlowBuilder::operator()(FunctionDefinition const& _function)
m_currentNode->successors.emplace_back(flow.exit); m_currentNode->successors.emplace_back(flow.exit);
m_functionFlows[_function.name] = move(flow); m_functionFlows[&_function] = move(flow);
m_leave = nullptr; m_leave = nullptr;
} }
@ -166,14 +166,17 @@ ControlFlowSideEffectsCollector::ControlFlowSideEffectsCollector(
Block const& _ast Block const& _ast
): ):
m_dialect(_dialect), 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); yulAssert(!flow.entry->functionCall);
m_processedNodes[name] = {}; yulAssert(function);
m_pendingNodes[name].push_front(flow.entry); m_processedNodes[function] = {};
m_functionSideEffects[name] = {false, false, false}; 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 // Process functions while we have progress. For now, we are only interested
@ -182,8 +185,8 @@ ControlFlowSideEffectsCollector::ControlFlowSideEffectsCollector(
while (progress) while (progress)
{ {
progress = false; progress = false;
for (auto const& functionName: m_pendingNodes | ranges::views::keys) for (FunctionDefinition const* function: m_pendingNodes | ranges::views::keys)
if (processFunction(functionName)) if (processFunction(*function))
progress = true; progress = true;
} }
@ -192,57 +195,64 @@ ControlFlowSideEffectsCollector::ControlFlowSideEffectsCollector(
// If we have not set `canContinue` by now, the function's exit // If we have not set `canContinue` by now, the function's exit
// is not reachable. // 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]; yulAssert(function);
auto _visit = [&, visited = std::set<YulString>{}](YulString _function, auto&& _recurse) mutable { ControlFlowSideEffects& functionSideEffects = m_functionSideEffects[function];
if (sideEffects.canTerminate && sideEffects.canRevert) 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; return;
if (!visited.insert(_function).second) if (!visited.insert(&_function).second)
return; return;
ControlFlowSideEffects const* calledSideEffects = nullptr; for (FunctionCall const* call: m_functionCalls.at(&_function))
if (BuiltinFunction const* f = _dialect.builtin(_function)) {
calledSideEffects = &f->controlFlowSideEffects; ControlFlowSideEffects const& calledSideEffects = sideEffects(*call);
else if (calledSideEffects.canTerminate)
calledSideEffects = &m_functionSideEffects.at(_function); functionSideEffects.canTerminate = true;
if (calledSideEffects.canRevert)
functionSideEffects.canRevert = true;
if (calledSideEffects->canTerminate) if (m_functionReferences.count(call))
sideEffects.canTerminate = true; _recurse(*m_functionReferences.at(call), _recurse);
if (calledSideEffects->canRevert) }
sideEffects.canRevert = true; };
_visit(*function, _visit);
set<YulString> emptySet;
for (YulString callee: util::valueOrDefault(m_functionCalls, _function, emptySet))
_recurse(callee, _recurse);
};
for (auto const& call: calls)
_visit(call, _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; 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; return true;
} }
for (ControlFlowNode const* s: node->successors) for (ControlFlowNode const* s: node->successors)
recordReachabilityAndQueue(_name, s); recordReachabilityAndQueue(_function, s);
progress = true; progress = true;
} }
return progress; 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) { auto it = ranges::find_if(nodes, [this](ControlFlowNode const* _node) {
return !_node->functionCall || sideEffects(*_node->functionCall).canContinue; return !_node->functionCall || sideEffects(*_node->functionCall).canContinue;
}); });
@ -254,22 +264,22 @@ ControlFlowNode const* ControlFlowSideEffectsCollector::nextProcessableNode(YulS
return node; 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; return builtin->controlFlowSideEffects;
else else
return m_functionSideEffects.at(_functionName); return m_functionSideEffects.at(m_functionReferences.at(&_call));
} }
void ControlFlowSideEffectsCollector::recordReachabilityAndQueue( void ControlFlowSideEffectsCollector::recordReachabilityAndQueue(
YulString _functionName, FunctionDefinition const& _function,
ControlFlowNode const* _node ControlFlowNode const* _node
) )
{ {
if (_node->functionCall) if (_node->functionCall)
m_functionCalls[_functionName].insert(*_node->functionCall); m_functionCalls[&_function].insert(_node->functionCall);
if (m_processedNodes[_functionName].insert(_node).second) if (m_processedNodes[&_function].insert(_node).second)
m_pendingNodes.at(_functionName).push_front(_node); m_pendingNodes.at(&_function).push_front(_node);
} }

View File

@ -34,8 +34,8 @@ struct Dialect;
struct ControlFlowNode struct ControlFlowNode
{ {
std::vector<ControlFlowNode const*> successors; std::vector<ControlFlowNode const*> successors;
/// Name of the called function if the node calls a function. /// Function call AST node, if present.
std::optional<YulString> functionCall; FunctionCall const* functionCall = nullptr;
}; };
/** /**
@ -56,7 +56,7 @@ public:
/// Computes the control-flows of all function defined in the block. /// Computes the control-flows of all function defined in the block.
/// Assumes the functions are hoisted to the topmost block. /// Assumes the functions are hoisted to the topmost block.
explicit ControlFlowBuilder(Block const& _ast); 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: private:
using ASTWalker::operator(); using ASTWalker::operator();
@ -79,12 +79,14 @@ private:
ControlFlowNode const* m_break = nullptr; ControlFlowNode const* m_break = nullptr;
ControlFlowNode const* m_continue = 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 class ControlFlowSideEffectsCollector
{ {
@ -94,36 +96,43 @@ public:
Block const& _ast Block const& _ast
); );
std::map<YulString, ControlFlowSideEffects> const& functionSideEffects() const std::map<FunctionDefinition const*, ControlFlowSideEffects> const& functionSideEffects() const
{ {
return m_functionSideEffects; return m_functionSideEffects;
} }
/// Returns the side effects by function name, requires unique function names.
std::map<YulString, ControlFlowSideEffects> functionSideEffectsNamed() const;
private: private:
/// @returns false if nothing could be processed. /// @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 /// @returns the next pending node of the function that is not
/// a function call to a function that might not continue. /// a function call to a function that might not continue.
/// De-queues the node or returns nullptr if no such node is found. /// 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 /// @returns the side-effects of either a builtin call or a user defined function
/// call (as far as already computed). /// 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) /// Queues the given node to be processed (if not already visited)
/// and if it is a function call, records that `_functionName` calls /// and if it is a function call, records that `_functionName` calls
/// `*_node->functionCall`. /// `*_node->functionCall`.
void recordReachabilityAndQueue(YulString _functionName, ControlFlowNode const* _node); void recordReachabilityAndQueue(FunctionDefinition const& _function, ControlFlowNode const* _node);
Dialect const& m_dialect; Dialect const& m_dialect;
ControlFlowBuilder m_cfgBuilder; ControlFlowBuilder m_cfgBuilder;
std::map<YulString, ControlFlowSideEffects> m_functionSideEffects; /// Function references, but only for calls to user-defined functions.
std::map<YulString, std::list<ControlFlowNode const*>> m_pendingNodes; std::map<FunctionCall const*, FunctionDefinition const*> m_functionReferences;
std::map<YulString, std::set<ControlFlowNode const*>> m_processedNodes; /// Side effects of user-defined functions, is being constructod.
/// `x` is in `m_functionCalls[y]` if a direct call to `x` is reachable inside `y` std::map<FunctionDefinition const*, ControlFlowSideEffects> m_functionSideEffects;
std::map<YulString, std::set<YulString>> m_functionCalls; /// 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;
}; };

View 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();
}

View File

@ -14,31 +14,35 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** // SPDX-License-Identifier: GPL-3.0
* AST walker that finds all function definitions and stores them into a map indexed by the function names.
*/
#pragma once #pragma once
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <map>
namespace solidity::yul 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: 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: private:
using ASTWalker::operator(); using ASTWalker::operator();
void operator()(FunctionDefinition const& _functionDefinition) override; void operator()(FunctionCall const& _functionCall) override;
std::map<YulString, FunctionDefinition const*> m_functionDefinitions; void operator()(Block const& _block) override;
std::map<FunctionCall const*, FunctionDefinition const*> m_functionReferences;
std::vector<std::map<YulString, FunctionDefinition const*>> m_scopes;
}; };
} }

View File

@ -176,9 +176,9 @@ StackSlot ControlFlowGraphBuilder::operator()(Expression const& _expression)
StackSlot ControlFlowGraphBuilder::operator()(FunctionCall const& _call) StackSlot ControlFlowGraphBuilder::operator()(FunctionCall const& _call)
{ {
CFG::Operation const& operation = visitFunctionCall(_call); Stack const& output = visitFunctionCall(_call);
yulAssert(operation.output.size() == 1, ""); yulAssert(output.size() == 1, "");
return operation.output.front(); return output.front();
} }
void ControlFlowGraphBuilder::operator()(VariableDeclaration const& _varDecl) void ControlFlowGraphBuilder::operator()(VariableDeclaration const& _varDecl)
@ -219,8 +219,8 @@ void ControlFlowGraphBuilder::operator()(ExpressionStatement const& _exprStmt)
yulAssert(m_currentBlock, ""); yulAssert(m_currentBlock, "");
std::visit(util::GenericVisitor{ std::visit(util::GenericVisitor{
[&](FunctionCall const& _call) { [&](FunctionCall const& _call) {
CFG::Operation const& operation = visitFunctionCall(_call); Stack const& output = visitFunctionCall(_call);
yulAssert(operation.output.empty(), ""); yulAssert(output.empty(), "");
}, },
[&](auto const&) { yulAssert(false, ""); } [&](auto const&) { yulAssert(false, ""); }
}, _exprStmt.expression); }, _exprStmt.expression);
@ -239,6 +239,9 @@ void ControlFlowGraphBuilder::operator()(ExpressionStatement const& _exprStmt)
void ControlFlowGraphBuilder::operator()(Block const& _block) void ControlFlowGraphBuilder::operator()(Block const& _block)
{ {
ScopedSaveAndRestore saveScope(m_scope, m_info.scopes.at(&_block).get()); 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) for (auto const& statement: _block.statements)
std::visit(*this, statement); 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)); Scope::Function& function = std::get<Scope::Function>(m_scope->identifiers.at(_function.name));
m_graph.functions.emplace_back(&function); 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), ""); yulAssert(m_info.scopes.at(&_function.body), "");
Scope* virtualFunctionScope = m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()).get(); Scope* virtualFunctionScope = m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()).get();
yulAssert(virtualFunctionScope, ""); 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.debugData,
function, function,
&m_graph.makeBlock(debugDataOf(_function.body)), &m_graph.makeBlock(debugDataOf(_function.body)),
@ -406,19 +424,11 @@ void ControlFlowGraphBuilder::operator()(FunctionDefinition const& _function)
_retVar.debugData _retVar.debugData
}; };
}) | ranges::to<vector> }) | ranges::to<vector>
})); })).second;
yulAssert(inserted, ""); 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};
} }
Stack const& ControlFlowGraphBuilder::visitFunctionCall(FunctionCall const& _call)
CFG::Operation const& ControlFlowGraphBuilder::visitFunctionCall(FunctionCall const& _call)
{ {
yulAssert(m_scope, ""); yulAssert(m_scope, "");
yulAssert(m_currentBlock, ""); yulAssert(m_currentBlock, "");
@ -439,7 +449,7 @@ CFG::Operation const& ControlFlowGraphBuilder::visitFunctionCall(FunctionCall co
}) | ranges::to<Stack>, }) | ranges::to<Stack>,
// operation // operation
move(builtinCall) move(builtinCall)
}); }).output;
} }
else else
{ {
@ -456,7 +466,7 @@ CFG::Operation const& ControlFlowGraphBuilder::visitFunctionCall(FunctionCall co
}) | ranges::to<Stack>, }) | ranges::to<Stack>,
// operation // operation
CFG::FunctionCall{_call.debugData, function, _call} CFG::FunctionCall{_call.debugData, function, _call}
}); }).output;
} }
} }
@ -464,9 +474,9 @@ Stack ControlFlowGraphBuilder::visitAssignmentRightHandSide(Expression const& _e
{ {
return std::visit(util::GenericVisitor{ return std::visit(util::GenericVisitor{
[&](FunctionCall const& _call) -> Stack { [&](FunctionCall const& _call) -> Stack {
CFG::Operation const& operation = visitFunctionCall(_call); Stack const& output = visitFunctionCall(_call);
yulAssert(_expectedSlotCount == operation.output.size(), ""); yulAssert(_expectedSlotCount == output.size(), "");
return operation.output; return output;
}, },
[&](auto const& _identifierOrLiteral) -> Stack { [&](auto const& _identifierOrLiteral) -> Stack {
yulAssert(_expectedSlotCount == 1, ""); yulAssert(_expectedSlotCount == 1, "");

View File

@ -57,7 +57,8 @@ private:
AsmAnalysisInfo const& _analysisInfo, AsmAnalysisInfo const& _analysisInfo,
Dialect const& _dialect 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); Stack visitAssignmentRightHandSide(Expression const& _expression, size_t _expectedSlotCount);
Scope::Function const& lookupFunction(YulString _name) const; Scope::Function const& lookupFunction(YulString _name) const;

View File

@ -23,6 +23,7 @@
#include <libyul/backends/evm/EVMCodeTransform.h> #include <libyul/backends/evm/EVMCodeTransform.h>
#include <libyul/backends/evm/EVMDialect.h> #include <libyul/backends/evm/EVMDialect.h>
#include <libyul/backends/evm/OptimizedEVMCodeTransform.h>
#include <libyul/Object.h> #include <libyul/Object.h>
#include <libyul/Exceptions.h> #include <libyul/Exceptions.h>
@ -62,6 +63,21 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize)
yulAssert(_object.analysisInfo, "No analysis info."); yulAssert(_object.analysisInfo, "No analysis info.");
yulAssert(_object.code, "No code."); yulAssert(_object.code, "No code.");
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, // 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. // which should be native to this part of the code.
CodeTransform transform{ CodeTransform transform{
@ -77,4 +93,5 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize)
transform(*_object.code); transform(*_object.code);
if (!transform.stackErrors().empty()) if (!transform.stackErrors().empty())
BOOST_THROW_EXCEPTION(transform.stackErrors().front()); BOOST_THROW_EXCEPTION(transform.stackErrors().front());
}
} }

View File

@ -44,7 +44,7 @@ vector<StackTooDeepError> OptimizedEVMCodeTransform::run(
Block const& _block, Block const& _block,
EVMDialect const& _dialect, EVMDialect const& _dialect,
BuiltinContext& _builtinContext, BuiltinContext& _builtinContext,
bool _useNamedLabelsForFunctions UseNamedLabels _useNamedLabelsForFunctions
) )
{ {
std::unique_ptr<CFG> dfg = ControlFlowGraphBuilder::build(_analysisInfo, _dialect, _block); std::unique_ptr<CFG> dfg = ControlFlowGraphBuilder::build(_analysisInfo, _dialect, _block);
@ -170,15 +170,35 @@ void OptimizedEVMCodeTransform::operator()(CFG::Assignment const& _assignment)
OptimizedEVMCodeTransform::OptimizedEVMCodeTransform( OptimizedEVMCodeTransform::OptimizedEVMCodeTransform(
AbstractAssembly& _assembly, AbstractAssembly& _assembly,
BuiltinContext& _builtinContext, BuiltinContext& _builtinContext,
bool _useNamedLabelsForFunctions, UseNamedLabels _useNamedLabelsForFunctions,
CFG const& _dfg, CFG const& _dfg,
StackLayout const& _stackLayout StackLayout const& _stackLayout
): ):
m_assembly(_assembly), m_assembly(_assembly),
m_builtinContext(_builtinContext), m_builtinContext(_builtinContext),
m_useNamedLabelsForFunctions(_useNamedLabelsForFunctions),
m_dfg(_dfg), 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) AbstractAssembly::LabelID OptimizedEVMCodeTransform::getFunctionLabel(Scope::Function const& _function)
{ {
CFG::FunctionInfo const& functionInfo = m_dfg.functionInfo.at(&_function); return m_functionLabels.at(&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];
} }
void OptimizedEVMCodeTransform::validateSlot(StackSlot const& _slot, Expression const& _expression) void OptimizedEVMCodeTransform::validateSlot(StackSlot const& _slot, Expression const& _expression)

View File

@ -43,13 +43,17 @@ struct StackLayout;
class OptimizedEVMCodeTransform class OptimizedEVMCodeTransform
{ {
public: 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( [[nodiscard]] static std::vector<StackTooDeepError> run(
AbstractAssembly& _assembly, AbstractAssembly& _assembly,
AsmAnalysisInfo& _analysisInfo, AsmAnalysisInfo& _analysisInfo,
Block const& _block, Block const& _block,
EVMDialect const& _dialect, EVMDialect const& _dialect,
BuiltinContext& _builtinContext, BuiltinContext& _builtinContext,
bool _useNamedLabelsForFunctions = false UseNamedLabels _useNamedLabelsForFunctions
); );
/// Generate code for the function call @a _call. Only public for using with std::visit. /// Generate code for the function call @a _call. Only public for using with std::visit.
@ -62,7 +66,7 @@ private:
OptimizedEVMCodeTransform( OptimizedEVMCodeTransform(
AbstractAssembly& _assembly, AbstractAssembly& _assembly,
BuiltinContext& _builtinContext, BuiltinContext& _builtinContext,
bool _useNamedLabelsForFunctions, UseNamedLabels _useNamedLabelsForFunctions,
CFG const& _dfg, CFG const& _dfg,
StackLayout const& _stackLayout StackLayout const& _stackLayout
); );
@ -70,6 +74,7 @@ private:
/// Assert that it is valid to transition from @a _currentStack to @a _desiredStack. /// 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. /// 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); static void assertLayoutCompatibility(Stack const& _currentStack, Stack const& _desiredStack);
/// @returns The label of the entry point of the given @a _function. /// @returns The label of the entry point of the given @a _function.
/// Creates and stores a new label, if none exists already. /// Creates and stores a new label, if none exists already.
AbstractAssembly::LabelID getFunctionLabel(Scope::Function const& _function); AbstractAssembly::LabelID getFunctionLabel(Scope::Function const& _function);
@ -94,13 +99,12 @@ private:
AbstractAssembly& m_assembly; AbstractAssembly& m_assembly;
BuiltinContext& m_builtinContext; BuiltinContext& m_builtinContext;
bool m_useNamedLabelsForFunctions = true;
CFG const& m_dfg; CFG const& m_dfg;
StackLayout const& m_stackLayout; StackLayout const& m_stackLayout;
Stack m_stack; Stack m_stack;
std::map<yul::FunctionCall const*, AbstractAssembly::LabelID> m_returnLabels; std::map<yul::FunctionCall const*, AbstractAssembly::LabelID> m_returnLabels;
std::map<CFG::BasicBlock const*, AbstractAssembly::LabelID> m_blockLabels; 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 /// Set of blocks already generated. If any of the contained blocks is ever jumped to, m_blockLabels should
/// contain a jump label for it. /// contain a jump label for it.
std::set<CFG::BasicBlock const*> m_generated; std::set<CFG::BasicBlock const*> m_generated;

View File

@ -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);
}
} }

View File

@ -19,6 +19,7 @@
#include <libyul/optimiser/Semantics.h> #include <libyul/optimiser/Semantics.h>
#include <libyul/AST.h> #include <libyul/AST.h>
#include <libyul/optimiser/NameCollector.h> #include <libyul/optimiser/NameCollector.h>
#include <libyul/ControlFlowSideEffectsCollector.h>
#include <libsolutil/CommonData.h> #include <libsolutil/CommonData.h>
using namespace std; using namespace std;
@ -26,6 +27,14 @@ using namespace solidity;
using namespace solidity::yul; using namespace solidity::yul;
using namespace solidity::util; 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) void ConditionalSimplifier::operator()(Switch& _switch)
{ {
visit(*_switch.expression); visit(*_switch.expression);
@ -65,7 +74,7 @@ void ConditionalSimplifier::operator()(Block& _block)
if ( if (
holds_alternative<Identifier>(*_if.condition) && holds_alternative<Identifier>(*_if.condition) &&
!_if.body.statements.empty() && !_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 TerminationFinder::ControlFlow::FlowOut
) )
{ {

View File

@ -44,7 +44,6 @@ namespace solidity::yul
* *
* Future features: * Future features:
* - allow replacements by "1" * - 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. * Works best with SSA form and if dead code removal has run before.
* *
@ -54,20 +53,21 @@ class ConditionalSimplifier: public ASTModifier
{ {
public: public:
static constexpr char const* name{"ConditionalSimplifier"}; static constexpr char const* name{"ConditionalSimplifier"};
static void run(OptimiserStepContext& _context, Block& _ast) static void run(OptimiserStepContext& _context, Block& _ast);
{
ConditionalSimplifier{_context.dialect}(_ast);
}
using ASTModifier::operator(); using ASTModifier::operator();
void operator()(Switch& _switch) override; void operator()(Switch& _switch) override;
void operator()(Block& _block) override; void operator()(Block& _block) override;
private: private:
explicit ConditionalSimplifier(Dialect const& _dialect): explicit ConditionalSimplifier(
m_dialect(_dialect) Dialect const& _dialect,
std::map<YulString, ControlFlowSideEffects> _sideEffects
):
m_dialect(_dialect), m_functionSideEffects(move(_sideEffects))
{} {}
Dialect const& m_dialect; Dialect const& m_dialect;
std::map<YulString, ControlFlowSideEffects> m_functionSideEffects;
}; };
} }

View File

@ -20,6 +20,7 @@
#include <libyul/AST.h> #include <libyul/AST.h>
#include <libyul/Utilities.h> #include <libyul/Utilities.h>
#include <libyul/optimiser/NameCollector.h> #include <libyul/optimiser/NameCollector.h>
#include <libyul/ControlFlowSideEffectsCollector.h>
#include <libsolutil/CommonData.h> #include <libsolutil/CommonData.h>
using namespace std; using namespace std;
@ -27,6 +28,14 @@ using namespace solidity;
using namespace solidity::yul; using namespace solidity::yul;
using namespace solidity::util; 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) void ConditionalUnsimplifier::operator()(Switch& _switch)
{ {
visit(*_switch.expression); visit(*_switch.expression);
@ -78,7 +87,7 @@ void ConditionalUnsimplifier::operator()(Block& _block)
YulString condition = std::get<Identifier>(*_if.condition).name; YulString condition = std::get<Identifier>(*_if.condition).name;
if ( if (
holds_alternative<Assignment>(_stmt2) && 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 TerminationFinder::ControlFlow::FlowOut
) )
{ {

View File

@ -33,20 +33,21 @@ class ConditionalUnsimplifier: public ASTModifier
{ {
public: public:
static constexpr char const* name{"ConditionalUnsimplifier"}; static constexpr char const* name{"ConditionalUnsimplifier"};
static void run(OptimiserStepContext& _context, Block& _ast) static void run(OptimiserStepContext& _context, Block& _ast);
{
ConditionalUnsimplifier{_context.dialect}(_ast);
}
using ASTModifier::operator(); using ASTModifier::operator();
void operator()(Switch& _switch) override; void operator()(Switch& _switch) override;
void operator()(Block& _block) override; void operator()(Block& _block) override;
private: private:
explicit ConditionalUnsimplifier(Dialect const& _dialect): explicit ConditionalUnsimplifier(
m_dialect(_dialect) Dialect const& _dialect,
std::map<YulString, ControlFlowSideEffects> const& _sideEffects
):
m_dialect(_dialect), m_functionSideEffects(_sideEffects)
{} {}
Dialect const& m_dialect; Dialect const& m_dialect;
std::map<YulString, ControlFlowSideEffects> const& m_functionSideEffects;
}; };
} }

View File

@ -45,9 +45,9 @@ DataFlowAnalyzer::DataFlowAnalyzer(
Dialect const& _dialect, Dialect const& _dialect,
map<YulString, SideEffects> _functionSideEffects map<YulString, SideEffects> _functionSideEffects
): ):
m_dialect(_dialect), m_dialect(_dialect),
m_functionSideEffects(std::move(_functionSideEffects)), m_functionSideEffects(std::move(_functionSideEffects)),
m_knowledgeBase(_dialect, m_value) m_knowledgeBase(_dialect, m_value)
{ {
if (auto const* builtin = _dialect.memoryStoreFunction(YulString{})) if (auto const* builtin = _dialect.memoryStoreFunction(YulString{}))
m_storeFunctionName[static_cast<unsigned>(StoreLoadLocation::Memory)] = builtin->name; m_storeFunctionName[static_cast<unsigned>(StoreLoadLocation::Memory)] = builtin->name;
@ -123,9 +123,7 @@ void DataFlowAnalyzer::operator()(If& _if)
joinKnowledge(storage, memory); joinKnowledge(storage, memory);
Assignments assignments; clearValues(assignedVariableNames(_if.body));
assignments(_if.body);
clearValues(assignments.names());
} }
void DataFlowAnalyzer::operator()(Switch& _switch) void DataFlowAnalyzer::operator()(Switch& _switch)
@ -140,11 +138,10 @@ void DataFlowAnalyzer::operator()(Switch& _switch)
(*this)(_case.body); (*this)(_case.body);
joinKnowledge(storage, memory); joinKnowledge(storage, memory);
Assignments assignments; set<YulString> variables = assignedVariableNames(_case.body);
assignments(_case.body); assignedVariables += variables;
assignedVariables += assignments.names();
// This is a little too destructive, we could retain the old values. // This is a little too destructive, we could retain the old values.
clearValues(assignments.names()); clearValues(variables);
clearKnowledgeIfInvalidated(_case.body); clearKnowledgeIfInvalidated(_case.body);
} }
for (auto& _case: _switch.cases) for (auto& _case: _switch.cases)
@ -190,10 +187,9 @@ void DataFlowAnalyzer::operator()(ForLoop& _for)
AssignmentsSinceContinue assignmentsSinceCont; AssignmentsSinceContinue assignmentsSinceCont;
assignmentsSinceCont(_for.body); assignmentsSinceCont(_for.body);
Assignments assignments; set<YulString> assignedVariables =
assignments(_for.body); assignedVariableNames(_for.body) + assignedVariableNames(_for.post);
assignments(_for.post); clearValues(assignedVariables);
clearValues(assignments.names());
// break/continue are tricky for storage and thus we almost always clear here. // break/continue are tricky for storage and thus we almost always clear here.
clearKnowledgeIfInvalidated(*_for.condition); clearKnowledgeIfInvalidated(*_for.condition);
@ -205,7 +201,7 @@ void DataFlowAnalyzer::operator()(ForLoop& _for)
clearValues(assignmentsSinceCont.names()); clearValues(assignmentsSinceCont.names());
clearKnowledgeIfInvalidated(_for.body); clearKnowledgeIfInvalidated(_for.body);
(*this)(_for.post); (*this)(_for.post);
clearValues(assignments.names()); clearValues(assignedVariables);
clearKnowledgeIfInvalidated(*_for.condition); clearKnowledgeIfInvalidated(*_for.condition);
clearKnowledgeIfInvalidated(_for.post); clearKnowledgeIfInvalidated(_for.post);
clearKnowledgeIfInvalidated(_for.body); clearKnowledgeIfInvalidated(_for.body);

View File

@ -22,6 +22,7 @@
#include <libyul/optimiser/DeadCodeEliminator.h> #include <libyul/optimiser/DeadCodeEliminator.h>
#include <libyul/optimiser/Semantics.h> #include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/OptimiserStep.h> #include <libyul/optimiser/OptimiserStep.h>
#include <libyul/ControlFlowSideEffectsCollector.h>
#include <libyul/AST.h> #include <libyul/AST.h>
#include <libevmasm/SemanticInformation.h> #include <libevmasm/SemanticInformation.h>
@ -36,7 +37,11 @@ using namespace solidity::yul;
void DeadCodeEliminator::run(OptimiserStepContext& _context, Block& _ast) 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) void DeadCodeEliminator::operator()(ForLoop& _for)
@ -49,7 +54,7 @@ void DeadCodeEliminator::operator()(Block& _block)
{ {
TerminationFinder::ControlFlow controlFlowChange; TerminationFinder::ControlFlow controlFlowChange;
size_t index; 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. // Erase everything after the terminating statement that is not a function definition.
if (controlFlowChange != TerminationFinder::ControlFlow::FlowOut && index != std::numeric_limits<size_t>::max()) if (controlFlowChange != TerminationFinder::ControlFlow::FlowOut && index != std::numeric_limits<size_t>::max())

View File

@ -23,6 +23,7 @@
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <libyul/YulString.h> #include <libyul/YulString.h>
#include <libyul/ControlFlowSideEffects.h>
#include <map> #include <map>
#include <set> #include <set>
@ -36,7 +37,9 @@ struct OptimiserStepContext;
* Optimisation stage that removes unreachable code * Optimisation stage that removes unreachable code
* *
* Unreachable code is any code within a block which is preceded by a * 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 * Function definitions are retained as they might be called by earlier
* code and thus are considered reachable. * code and thus are considered reachable.
@ -57,9 +60,13 @@ public:
void operator()(Block& _block) override; void operator()(Block& _block) override;
private: 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; Dialect const& m_dialect;
std::map<YulString, ControlFlowSideEffects> m_functionSideEffects;
}; };
} }

View File

@ -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);
}

View File

@ -78,13 +78,6 @@ map<YulString, size_t> ReferencesCounter::countReferences(Expression const& _exp
return counter.references(); 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) void AssignmentsSinceContinue::operator()(ForLoop const& _forLoop)
{ {
m_forLoopDepth++; m_forLoopDepth++;
@ -109,3 +102,22 @@ void AssignmentsSinceContinue::operator()(FunctionDefinition const&)
{ {
yulAssert(false, ""); 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;
}

View File

@ -91,20 +91,6 @@ private:
std::map<YulString, size_t> m_references; 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. * Collects all names from a given continue statement on onwards.
* *
@ -130,4 +116,12 @@ private:
std::set<YulString> m_names; 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);
} }

View File

@ -196,12 +196,7 @@ void IntroduceControlFlowSSA::operator()(ForLoop& _for)
{ {
yulAssert(_for.pre.statements.empty(), "For loop init rewriter not run."); yulAssert(_for.pre.statements.empty(), "For loop init rewriter not run.");
Assignments assignments; for (auto const& var: assignedVariableNames(_for.body) + assignedVariableNames(_for.post))
assignments(_for.body);
assignments(_for.post);
for (auto const& var: assignments.names())
if (m_variablesInScope.count(var)) if (m_variablesInScope.count(var))
m_variablesToReassign.insert(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."); yulAssert(_for.pre.statements.empty(), "For loop init rewriter not run.");
Assignments assignments; for (auto const& var: assignedVariableNames(_for.body) + assignedVariableNames(_for.post))
assignments(_for.body);
assignments(_for.post);
for (auto const& var: assignments.names())
m_currentVariableValues.erase(var); m_currentVariableValues.erase(var);
visit(*_for.condition); visit(*_for.condition);
@ -389,11 +380,10 @@ void PropagateValues::operator()(Block& _block)
void SSATransform::run(OptimiserStepContext& _context, Block& _ast) void SSATransform::run(OptimiserStepContext& _context, Block& _ast)
{ {
TypeInfo typeInfo(_context.dialect, _ast); TypeInfo typeInfo(_context.dialect, _ast);
Assignments assignments; set<YulString> assignedVariables = assignedVariableNames(_ast);
assignments(_ast); IntroduceSSA{_context.dispenser, assignedVariables, typeInfo}(_ast);
IntroduceSSA{_context.dispenser, assignments.names(), typeInfo}(_ast); IntroduceControlFlowSSA{_context.dispenser, assignedVariables, typeInfo}(_ast);
IntroduceControlFlowSSA{_context.dispenser, assignments.names(), typeInfo}(_ast); PropagateValues{assignedVariables}(_ast);
PropagateValues{assignments.names()}(_ast);
} }

View File

@ -182,8 +182,19 @@ pair<TerminationFinder::ControlFlow, size_t> TerminationFinder::firstUncondition
TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement const& _statement) TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement const& _statement)
{ {
if ( 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) && holds_alternative<ExpressionStatement>(_statement) &&
isTerminatingBuiltin(std::get<ExpressionStatement>(_statement)) containsNonContinuingFunctionCall(std::get<ExpressionStatement>(_statement).expression)
) )
return ControlFlow::Terminate; return ControlFlow::Terminate;
else if (holds_alternative<Break>(_statement)) else if (holds_alternative<Break>(_statement))
@ -196,10 +207,18 @@ TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement cons
return ControlFlow::FlowOut; return ControlFlow::FlowOut;
} }
bool TerminationFinder::isTerminatingBuiltin(ExpressionStatement const& _exprStmnt) bool TerminationFinder::containsNonContinuingFunctionCall(Expression const& _expr)
{ {
if (holds_alternative<FunctionCall>(_exprStmnt.expression)) if (auto functionCall = std::get_if<FunctionCall>(&_expr))
if (auto instruction = toEVMInstruction(m_dialect, std::get<FunctionCall>(_exprStmnt.expression).functionName.name)) {
return evmasm::SemanticInformation::terminatesControlFlow(*instruction); 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; return false;
} }

View File

@ -205,22 +205,31 @@ private:
std::set<YulString> m_variableReferences; std::set<YulString> m_variableReferences;
}; };
struct ControlFlowSideEffects;
/** /**
* Helper class to find "irregular" control flow. * 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 class TerminationFinder
{ {
public: 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 }; 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 /// @returns the index of the first statement in the provided sequence
/// that is an unconditional ``break``, ``continue``, ``leave`` or a /// 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, /// If control flow can continue at the end of the list,
/// returns `FlowOut` and ``size_t(-1)``. /// returns `FlowOut` and ``size_t(-1)``.
/// The function might return ``FlowOut`` even though control /// The function might return ``FlowOut`` even though control
@ -233,13 +242,14 @@ public:
/// This function could return FlowOut even if control flow never continues. /// This function could return FlowOut even if control flow never continues.
ControlFlow controlFlowKind(Statement const& _statement); ControlFlow controlFlowKind(Statement const& _statement);
/// @returns true if the expression statement is a direct /// @returns true if the expression contains a
/// call to a builtin terminating function like /// call to a terminating function, i.e. a function that does not have
/// ``stop``, ``revert`` or ``return``. /// a regular "flow out" control-flow (it might also be recursive).
bool isTerminatingBuiltin(ExpressionStatement const& _exprStmnt); bool containsNonContinuingFunctionCall(Expression const& _expr);
private: private:
Dialect const& m_dialect; Dialect const& m_dialect;
std::map<YulString, ControlFlowSideEffects> const* m_functionSideEffects;
}; };
} }

View File

@ -27,6 +27,13 @@
#include <libyul/optimiser/Metrics.h> #include <libyul/optimiser/Metrics.h>
#include <libyul/optimiser/Semantics.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/CompilabilityChecker.h>
#include <libyul/AST.h> #include <libyul/AST.h>
@ -162,6 +169,50 @@ void eliminateVariables(
UnusedPruner::runUntilStabilised(_dialect, _node, _allowMSizeOptimization); 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( bool StackCompressor::run(
@ -176,7 +227,34 @@ bool StackCompressor::run(
_object.code->statements.size() > 0 && holds_alternative<Block>(_object.code->statements.at(0)), _object.code->statements.size() > 0 && holds_alternative<Block>(_object.code->statements.at(0)),
"Need to run the function grouper before the stack compressor." "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); bool allowMSizeOptimzation = !MSizeFinder::containsMSize(_dialect, *_object.code);
if (usesOptimizedCodeGenerator)
{
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 (
auto stackTooDeepErrors = StackLayoutGenerator::reportStackTooDeep(*cfg, fun.name);
!stackTooDeepErrors.empty()
)
eliminateVariables(_dialect, fun.body, stackTooDeepErrors, allowMSizeOptimzation);
}
}
else
for (size_t iterations = 0; iterations < _maxIterations; iterations++) for (size_t iterations = 0; iterations < _maxIterations; iterations++)
{ {
map<YulString, int> stackSurplus = CompilabilityChecker(_dialect, _object, _optimizeStackAllocation).stackDeficit; map<YulString, int> stackSurplus = CompilabilityChecker(_dialect, _object, _optimizeStackAllocation).stackDeficit;

View File

@ -18,17 +18,21 @@
#include <libyul/optimiser/StackLimitEvader.h> #include <libyul/optimiser/StackLimitEvader.h>
#include <libyul/optimiser/CallGraphGenerator.h> #include <libyul/optimiser/CallGraphGenerator.h>
#include <libyul/optimiser/FunctionCallFinder.h> #include <libyul/optimiser/FunctionCallFinder.h>
#include <libyul/optimiser/FunctionDefinitionCollector.h>
#include <libyul/optimiser/NameDispenser.h> #include <libyul/optimiser/NameDispenser.h>
#include <libyul/optimiser/NameCollector.h>
#include <libyul/optimiser/StackToMemoryMover.h> #include <libyul/optimiser/StackToMemoryMover.h>
#include <libyul/backends/evm/ControlFlowGraphBuilder.h>
#include <libyul/backends/evm/EVMDialect.h> #include <libyul/backends/evm/EVMDialect.h>
#include <libyul/AsmAnalysis.h>
#include <libyul/AST.h> #include <libyul/AST.h>
#include <libyul/CompilabilityChecker.h>
#include <libyul/Exceptions.h> #include <libyul/Exceptions.h>
#include <libyul/Object.h> #include <libyul/Object.h>
#include <libyul/Utilities.h> #include <libyul/Utilities.h>
#include <libsolutil/Algorithms.h> #include <libsolutil/Algorithms.h>
#include <libsolutil/CommonData.h> #include <libsolutil/CommonData.h>
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/concat.hpp> #include <range/v3/view/concat.hpp>
#include <range/v3/view/take.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( void StackLimitEvader::run(
OptimiserStepContext& _context, OptimiserStepContext& _context,
Object& _object, Object& _object,
@ -150,7 +193,7 @@ void StackLimitEvader::run(
if (_unreachableVariables.count(function)) if (_unreachableVariables.count(function))
return; return;
map<YulString, FunctionDefinition const*> functionDefinitions = FunctionDefinitionCollector::run(*_object.code); map<YulString, FunctionDefinition const*> functionDefinitions = allFunctionDefinitions(*_object.code);
MemoryOffsetAllocator memoryOffsetAllocator{_unreachableVariables, callGraph.functionCalls, functionDefinitions}; MemoryOffsetAllocator memoryOffsetAllocator{_unreachableVariables, callGraph.functionCalls, functionDefinitions};
uint64_t requiredSlots = memoryOffsetAllocator.run(); uint64_t requiredSlots = memoryOffsetAllocator.run();

View File

@ -22,6 +22,7 @@
#pragma once #pragma once
#include <libyul/optimiser/OptimiserStep.h> #include <libyul/optimiser/OptimiserStep.h>
#include <libyul/backends/evm/StackLayoutGenerator.h>
namespace solidity::yul namespace solidity::yul
{ {
@ -61,6 +62,25 @@ public:
Object& _object, Object& _object,
std::map<YulString, std::set<YulString>> const& _unreachableVariables 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
);
}; };
} }

View File

@ -15,7 +15,7 @@
along with solidity. If not, see <http://www.gnu.org/licenses/>. along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <libyul/optimiser/StackToMemoryMover.h> #include <libyul/optimiser/StackToMemoryMover.h>
#include <libyul/optimiser/FunctionDefinitionCollector.h> #include <libyul/optimiser/NameCollector.h>
#include <libyul/optimiser/NameDispenser.h> #include <libyul/optimiser/NameDispenser.h>
#include <libyul/backends/evm/EVMDialect.h> #include <libyul/backends/evm/EVMDialect.h>
@ -87,7 +87,7 @@ void StackToMemoryMover::run(
_context, _context,
memoryOffsetTracker, memoryOffsetTracker,
util::applyMap( util::applyMap(
FunctionDefinitionCollector::run(_block), allFunctionDefinitions(_block),
util::mapTuple([](YulString _name, FunctionDefinition const* _funDef) { util::mapTuple([](YulString _name, FunctionDefinition const* _funDef) {
return make_pair(_name, _funDef->returnVariables); return make_pair(_name, _funDef->returnVariables);
}), }),

View File

@ -95,6 +95,12 @@ void OptimiserSuite::run(
set<YulString> const& _externallyUsedIdentifiers 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; set<YulString> reservedIdentifiers = _externallyUsedIdentifiers;
reservedIdentifiers += _dialect.fixedFunctionNames(); reservedIdentifiers += _dialect.fixedFunctionNames();
@ -105,7 +111,10 @@ void OptimiserSuite::run(
)(*_object.code)); )(*_object.code));
Block& ast = *_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 // Some steps depend on properties ensured by FunctionHoister, BlockFlattener, FunctionGrouper and
// ForLoopInitRewriter. Run them first to be able to run arbitrary sequences safely. // ForLoopInitRewriter. Run them first to be able to run arbitrary sequences safely.
@ -121,6 +130,7 @@ void OptimiserSuite::run(
// We ignore the return value because we will get a much better error // We ignore the return value because we will get a much better error
// message once we perform code generation. // message once we perform code generation.
if (!usesOptimizedCodeGenerator)
StackCompressor::run( StackCompressor::run(
_dialect, _dialect,
_object, _object,
@ -129,16 +139,23 @@ void OptimiserSuite::run(
); );
suite.runSequence("fDnTOc g", ast); suite.runSequence("fDnTOc g", ast);
if (EVMDialect const* dialect = dynamic_cast<EVMDialect const*>(&_dialect)) if (evmDialect)
{ {
yulAssert(_meter, ""); yulAssert(_meter, "");
ConstantOptimiser{*dialect, *_meter}(ast); ConstantOptimiser{*evmDialect, *_meter}(ast);
if (dialect->providesObjectAccess() && _optimizeStackAllocation) if (usesOptimizedCodeGenerator)
StackLimitEvader::run(suite.m_context, _object, CompilabilityChecker{ {
StackCompressor::run(
_dialect, _dialect,
_object, _object,
_optimizeStackAllocation _optimizeStackAllocation,
}.unreachableVariables); 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)) else if (dynamic_cast<WasmDialect const*>(&_dialect))
{ {
@ -148,7 +165,7 @@ void OptimiserSuite::run(
ast.statements.erase(ast.statements.begin()); ast.statements.erase(ast.statements.begin());
} }
suite.m_dispenser.reset(ast); dispenser.reset(ast);
NameSimplifier::run(suite.m_context, ast); NameSimplifier::run(suite.m_context, ast);
VarNameCleaner::run(suite.m_context, ast); VarNameCleaner::run(suite.m_context, ast);

View File

@ -59,6 +59,8 @@ public:
PrintStep, PrintStep,
PrintChanges PrintChanges
}; };
OptimiserSuite(OptimiserStepContext& _context, Debug _debug = Debug::None): m_context(_context), m_debug(_debug) {}
/// The value nullopt for `_expectedExecutionsPerDeployment` represents creation code. /// The value nullopt for `_expectedExecutionsPerDeployment` represents creation code.
static void run( static void run(
Dialect const& _dialect, Dialect const& _dialect,
@ -82,20 +84,7 @@ public:
static std::map<char, std::string> const& stepAbbreviationToNameMap(); static std::map<char, std::string> const& stepAbbreviationToNameMap();
private: private:
OptimiserSuite( OptimiserStepContext& m_context;
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;
Debug m_debug; Debug m_debug;
}; };

View File

@ -6,8 +6,7 @@ MAINTAINER chriseth <chris@ethereum.org>
WORKDIR /solidity WORKDIR /solidity
# Build dependencies # Build dependencies
ADD /scripts/install_deps.sh /solidity/scripts/install_deps.sh RUN apk update && apk add boost-dev boost-static build-base cmake git
RUN ./scripts/install_deps.sh
#Copy working directory on travis to the image #Copy working directory on travis to the image
COPY / $WORKDIR COPY / $WORKDIR

View File

@ -34,7 +34,7 @@ else
BUILD_DIR="$1" BUILD_DIR="$1"
fi fi
# solbuildpackpusher/solidity-buildpack-deps:emscripten-6 # solbuildpackpusher/solidity-buildpack-deps:emscripten-7
docker run -v "$(pwd):/root/project" -w /root/project \ 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" ./scripts/ci/build_emscripten.sh "$BUILD_DIR"

View File

@ -35,7 +35,7 @@ then
exit 1 exit 1
fi fi
function preparedGrep() function preparedGrep
{ {
git grep -nIE "$1" -- '*.h' '*.cpp' | grep -v "${EXCLUDE_FILES_JOINED}" git grep -nIE "$1" -- '*.h' '*.cpp' | grep -v "${EXCLUDE_FILES_JOINED}"
return $? return $?

View File

@ -68,10 +68,6 @@ emcmake cmake \
-DTESTS=0 \ -DTESTS=0 \
.. ..
make soljson 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 .. cd ..
mkdir -p upload mkdir -p upload

View File

@ -5,7 +5,7 @@ ROOTDIR="/root/project"
BUILDDIR="${ROOTDIR}/build" BUILDDIR="${ROOTDIR}/build"
mkdir -p "${BUILDDIR}" && mkdir -p "$BUILDDIR/deps" mkdir -p "${BUILDDIR}" && mkdir -p "$BUILDDIR/deps"
generate_protobuf_bindings() function generate_protobuf_bindings
{ {
cd "${ROOTDIR}"/test/tools/ossfuzz cd "${ROOTDIR}"/test/tools/ossfuzz
# Generate protobuf C++ bindings # Generate protobuf C++ bindings
@ -15,7 +15,7 @@ generate_protobuf_bindings()
done done
} }
build_fuzzers() function build_fuzzers
{ {
cd "${BUILDDIR}" cd "${BUILDDIR}"
cmake .. -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-Release}" \ cmake .. -DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:-Release}" \

View File

@ -1,11 +1,13 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -e set -e
function error() { function error
{
echo >&2 "ERROR: ${1} Aborting." && false echo >&2 "ERROR: ${1} Aborting." && false
} }
function warning() { function warning
{
echo >&2 "WARNING: ${1}" echo >&2 "WARNING: ${1}"
} }

View File

@ -29,15 +29,15 @@ _initial_work_dir=$(pwd)
if [ "$CIRCLECI" ] if [ "$CIRCLECI" ]
then then
export TERM="${TERM:-xterm}" export TERM="${TERM:-xterm}"
function printTask() { echo "$(tput bold)$(tput setaf 2)$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 printError { >&2 echo "$(tput setaf 1)$1$(tput setaf 7)"; }
function printWarning() { >&2 echo "$(tput setaf 11)$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 printLog { echo "$(tput setaf 3)$1$(tput setaf 7)"; }
else else
function printTask() { echo "$(tput bold)$(tput setaf 2)$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 printError { >&2 echo "$(tput setaf 1)$1$(tput sgr0)"; }
function printWarning() { >&2 echo "$(tput setaf 11)$1$(tput sgr0)"; } function printWarning { >&2 echo "$(tput setaf 11)$1$(tput sgr0)"; }
function printLog() { echo "$(tput setaf 3)$1$(tput sgr0)"; } function printLog { echo "$(tput setaf 3)$1$(tput sgr0)"; }
fi fi
function printStackTrace function printStackTrace
@ -78,7 +78,7 @@ function printStackTrace
done done
} }
function fail() function fail
{ {
printError "$@" printError "$@"
@ -86,7 +86,7 @@ function fail()
return 1 return 1
} }
function assertFail() function assertFail
{ {
printError "" printError ""
(( $# == 0 )) && printError "Assertion failed." (( $# == 0 )) && printError "Assertion failed."
@ -97,7 +97,7 @@ function assertFail()
exit 2 exit 2
} }
function msg_on_error() function msg_on_error
{ {
local error_message local error_message
local no_stdout=false local no_stdout=false
@ -171,7 +171,7 @@ function msg_on_error()
fi fi
} }
safe_kill() function safe_kill
{ {
local PID=${1} local PID=${1}
local NAME=${2:-${1}} local NAME=${2:-${1}}

View File

@ -22,7 +22,8 @@
YULARGS=(--strict-assembly) YULARGS=(--strict-assembly)
FULLARGS=(--optimize --combined-json "abi,asm,ast,bin,bin-runtime,devdoc,hashes,metadata,opcodes,srcmap,srcmap-runtime,userdoc") 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") 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 expected_exit_code=0
local expect_output='none' local expect_output='none'

View File

@ -26,10 +26,14 @@
# contains a Makefile in the docker/ subdirectory that can be used to create the # contains a Makefile in the docker/ subdirectory that can be used to create the
# required base image using: # required base image using:
# #
# make version=2.0.12 build # make version=2.0.33 build
# #
FROM emscripten/emsdk:2.0.12 AS base # Note that emscripten is supposed to automatically install to $(em-config CACHE)/sysroot, but
LABEL version="6" # 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 ADD emscripten.jam /usr/src
RUN set -ex; \ RUN set -ex; \
@ -39,8 +43,8 @@ RUN set -ex; \
mkdir build; \ mkdir build; \
cd build; \ cd build; \
emcmake cmake \ emcmake cmake \
-DCMAKE_INSTALL_PREFIX=$(em-config CACHE)/sysroot/usr \
-DCMAKE_BUILD_TYPE=MinSizeRel \ -DCMAKE_BUILD_TYPE=MinSizeRel \
-DCMAKE_INSTALL_PREFIX=/emsdk/upstream/emscripten/system \
-DZ3_BUILD_LIBZ3_SHARED=OFF \ -DZ3_BUILD_LIBZ3_SHARED=OFF \
-DZ3_ENABLE_EXAMPLE_TARGETS=OFF \ -DZ3_ENABLE_EXAMPLE_TARGETS=OFF \
-DZ3_BUILD_TEST_EXECUTABLES=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 \ ./b2 toolset=emscripten link=static variant=release threading=single runtime-link=static \
--with-system --with-filesystem --with-test --with-program_options \ --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" \ 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 rm -r /usr/src/boost_1_75_0

View File

@ -28,7 +28,7 @@ else
date -u +"nightly.%Y.%-m.%-d" > prerelease.txt date -u +"nightly.%Y.%-m.%-d" > prerelease.txt
fi fi
tag_and_push() function tag_and_push
{ {
docker tag "$image:$1" "$image:$2" docker tag "$image:$1" "$image:$2"
docker push "$image:$2" docker push "$image:$2"

View File

@ -36,7 +36,7 @@ source "${REPO_ROOT}/scripts/common_cmdline.sh"
developmentVersion=$("$REPO_ROOT/scripts/get_version.sh") developmentVersion=$("$REPO_ROOT/scripts/get_version.sh")
function versionGreater() function versionGreater
{ {
v1=$1 v1=$1
v2=$2 v2=$2
@ -58,7 +58,7 @@ function versionGreater()
return 1 return 1
} }
function versionEqual() function versionEqual
{ {
if [[ "$1" == "$2" ]] if [[ "$1" == "$2" ]]
then then
@ -67,7 +67,7 @@ function versionEqual()
return 1 return 1
} }
function getAllAvailableVersions() function getAllAvailableVersions
{ {
allVersions=() allVersions=()
local allListedVersions local allListedVersions
@ -85,7 +85,7 @@ function getAllAvailableVersions()
done done
} }
function findMinimalVersion() function findMinimalVersion
{ {
local f=$1 local f=$1
local greater=false local greater=false

View File

@ -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

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -134,7 +134,7 @@ def extract_and_write(path, language):
if __name__ == '__main__': if __name__ == '__main__':
script_description = ( script_description = (
"Reads Solidity, C++ or RST source files and extracts compilable solidity and yul code blocks from them. " "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) parser = ArgumentParser(description=script_description)

View File

@ -50,14 +50,14 @@
# FIXME: Can't use set -u because the old Bash on macOS treats empty arrays as unbound variables # FIXME: Can't use set -u because the old Bash on macOS treats empty arrays as unbound variables
set -eo pipefail set -eo pipefail
die() function die
{ {
# shellcheck disable=SC2059 # shellcheck disable=SC2059
>&2 printf "ERROR: $1\n" "${@:2}" >&2 printf "ERROR: $1\n" "${@:2}"
exit 1 exit 1
} }
get_reported_solc_version() function get_reported_solc_version
{ {
local solc_binary="$1" 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' 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 reported_version="$1"
local expected_version_and_commit="$2" local expected_version_and_commit="$2"

View File

@ -8,7 +8,8 @@ BOOST_OPTIONS=()
SOLTEST_OPTIONS=() SOLTEST_OPTIONS=()
SOLIDITY_BUILD_DIR=${SOLIDITY_BUILD_DIR:-${REPO_ROOT}/build} SOLIDITY_BUILD_DIR=${SOLIDITY_BUILD_DIR:-${REPO_ROOT}/build}
usage() { function usage
{
echo 2>&1 " echo 2>&1 "
Usage: $0 [options] [soltest-options] Usage: $0 [options] [soltest-options]
Runs BOOST C++ unit test program, soltest. Runs BOOST C++ unit test program, soltest.

View File

@ -20,7 +20,7 @@ SGR_BLUE="\033[34m"
vt_cursor_up() { echo -ne "\033[A"; } vt_cursor_up() { echo -ne "\033[A"; }
vt_cursor_begin_of_line() { echo -ne "\r"; } vt_cursor_begin_of_line() { echo -ne "\r"; }
download_antlr4() function download_antlr4
{ {
if [[ ! -e "$ANTLR_JAR" ]] if [[ ! -e "$ANTLR_JAR" ]]
then then
@ -28,7 +28,7 @@ download_antlr4()
fi fi
} }
prepare_workdir() function prepare_workdir
{ {
mkdir -p "${ROOT_DIR}/build/deps" mkdir -p "${ROOT_DIR}/build/deps"
mkdir -p "${WORKDIR}" mkdir -p "${WORKDIR}"
@ -51,7 +51,7 @@ javac -classpath "${ANTLR_JAR}" "${WORKDIR}/src/"*.java -d "${WORKDIR}/target/"
# Run tests # Run tests
failed_count=0 failed_count=0
test_file() function test_file
{ {
local SOL_FILE local SOL_FILE
SOL_FILE="$(${READLINK} -m "${1}")" SOL_FILE="$(${READLINK} -m "${1}")"

View File

@ -38,7 +38,8 @@ source "${REPO_ROOT}/scripts/common.sh"
WORKDIR=$(mktemp -d) WORKDIR=$(mktemp -d)
CMDLINE_PID= CMDLINE_PID=
cleanup() { function cleanup
{
# ensure failing commands don't cause termination during cleanup (especially within safe_kill) # ensure failing commands don't cause termination during cleanup (especially within safe_kill)
set +e set +e

View File

@ -38,7 +38,8 @@ ORANGE='\033[0;33m'
CYAN='\033[0;36m' CYAN='\033[0;36m'
RESET='\033[0m' RESET='\033[0m'
function generate_bytecode_report() { function generate_bytecode_report
{
rm -rf /tmp/report.txt rm -rf /tmp/report.txt
local EXIT_STATUS local EXIT_STATUS
@ -74,7 +75,8 @@ function generate_bytecode_report() {
echo -e "${RED}FAILURE${RESET}" echo -e "${RED}FAILURE${RESET}"
fi fi
} }
function clean_git_checkout() { function clean_git_checkout
{
git submodule deinit --all -q git submodule deinit --all -q
git reset --hard HEAD --quiet git reset --hard HEAD --quiet
git clean -f -d -x --quiet git clean -f -d -x --quiet
@ -82,7 +84,8 @@ function clean_git_checkout() {
git submodule init -q git submodule init -q
git submodule update -q git submodule update -q
} }
function process_tag() { function process_tag
{
local TAG=$1 local TAG=$1
cd /src cd /src
# Checkout the historic commit instead of the tag directly. # Checkout the historic commit instead of the tag directly.

View File

@ -83,7 +83,8 @@ for arg in "$@"; do
esac esac
done done
show_output_if() { function show_output_if
{
local VAR=${1} local VAR=${1}
if [ -n "${VAR}" ]; then if [ -n "${VAR}" ]; then
echo "${SOL_FILE}" echo "${SOL_FILE}"
@ -102,7 +103,8 @@ if [ ! -f "${SOLC}" ]; then
exit 1 exit 1
fi fi
test_file() { function test_file
{
local SOL_FILE local SOL_FILE
local OUTPUT local OUTPUT
SOL_FILE=${1} SOL_FILE=${1}

View File

@ -23,6 +23,7 @@
*/ */
#include <solc/CommandLineInterface.h> #include <solc/CommandLineInterface.h>
#include "license.h"
#include "solidity/BuildInfo.h" #include "solidity/BuildInfo.h"
#include <libsolidity/interface/Version.h> #include <libsolidity/interface/Version.h>
@ -405,6 +406,13 @@ bool CommandLineInterface::readInputFiles()
{ {
solAssert(!m_standardJsonInput.has_value(), ""); 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); m_fileReader.setBasePath(m_options.input.basePath);
if (m_fileReader.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) bool CommandLineInterface::parseArguments(int _argc, char const* const* _argv)
{ {
CommandLineParser parser(sout(/* _markAsUsed */ false), serr(/* _markAsUsed */ false)); CommandLineParser parser(serr(/* _markAsUsed */ false));
bool success = parser.parse(_argc, _argv, isatty(fileno(stdin)));
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) if (!success)
return false; return false;
m_hasOutput = m_hasOutput || parser.hasOutput(); m_hasOutput = m_hasOutput || parser.hasOutput();
@ -587,6 +605,15 @@ bool CommandLineInterface::processInput()
{ {
switch (m_options.input.mode) 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: case InputMode::StandardJson:
{ {
solAssert(m_standardJsonInput.has_value(), ""); solAssert(m_standardJsonInput.has_value(), "");
@ -594,21 +621,38 @@ bool CommandLineInterface::processInput()
StandardCompiler compiler(m_fileReader.reader(), m_options.formatting.json); StandardCompiler compiler(m_fileReader.reader(), m_options.formatting.json);
sout() << compiler.compile(move(m_standardJsonInput.value())) << endl; sout() << compiler.compile(move(m_standardJsonInput.value())) << endl;
m_standardJsonInput.reset(); m_standardJsonInput.reset();
return true; break;
} }
case InputMode::Assembler: case InputMode::Assembler:
{ if (!assemble(m_options.assembly.inputLanguage, m_options.assembly.targetMachine))
return assemble(m_options.assembly.inputLanguage, m_options.assembly.targetMachine); return false;
} break;
case InputMode::Linker: case InputMode::Linker:
return link(); if (!link())
return false;
writeLinkedFiles();
break;
case InputMode::Compiler: case InputMode::Compiler:
case InputMode::CompilerWithASTImport: case InputMode::CompilerWithASTImport:
return compile(); if (!compile())
return false;
outputCompilationResults();
} }
solAssert(false, ""); return !m_outputFailed;
return false; }
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() 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() bool CommandLineInterface::link()
{ {
solAssert(m_options.input.mode == InputMode::Linker, ""); solAssert(m_options.input.mode == InputMode::Linker, "");
@ -1013,35 +1042,53 @@ bool CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul:
yul::AssemblyStack& stack = assemblyStacks[src.first]; yul::AssemblyStack& stack = assemblyStacks[src.first];
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() << endl << "Pretty printed source:" << endl;
sout() << stack.print() << endl; sout() << stack.print() << endl;
}
if (_language != yul::AssemblyStack::Language::Ewasm && _targetMachine == yul::AssemblyStack::Machine::Ewasm) if (_language != yul::AssemblyStack::Language::Ewasm && _targetMachine == yul::AssemblyStack::Machine::Ewasm)
{ {
stack.translate(yul::AssemblyStack::Language::Ewasm); stack.translate(yul::AssemblyStack::Language::Ewasm);
stack.optimize(); stack.optimize();
if (m_options.compiler.outputs.ewasmIR)
{
sout() << endl << "==========================" << endl; sout() << endl << "==========================" << endl;
sout() << endl << "Translated source:" << endl; sout() << endl << "Translated source:" << endl;
sout() << stack.print() << endl; sout() << stack.print() << endl;
} }
}
yul::MachineAssemblyObject object; yul::MachineAssemblyObject object;
object = stack.assemble(_targetMachine); object = stack.assemble(_targetMachine);
object.bytecode->link(m_options.linker.libraries); object.bytecode->link(m_options.linker.libraries);
if (m_options.compiler.outputs.binary)
{
sout() << endl << "Binary representation:" << endl; sout() << endl << "Binary representation:" << endl;
if (object.bytecode) if (object.bytecode)
sout() << object.bytecode->toHex() << endl; sout() << object.bytecode->toHex() << endl;
else else
serr() << "No binary representation found." << endl; serr() << "No binary 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; sout() << endl << "Text representation:" << endl;
if (!object.assembly.empty()) if (!object.assembly.empty())
sout() << object.assembly << endl; sout() << object.assembly << endl;
else else
serr() << "No text representation found." << endl; serr() << "No text representation found." << endl;
} }
}
return true; return true;
} }

View File

@ -55,17 +55,16 @@ public:
bool parseArguments(int _argc, char const* const* _argv); bool parseArguments(int _argc, char const* const* _argv);
/// Read the content of all input files and initialize the file reader. /// Read the content of all input files and initialize the file reader.
bool readInputFiles(); bool readInputFiles();
/// Parse the files and create source code objects /// Parse the files, create source code objects, print the output.
bool processInput(); 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; } CommandLineOptions const& options() const { return m_options; }
FileReader const& fileReader() const { return m_fileReader; } FileReader const& fileReader() const { return m_fileReader; }
std::optional<std::string> const& standardJsonInput() const { return m_standardJsonInput; } std::optional<std::string> const& standardJsonInput() const { return m_standardJsonInput; }
private: private:
void printVersion();
void printLicense();
bool compile(); bool compile();
bool link(); bool link();
void writeLinkedFiles(); void writeLinkedFiles();

View File

@ -16,8 +16,6 @@
*/ */
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
#include "license.h"
#include <solc/CommandLineParser.h> #include <solc/CommandLineParser.h>
#include <libyul/optimiser/Suite.h> #include <libyul/optimiser/Suite.h>
#include <liblangutil/EVMVersion.h> #include <liblangutil/EVMVersion.h>
@ -36,19 +34,12 @@ namespace po = boost::program_options;
namespace solidity::frontend namespace solidity::frontend
{ {
ostream& CommandLineParser::sout()
{
m_hasOutput = true;
return m_sout;
}
ostream& CommandLineParser::serr() ostream& CommandLineParser::serr()
{ {
m_hasOutput = true; m_hasOutput = true;
return m_serr; return m_serr;
} }
#define cout
#define cerr #define cerr
static string const g_strAllowPaths = "allow-paths"; static string const g_strAllowPaths = "allow-paths";
@ -147,26 +138,6 @@ static map<InputMode, string> const g_inputModeName = {
{InputMode::Linker, "linker"}, {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) bool CommandLineParser::checkMutuallyExclusive(vector<string> const& _optionNames)
{ {
if (countEnabledOptions(_optionNames) > 1) if (countEnabledOptions(_optionNames) > 1)
@ -297,11 +268,11 @@ OptimiserSettings CommandLineOptions::optimiserSettings() const
return settings; 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; m_hasOutput = false;
if (!parseArgs(_argc, _argv, _interactiveTerminal)) if (!parseArgs(_argc, _argv))
return false; return false;
return processArgs(); return processArgs();
@ -463,17 +434,30 @@ bool CommandLineParser::parseOutputSelection()
{ {
static auto outputSupported = [](InputMode _mode, string_view _outputName) static auto outputSupported = [](InputMode _mode, string_view _outputName)
{ {
static set<string> const compilerModeOutputs = static set<string> const compilerModeOutputs = (
CompilerOutputs::componentMap() | CompilerOutputs::componentMap() |
ranges::views::keys | 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) switch (_mode)
{ {
case InputMode::Help:
case InputMode::License:
case InputMode::Version:
solAssert(false);
case InputMode::Compiler: case InputMode::Compiler:
case InputMode::CompilerWithASTImport: case InputMode::CompilerWithASTImport:
return contains(compilerModeOutputs, _outputName); return contains(compilerModeOutputs, _outputName);
case InputMode::Assembler: case InputMode::Assembler:
return contains(assemblerModeOutputs, _outputName);
case InputMode::StandardJson: case InputMode::StandardJson:
case InputMode::Linker: case InputMode::Linker:
return false; return false;
@ -485,6 +469,17 @@ bool CommandLineParser::parseOutputSelection()
for (auto&& [optionName, outputComponent]: CompilerOutputs::componentMap()) for (auto&& [optionName, outputComponent]: CompilerOutputs::componentMap())
m_options.compiler.outputs.*outputComponent = (m_args.count(optionName) > 0); 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; vector<string> unsupportedOutputs;
for (auto&& [optionName, outputComponent]: CompilerOutputs::componentMap()) for (auto&& [optionName, outputComponent]: CompilerOutputs::componentMap())
if (m_options.compiler.outputs.*outputComponent && !outputSupported(m_options.input.mode, optionName)) 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::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::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::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::signatureHashes).c_str(), "Function signature hashes of the contracts.")
(CompilerOutputs::componentName(&CompilerOutputs::natspecUser).c_str(), "Natspec user documentation of all 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.") (CompilerOutputs::componentName(&CompilerOutputs::natspecDev).c_str(), "Natspec developer documentation of all contracts.")
@ -835,7 +831,7 @@ po::positional_options_description CommandLineParser::positionalOptionsDescripti
return filesPositions; 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::options_description allOptions = optionsDescription();
po::positional_options_description filesPositions = positionalOptionsDescription(); po::positional_options_description filesPositions = positionalOptionsDescription();
@ -854,18 +850,6 @@ bool CommandLineParser::parseArgs(int _argc, char const* const* _argv, bool _int
return false; 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); po::notify(m_args);
return true; return true;
@ -874,6 +858,9 @@ bool CommandLineParser::parseArgs(int _argc, char const* const* _argv, bool _int
bool CommandLineParser::processArgs() bool CommandLineParser::processArgs()
{ {
if (!checkMutuallyExclusive({ if (!checkMutuallyExclusive({
g_strHelp,
g_strLicense,
g_strVersion,
g_strStandardJSON, g_strStandardJSON,
g_strLink, g_strLink,
g_strAssemble, g_strAssemble,
@ -883,7 +870,13 @@ bool CommandLineParser::processArgs()
})) }))
return false; 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; 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) 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; m_options.input.mode = InputMode::Assembler;
@ -894,6 +887,13 @@ bool CommandLineParser::processArgs()
else else
m_options.input.mode = InputMode::Compiler; 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 = { map<string, set<InputMode>> validOptionInputModeCombinations = {
// TODO: This should eventually contain all options. // TODO: This should eventually contain all options.
{g_strErrorRecovery, {InputMode::Compiler, InputMode::CompilerWithASTImport}}, {g_strErrorRecovery, {InputMode::Compiler, InputMode::CompilerWithASTImport}},
@ -915,11 +915,12 @@ bool CommandLineParser::processArgs()
if (!checkMutuallyExclusive({g_strColor, g_strNoColor})) if (!checkMutuallyExclusive({g_strColor, g_strNoColor}))
return false; return false;
array<string, 8> const conflictingWithStopAfter{ array<string, 9> const conflictingWithStopAfter{
CompilerOutputs::componentName(&CompilerOutputs::binary), CompilerOutputs::componentName(&CompilerOutputs::binary),
CompilerOutputs::componentName(&CompilerOutputs::ir), CompilerOutputs::componentName(&CompilerOutputs::ir),
CompilerOutputs::componentName(&CompilerOutputs::irOptimized), CompilerOutputs::componentName(&CompilerOutputs::irOptimized),
CompilerOutputs::componentName(&CompilerOutputs::ewasm), CompilerOutputs::componentName(&CompilerOutputs::ewasm),
CompilerOutputs::componentName(&CompilerOutputs::ewasmIR),
g_strGas, g_strGas,
CompilerOutputs::componentName(&CompilerOutputs::asm_), CompilerOutputs::componentName(&CompilerOutputs::asm_),
CompilerOutputs::componentName(&CompilerOutputs::asmJson), CompilerOutputs::componentName(&CompilerOutputs::asmJson),

View File

@ -48,6 +48,9 @@ namespace solidity::frontend
enum class InputMode enum class InputMode
{ {
Help,
License,
Version,
Compiler, Compiler,
CompilerWithASTImport, CompilerWithASTImport,
StandardJson, StandardJson,
@ -75,6 +78,7 @@ struct CompilerOutputs
{"ir", &CompilerOutputs::ir}, {"ir", &CompilerOutputs::ir},
{"ir-optimized", &CompilerOutputs::irOptimized}, {"ir-optimized", &CompilerOutputs::irOptimized},
{"ewasm", &CompilerOutputs::ewasm}, {"ewasm", &CompilerOutputs::ewasm},
{"ewasm-ir", &CompilerOutputs::ewasmIR},
{"hashes", &CompilerOutputs::signatureHashes}, {"hashes", &CompilerOutputs::signatureHashes},
{"userdoc", &CompilerOutputs::natspecUser}, {"userdoc", &CompilerOutputs::natspecUser},
{"devdoc", &CompilerOutputs::natspecDev}, {"devdoc", &CompilerOutputs::natspecDev},
@ -94,6 +98,7 @@ struct CompilerOutputs
bool ir = false; bool ir = false;
bool irOptimized = false; bool irOptimized = false;
bool ewasm = false; bool ewasm = false;
bool ewasmIR = false;
bool signatureHashes = false; bool signatureHashes = false;
bool natspecUser = false; bool natspecUser = false;
bool natspecDev = false; bool natspecDev = false;
@ -230,34 +235,28 @@ struct CommandLineOptions
/// Parses the command-line arguments and produces a filled-out CommandLineOptions structure. /// Parses the command-line arguments and produces a filled-out CommandLineOptions structure.
/// Validates provided values and prints error messages in case of errors. /// 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 class CommandLineParser
{ {
public: public:
explicit CommandLineParser(std::ostream& _sout, std::ostream& _serr): explicit CommandLineParser(std::ostream& _serr):
m_sout(_sout),
m_serr(_serr) m_serr(_serr)
{} {}
/// Parses the command-line arguments and fills out the internal CommandLineOptions structure. /// Parses the command-line arguments and fills out the internal CommandLineOptions structure.
/// Performs validation and prints error messages. If requested, prints usage banner, version /// Performs validation and prints error messages.
/// 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.
/// @return true if there were no validation errors when parsing options and the /// @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 /// 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 /// 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. /// 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; } CommandLineOptions const& options() const { return m_options; }
/// Returns true if the parser has written anything to any of its output streams. /// Returns true if the parser has written anything to any of its output streams.
bool hasOutput() const { return m_hasOutput; } bool hasOutput() const { return m_hasOutput; }
static void printHelp(std::ostream& _out) { _out << optionsDescription(); }
private: private:
/// @returns a specification of all named command-line options accepted by the compiler. /// @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. /// 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. /// 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. /// 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. /// @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 /// Validates parsed arguments stored in @a m_args and fills out the internal CommandLineOptions
/// structure. /// structure.
@ -294,20 +293,13 @@ private:
bool parseOutputSelection(); bool parseOutputSelection();
bool checkMutuallyExclusive(std::vector<std::string> const& _optionNames); bool checkMutuallyExclusive(std::vector<std::string> const& _optionNames);
[[noreturn]] void printVersionAndExit();
[[noreturn]] void printLicenseAndExit();
size_t countEnabledOptions(std::vector<std::string> const& _optionNames) const; size_t countEnabledOptions(std::vector<std::string> const& _optionNames) const;
static std::string joinOptionNames(std::vector<std::string> const& _optionNames, std::string _separator = ", "); 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 /// Returns the stream that should receive error output. Sets m_hasOutput to true if the
/// stream has ever been used. /// stream has ever been used.
std::ostream& serr(); std::ostream& serr();
std::ostream& m_sout;
std::ostream& m_serr; std::ostream& m_serr;
bool m_hasOutput = false; bool m_hasOutput = false;

View File

@ -65,8 +65,7 @@ int main(int argc, char** argv)
bool success = bool success =
cli.parseArguments(argc, argv) && cli.parseArguments(argc, argv) &&
cli.readInputFiles() && cli.readInputFiles() &&
cli.processInput() && cli.processInput();
cli.actOnInput();
return success ? 0 : 1; return success ? 0 : 1;
} }

View File

@ -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. # General helper function for testing SOLC behaviour, based on file name, compile opts, exit code, stdout and stderr.
# An failure is expected. # An failure is expected.
function test_solc_behaviour() function test_solc_behaviour
{ {
local filename="${1}" local filename="${1}"
local solc_args local solc_args
@ -288,7 +288,7 @@ EOF
} }
function test_solc_assembly_output() function test_solc_assembly_output
{ {
local input="${1}" local input="${1}"
local expected="${2}" local expected="${2}"

View File

@ -66,7 +66,7 @@ IR:
object "C_6" { object "C_6" {
code { code {
/// @src 0:60:101 "contract C {..." /// @src 0:60:101 "contract C {..."
mstore(64, 128) mstore(64, memoryguard(128))
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
constructor_C_6() constructor_C_6()
@ -97,7 +97,7 @@ object "C_6" {
object "C_6_deployed" { object "C_6_deployed" {
code { code {
/// @src 0:60:101 "contract C {..." /// @src 0:60:101 "contract C {..."
mstore(64, 128) mstore(64, memoryguard(128))
if iszero(lt(calldatasize(), 4)) if iszero(lt(calldatasize(), 4))
{ {
@ -182,11 +182,12 @@ object "C_6" {
code { code {
{ {
/// @src 0:60:101 "contract C {..." /// @src 0:60:101 "contract C {..."
mstore(64, 128) let _1 := memoryguard(0x80)
mstore(64, _1)
if callvalue() { revert(0, 0) } if callvalue() { revert(0, 0) }
let _1 := datasize("C_6_deployed") let _2 := datasize("C_6_deployed")
codecopy(128, dataoffset("C_6_deployed"), _1) codecopy(_1, dataoffset("C_6_deployed"), _2)
return(128, _1) return(_1, _2)
} }
} }
/// @use-src 0:"debug_info_in_yul_and_evm_asm_print_all/input.sol" /// @use-src 0:"debug_info_in_yul_and_evm_asm_print_all/input.sol"
@ -194,15 +195,16 @@ object "C_6" {
code { code {
{ {
/// @src 0:60:101 "contract C {..." /// @src 0:60:101 "contract C {..."
mstore(64, 128) let _1 := memoryguard(0x80)
mstore(64, _1)
if iszero(lt(calldatasize(), 4)) if iszero(lt(calldatasize(), 4))
{ {
let _1 := 0 let _2 := 0
if eq(0x26121ff0, shr(224, calldataload(_1))) if eq(0x26121ff0, shr(224, calldataload(_2)))
{ {
if callvalue() { revert(_1, _1) } if callvalue() { revert(_2, _2) }
if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) } if slt(add(calldatasize(), not(3)), _2) { revert(_2, _2) }
return(128, _1) return(_1, _2)
} }
} }
revert(0, 0) revert(0, 0)

View File

@ -66,7 +66,7 @@ IR:
object "C_6" { object "C_6" {
code { code {
/// @src 0:60:101 /// @src 0:60:101
mstore(64, 128) mstore(64, memoryguard(128))
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
constructor_C_6() constructor_C_6()
@ -97,7 +97,7 @@ object "C_6" {
object "C_6_deployed" { object "C_6_deployed" {
code { code {
/// @src 0:60:101 /// @src 0:60:101
mstore(64, 128) mstore(64, memoryguard(128))
if iszero(lt(calldatasize(), 4)) if iszero(lt(calldatasize(), 4))
{ {
@ -181,11 +181,12 @@ object "C_6" {
code { code {
{ {
/// @src 0:60:101 /// @src 0:60:101
mstore(64, 128) let _1 := memoryguard(0x80)
mstore(64, _1)
if callvalue() { revert(0, 0) } if callvalue() { revert(0, 0) }
let _1 := datasize("C_6_deployed") let _2 := datasize("C_6_deployed")
codecopy(128, dataoffset("C_6_deployed"), _1) codecopy(_1, dataoffset("C_6_deployed"), _2)
return(128, _1) return(_1, _2)
} }
} }
/// @use-src 0:"debug_info_in_yul_and_evm_asm_print_location_only/input.sol" /// @use-src 0:"debug_info_in_yul_and_evm_asm_print_location_only/input.sol"
@ -193,15 +194,16 @@ object "C_6" {
code { code {
{ {
/// @src 0:60:101 /// @src 0:60:101
mstore(64, 128) let _1 := memoryguard(0x80)
mstore(64, _1)
if iszero(lt(calldatasize(), 4)) if iszero(lt(calldatasize(), 4))
{ {
let _1 := 0 let _2 := 0
if eq(0x26121ff0, shr(224, calldataload(_1))) if eq(0x26121ff0, shr(224, calldataload(_2)))
{ {
if callvalue() { revert(_1, _1) } if callvalue() { revert(_2, _2) }
if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) } if slt(add(calldatasize(), not(3)), _2) { revert(_2, _2) }
return(128, _1) return(_1, _2)
} }
} }
revert(0, 0) revert(0, 0)

View File

@ -63,7 +63,7 @@ IR:
object "C_6" { object "C_6" {
code { code {
mstore(64, 128) mstore(64, memoryguard(128))
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
constructor_C_6() constructor_C_6()
@ -90,7 +90,7 @@ object "C_6" {
object "C_6_deployed" { object "C_6_deployed" {
code { code {
mstore(64, 128) mstore(64, memoryguard(128))
if iszero(lt(calldatasize(), 4)) if iszero(lt(calldatasize(), 4))
{ {
@ -171,26 +171,28 @@ Optimized IR:
object "C_6" { object "C_6" {
code { code {
{ {
mstore(64, 128) let _1 := memoryguard(0x80)
mstore(64, _1)
if callvalue() { revert(0, 0) } if callvalue() { revert(0, 0) }
let _1 := datasize("C_6_deployed") let _2 := datasize("C_6_deployed")
codecopy(128, dataoffset("C_6_deployed"), _1) codecopy(_1, dataoffset("C_6_deployed"), _2)
return(128, _1) return(_1, _2)
} }
} }
/// @use-src 0:"debug_info_in_yul_and_evm_asm_print_none/input.sol" /// @use-src 0:"debug_info_in_yul_and_evm_asm_print_none/input.sol"
object "C_6_deployed" { object "C_6_deployed" {
code { code {
{ {
mstore(64, 128) let _1 := memoryguard(0x80)
mstore(64, _1)
if iszero(lt(calldatasize(), 4)) if iszero(lt(calldatasize(), 4))
{ {
let _1 := 0 let _2 := 0
if eq(0x26121ff0, shr(224, calldataload(_1))) if eq(0x26121ff0, shr(224, calldataload(_2)))
{ {
if callvalue() { revert(_1, _1) } if callvalue() { revert(_2, _2) }
if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) } if slt(add(calldatasize(), not(3)), _2) { revert(_2, _2) }
return(128, _1) return(_1, _2)
} }
} }
revert(0, 0) revert(0, 0)

View File

@ -11,7 +11,7 @@ IR:
object "C_2" { object "C_2" {
code { code {
/// @src 0:265:278 "contract C {}" /// @src 0:265:278 "contract C {}"
mstore(64, 128) mstore(64, memoryguard(128))
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
constructor_C_2() constructor_C_2()
@ -42,7 +42,7 @@ object "C_2" {
object "C_2_deployed" { object "C_2_deployed" {
code { code {
/// @src 0:265:278 "contract C {}" /// @src 0:265:278 "contract C {}"
mstore(64, 128) mstore(64, memoryguard(128))
revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74()
@ -82,11 +82,12 @@ object "C_2" {
code { code {
{ {
/// @src 0:265:278 "contract C {}" /// @src 0:265:278 "contract C {}"
mstore(64, 128) let _1 := memoryguard(0x80)
mstore(64, _1)
if callvalue() { revert(0, 0) } if callvalue() { revert(0, 0) }
let _1 := datasize("C_2_deployed") let _2 := datasize("C_2_deployed")
codecopy(128, dataoffset("C_2_deployed"), _1) codecopy(_1, dataoffset("C_2_deployed"), _2)
return(128, _1) return(_1, _2)
} }
} }
/// @use-src 0:"debug_info_in_yul_snippet_escaping/input.sol" /// @use-src 0:"debug_info_in_yul_snippet_escaping/input.sol"
@ -94,7 +95,7 @@ object "C_2" {
code { code {
{ {
/// @src 0:265:278 "contract C {}" /// @src 0:265:278 "contract C {}"
mstore(64, 128) mstore(64, memoryguard(0x80))
revert(0, 0) revert(0, 0)
} }
} }
@ -115,7 +116,7 @@ IR:
object "D_27" { object "D_27" {
code { code {
/// @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." /// @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..."
mstore(64, 128) mstore(64, memoryguard(128))
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
constructor_D_27() constructor_D_27()
@ -146,7 +147,7 @@ object "D_27" {
object "D_27_deployed" { object "D_27_deployed" {
code { code {
/// @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." /// @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..."
mstore(64, 128) mstore(64, memoryguard(128))
if iszero(lt(calldatasize(), 4)) if iszero(lt(calldatasize(), 4))
{ {
@ -372,7 +373,7 @@ object "D_27" {
object "C_2" { object "C_2" {
code { code {
/// @src 0:265:278 "contract C {}" /// @src 0:265:278 "contract C {}"
mstore(64, 128) mstore(64, memoryguard(128))
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
constructor_C_2() constructor_C_2()
@ -403,7 +404,7 @@ object "D_27" {
object "C_2_deployed" { object "C_2_deployed" {
code { code {
/// @src 0:265:278 "contract C {}" /// @src 0:265:278 "contract C {}"
mstore(64, 128) mstore(64, memoryguard(128))
revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74()
@ -448,11 +449,12 @@ object "D_27" {
code { code {
{ {
/// @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." /// @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) } if callvalue() { revert(0, 0) }
let _1 := datasize("D_27_deployed") let _2 := datasize("D_27_deployed")
codecopy(128, dataoffset("D_27_deployed"), _1) codecopy(_1, dataoffset("D_27_deployed"), _2)
return(128, _1) return(_1, _2)
} }
} }
/// @use-src 0:"debug_info_in_yul_snippet_escaping/input.sol" /// @use-src 0:"debug_info_in_yul_snippet_escaping/input.sol"
@ -460,26 +462,25 @@ object "D_27" {
code { code {
{ {
/// @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." /// @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)) if iszero(lt(calldatasize(), 4))
{ {
let _1 := 0 let _2 := 0
if eq(0x26121ff0, shr(224, calldataload(_1))) if eq(0x26121ff0, shr(224, calldataload(_2)))
{ {
if callvalue() { revert(_1, _1) } if callvalue() { revert(_2, _2) }
if slt(add(calldatasize(), not(3)), _1) { revert(_1, _1) } if slt(add(calldatasize(), not(3)), _2) { revert(_2, _2) }
/// @src 0:446:491 "new /// @src 0:149:156 \"new C()\"..." /// @src 0:446:491 "new /// @src 0:149:156 \"new C()\"..."
let _2 := datasize("C_2") let _3 := 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) let _4 := add(_1, _3)
if or(gt(_3, 0xffffffffffffffff), lt(_3, /** @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." */ 128)) if or(gt(_4, 0xffffffffffffffff), lt(_4, _1)) { panic_error_0x41() }
/// @src 0:446:491 "new /// @src 0:149:156 \"new C()\"..." datacopy(_1, dataoffset("C_2"), _3)
{ panic_error_0x41() } 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)))
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))
{ {
/// @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..." /// @src 0:279:599 "contract D /** @src 0:96:165 \"contract D {...\" *\/ {..."
let pos := mload(64) let pos := mload(64)
returndatacopy(pos, _1, returndatasize()) returndatacopy(pos, _2, returndatasize())
revert(pos, returndatasize()) revert(pos, returndatasize())
} }
mstore(add(allocate_memory_array_string(), 32), "/*") mstore(add(allocate_memory_array_string(), 32), "/*")
@ -539,11 +540,12 @@ object "D_27" {
code { code {
{ {
/// @src 0:265:278 "contract C {}" /// @src 0:265:278 "contract C {}"
mstore(64, 128) let _1 := memoryguard(0x80)
mstore(64, _1)
if callvalue() { revert(0, 0) } if callvalue() { revert(0, 0) }
let _1 := datasize("C_2_deployed") let _2 := datasize("C_2_deployed")
codecopy(128, dataoffset("C_2_deployed"), _1) codecopy(_1, dataoffset("C_2_deployed"), _2)
return(128, _1) return(_1, _2)
} }
} }
/// @use-src 0:"debug_info_in_yul_snippet_escaping/input.sol" /// @use-src 0:"debug_info_in_yul_snippet_escaping/input.sol"
@ -551,7 +553,7 @@ object "D_27" {
code { code {
{ {
/// @src 0:265:278 "contract C {}" /// @src 0:265:278 "contract C {}"
mstore(64, 128) mstore(64, memoryguard(0x80))
revert(0, 0) revert(0, 0)
} }
} }

View File

@ -0,0 +1 @@
--assemble --optimize --yul-dialect evm --machine ewasm --asm

View File

@ -0,0 +1 @@
Warning: Yul is still experimental. Please use the output with care.

View File

@ -0,0 +1,4 @@
{
let x := 42
sstore(0, x)
}

View File

@ -0,0 +1,2 @@
======= evm_to_wasm_output_selection_asm_only/input.yul (Ewasm) =======

View File

@ -0,0 +1 @@
--assemble --optimize --yul-dialect evm --machine ewasm --ewasm-ir

View File

@ -0,0 +1 @@
Warning: Yul is still experimental. Please use the output with care.

View File

@ -0,0 +1,4 @@
{
let x := 42
sstore(0, x)
}

View File

@ -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)))
}
}
}

View File

@ -0,0 +1 @@
--assemble --optimize --yul-dialect evm --machine ewasm --ewasm

View File

@ -0,0 +1 @@
Warning: Yul is still experimental. Please use the output with care.

View File

@ -0,0 +1,4 @@
{
let x := 42
sstore(0, x)
}

View File

@ -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)
)
)

View File

@ -11,7 +11,7 @@ IR:
object "C_81" { object "C_81" {
code { code {
/// @src 0:82:370 "contract C {..." /// @src 0:82:370 "contract C {..."
mstore(64, 128) mstore(64, memoryguard(128))
if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() } if callvalue() { revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() }
constructor_C_81() constructor_C_81()
@ -42,7 +42,7 @@ object "C_81" {
object "C_81_deployed" { object "C_81_deployed" {
code { code {
/// @src 0:82:370 "contract C {..." /// @src 0:82:370 "contract C {..."
mstore(64, 128) mstore(64, memoryguard(128))
if iszero(lt(calldatasize(), 4)) if iszero(lt(calldatasize(), 4))
{ {

View File

@ -13,7 +13,7 @@
}, },
"calldata_array_index_access_uint256_dyn_calldata": "calldata_array_index_access_uint256_dyn_calldata":
{ {
"entryPoint": 168, "entryPoint": 152,
"parameterSlots": 2, "parameterSlots": 2,
"returnSlots": 1 "returnSlots": 1
} }

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