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

View File

@ -46,7 +46,7 @@ source "${REPODIR}/scripts/common.sh"
# Test result output directory (CircleCI is reading test results from here)
mkdir -p test_results
# in case we run with ASAN enabled, we must increase stck size.
# in case we run with ASAN enabled, we must increase stack size.
ulimit -s 16384
get_logfile_basename() {

View File

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

View File

@ -7,20 +7,36 @@ Breaking changes:
* Commandline Interface: Remapping targets are not automatically added to allowed paths.
### 0.8.10 (unreleased)
### 0.8.11 (unreleased)
Language Features:
Compiler Features:
Bugfixes:
### 0.8.10 (2021-11-09)
Language Features:
* Inline Assembly: Support ``.address`` and ``.selector`` on external function pointers to access their address and function selector.
Compiler Features:
* Code Generator: Skip existence check for external contract if return data is expected. In this case, the ABI decoder will revert if the contract does not exist.
* Commandline Interface: Accept nested brackets in step sequences passed to ``--yul-optimizations``.
* Commandline Interface: Add ``--debug-info`` option for selecting how much extra debug information should be included in the produced EVM assembly and Yul code.
* Commandline Interface: Support ``--asm``, ``--bin``, ``--ir-optimized``, ``--ewasm`` and ``--ewasm-ir`` output selection options in assembler mode.
* Commandline Interface: Use different colors when printing errors, warnings and infos.
* JSON AST: Set absolute paths of imports earlier, in the ``parsing`` stage.
* SMTChecker: Output values for ``block.*``, ``msg.*`` and ``tx.*`` variables that are present in the called functions.
* SMTChecker: Report contract invariants and reentrancy properties. This can be enabled via the CLI option ``--model-checker-invariants`` or the Standard JSON option ``settings.modelChecker.invariants``.
* Standard JSON: Accept nested brackets in step sequences passed to ``settings.optimizer.details.yulDetails.optimizerSteps``.
* Standard JSON: Add ``settings.debug.debugInfo`` option for selecting how much extra debug information should be included in the produced EVM assembly and Yul code.
* Yul EVM Code Transform: Switch to new optimized code transform when compiling via Yul with enabled optimizer.
* Yul Optimizer: Take control-flow side-effects of user-defined functions into account in various optimizer steps.
Bugfixes:
@ -31,13 +47,23 @@ Bugfixes:
* Commandline Interface: Report output selection options unsupported by the selected input mode instead of ignoring them.
* Commandline Interface: When linking only accept exact matches for library names passed to the ``--libraries`` option. Library names not prefixed with a file name used to match any library with that name.
* SMTChecker: Fix internal error in magic type access (``block``, ``msg``, ``tx``).
* TypeChecker: Fix internal error when using user defined value types in public library functions.
* SMTChecker: Fix internal error in the CHC engine when passing gas in the function options.
* TypeChecker: Fix internal error when using arrays and structs with user defined value types before declaration.
* TypeChecker: Fix internal error when using user defined value types in public library functions.
* TypeChecker: Improved error message for constant variables with (nested) mapping types.
* Yul Assembler: Fix internal error when function names are not unique.
* Yul IR Generator: Do not output empty switches/if-bodies for empty contracts.
Important Bugfixes in Experimental Features:
* Yul IR Generator: Changes to function return variables referenced in modifier invocation arguments were not properly forwarded if there was more than one return variable.
Build System:
* Pass linker-only emscripten options only when linking.
* Remove obsolete compatibility workaround for emscripten builds.
* Update emscripten to version 2.0.33.
### 0.8.9 (2021-09-29)

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
add_compile_options(-Qunused-arguments)
elseif(EMSCRIPTEN)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --memory-init-file 0")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --memory-init-file 0")
# Leave only exported symbols as public and aggressively remove others
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -ffunction-sections -fvisibility=hidden")
# Optimisation level
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
# Re-enable exception catching (optimisations above -O1 disable it)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DISABLE_EXCEPTION_CATCHING=0")
# Remove any code related to exit (such as atexit)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXIT_RUNTIME=0")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXIT_RUNTIME=0")
# Remove any code related to filesystem access
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s FILESYSTEM=0")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s FILESYSTEM=0")
# Allow memory growth, but disable some optimisations
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ALLOW_MEMORY_GROWTH=1")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1")
# Disable eval()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s DYNAMIC_EXECUTION=0")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s DYNAMIC_EXECUTION=0")
# Disable greedy exception catcher
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NODEJS_CATCH_EXIT=0")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s NODEJS_CATCH_EXIT=0")
# Abort if linking results in any undefined symbols
# Note: this is on by default in the CMake Emscripten module which we aren't using
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1")
# Disallow deprecated emscripten build options.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s STRICT=1")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s STRICT=1")
# Export the Emscripten-generated auxiliary methods which are needed by solc-js.
# Which methods of libsolc itself are exported is specified in libsolc/CMakeLists.txt.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap','addFunction','removeFunction','UTF8ToString','lengthBytesUTF8','stringToUTF8','setValue']")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap','addFunction','removeFunction','UTF8ToString','lengthBytesUTF8','stringToUTF8','setValue']")
# Build for webassembly target.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WASM=1")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s WASM=1")
# Set webassembly build to synchronous loading.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WASM_ASYNC_COMPILATION=0")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s WASM_ASYNC_COMPILATION=0")
# Output a single js file with the wasm binary embedded as base64 string.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s SINGLE_FILE=1")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s SINGLE_FILE=1")
# Allow new functions to be added to the wasm module via addFunction.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ALLOW_TABLE_GROWTH=1")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s ALLOW_TABLE_GROWTH=1")
# Disable warnings about not being pure asm.js due to memory growth.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-almost-asm")
endif()

View File

@ -6,9 +6,34 @@ static std::string const otherLicenses{R"(Most of the code is licensed under GPL
parts are as follows:
libkeccak-tiny:
The file libsolutil/Keccak256.cpp incorporates libkeccak-tiny.
A single-file implementation of SHA-3 and SHAKE implemented by David Leon Gil
License: CC0, attribution kindly requested. Blame taken too, but not liability.
picosha2:
The file libsolutil/picosha2.h is imported.
Copyright (C) 2017 okdshin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
jsoncpp:
The JsonCpp library's source code, including accompanying documentation,
tests and demonstration applications, are licensed under the following

View File

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

View File

@ -91,7 +91,7 @@ else:
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build', 'contracts', 'types', 'examples', 'grammar', 'ir']
exclude_patterns = ['_build', 'contracts', 'types', 'examples', 'grammar']
# The reST default role (used for this markup: `text`) to use for all
# documents.

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.
In particular, it should be possible to evaluate a ``pure`` function at compile-time given
only its inputs and ``msg.data``, but without any knowledge of the current blockchain state.
This means that reading from ``immutable`` variables can be a non-pure operation.
.. note::
If the compiler's EVM target is Byzantium or newer (default) the opcode ``STATICCALL`` is used,

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
always succeed, Solidity uses the ``extcodesize`` opcode to check that
the contract that is about to be called actually exists (it contains code)
and causes an exception if it does not.
and causes an exception if it does not. This check is skipped if the return
data will be decoded after the call and thus the ABI decoder will catch the
case of a non-existing contract.
Note that this check is not performed in case of :ref:`low-level calls <address_related>` which
operate on addresses rather than contract instances.
.. note::
Be careful when using high-level calls to
:ref:`precompiled contracts <precompiledContracts>`,
since the compiler considers them non-existing according to the
above logic even though they execute code and can return data.
Function calls also cause exceptions if the called contract itself
throws an exception or goes out of gas.

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>`_.
Furthermore, breaking changes as well as
new features are introduced regularly. We currently use
a 0.x version number `to indicate this fast pace of change <https://semver.org/#spec-item-4>`_.
a 0.y.z version number `to indicate this fast pace of change <https://semver.org/#spec-item-4>`_.
.. warning::
@ -136,6 +136,7 @@ Contents
using-the-compiler.rst
analysing-compilation-output.rst
ir-breaking-changes.rst
.. toctree::
:maxdepth: 2

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 Build Tools: https://www.visualstudio.com/downloads/#build-tools-for-visual-studio-2019
Dependencies Helper Script
--------------------------
We have a helper script which you can use to install all required external dependencies
on macOS, Windows and on numerous Linux distros.
.. code-block:: bash
./scripts/install_deps.sh
Or, on Windows:
We have a helper script which you can use to install all required external dependencies:
.. code-block:: bat
scripts\install_deps.ps1
Note that the latter command will install ``boost`` and ``cmake`` to the ``deps`` subdirectory, while the former command
will attempt to install the dependencies globally.
This will install ``boost`` and ``cmake`` to the ``deps`` subdirectory.
Clone the Repository
--------------------

View File

@ -429,12 +429,10 @@ is transformed to
.. code-block:: text
{
Init...
for {} C { Post... } {
Body...
}
}
This eases the rest of the optimization process because we can ignore
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
by changing some internal state which causes all functions to revert. This
makes it impossible to use the contract, as it returns Ether immediately.
.. index:: ! precompiled contracts, ! precompiles, ! contract;precompiled
.. _precompiledContracts:
Precompiled Contracts
=====================
There is a small set of contract addresses that are special:
The address range between ``1`` and (including) ``8`` contains
"precompiled contracts" that can be called as any other contract
but their behaviour (and their gas consumption) is not defined
by EVM code stored at that address (they do not contain code)
but instead is implemented in the EVM execution environment itself.
Different EVM-compatible chains might use a different set of
precompiled contracts. It might also be possible that new
precompiled contracts are added to the Ethereum main chain in the future,
but you can reasonabyly expect them to always be in the range between
``1`` and ``0xffff`` (inclusive).

View File

@ -5,8 +5,26 @@
Solidity IR-based Codegen Changes
*********************************
This section highlights the main differences between the old and the IR-based codegen,
along with the reasoning behind the changes and how to update affected code.
Solidity can generate EVM bytecode in two different ways:
Either directly from Solidity to EVM opcodes ("old codegen") or through
an intermediate representation ("IR") in Yul ("new codegen" or "IR-based codegen").
The IR-based code generator was introduced with an aim to not only allow
code generation to be more transparent and auditable but also
to enable more powerful optimization passes that span across functions.
Currently, the IR-based code generator is still marked experimental,
but it supports all language features and has received a lot of testing,
so we consider it almost ready for production use.
You can enable it on the command line using ``--experimental-via-ir``
or with the option ``{"viaIR": true}`` in standard-json and we
encourage everyone to try it out!
For several reasons, there are tiny semantic differences between the old
and the IR-based code generator, mostly in areas where we would not
expect people to rely on this behaviour anyway.
This section highlights the main differences between the old and the IR-based codegen.
Semantic Only Changes
=====================
@ -14,8 +32,13 @@ Semantic Only Changes
This section lists the changes that are semantic-only, thus potentially
hiding new and different behavior in existing code.
- When storage structs are deleted, every storage slot that contains a member of the struct is set to zero entirely. Formally, padding space was left untouched.
Consequently, if the padding space within a struct is used to store data (e.g. in the context of a contract upgrade), you have to be aware that ``delete`` will now also clear the added member (while it wouldn't have been cleared in the past).
- When storage structs are deleted, every storage slot that contains
a member of the struct is set to zero entirely. Formerly, padding space
was left untouched.
Consequently, if the padding space within a struct is used to store data
(e.g. in the context of a contract upgrade), you have to be aware that
``delete`` will now also clear the added member (while it wouldn't
have been cleared in the past).
.. code-block:: solidity
@ -132,7 +155,10 @@ This causes differences in some contracts, for example:
Previously, ``y`` would be set to 0. This is due to the fact that we would first initialize state variables: First, ``x`` is set to 0, and when initializing ``y``, ``f()`` would return 0 causing ``y`` to be 0 as well.
With the new rules, ``y`` will be set to 42. We first initialize ``x`` to 0, then call A's constructor which sets ``x`` to 42. Finally, when initializing ``y``, ``f()`` returns 42 causing ``y`` to be 42.
- Copying ``bytes`` arrays from memory to storage is implemented in a different way. The old code generator always copies full words, while the new one cuts the byte array after its end. The old behaviour can lead to dirty data being copied after the end of the array (but still in the same storage slot).
- Copying ``bytes`` arrays from memory to storage is implemented in a different way.
The old code generator always copies full words, while the new one cuts the byte
array after its end. The old behaviour can lead to dirty data being copied after
the end of the array (but still in the same storage slot).
This causes differences in some contracts, for example:
.. code-block:: solidity
@ -155,8 +181,10 @@ This causes differences in some contracts, for example:
}
}
Previously ``f()`` would return ``0x6465616462656566313564656164000000000000000000000000000000000010`` (it has correct length, and correct first 8 elements, but then it contains dirty data which was set via assembly).
Now it is returning ``0x6465616462656566000000000000000000000000000000000000000000000010`` (it has correct length, and correct elements, but does not contain superfluous data).
Previously ``f()`` would return ``0x6465616462656566313564656164000000000000000000000000000000000010``
(it has correct length, and correct first 8 elements, but then it contains dirty data which was set via assembly).
Now it is returning ``0x6465616462656566000000000000000000000000000000000000000000000010`` (it has
correct length, and correct elements, but does not contain superfluous data).
.. index:: ! evaluation order; expression
@ -183,7 +211,8 @@ This causes differences in some contracts, for example:
.. index:: ! evaluation order; function arguments
On the other hand, function argument expressions are evaluated in the same order by both code generators with the exception of the global functions ``addmod`` and ``mulmod``.
On the other hand, function argument expressions are evaluated in the same order
by both code generators with the exception of the global functions ``addmod`` and ``mulmod``.
For example:
.. code-block:: solidity
@ -227,11 +256,15 @@ This causes differences in some contracts, for example:
- Old code generator: ``aMod = 0`` and ``mMod = 2``
- New code generator: ``aMod = 4`` and ``mMod = 0``
- The new code generator imposes a hard limit of ``type(uint64).max`` (``0xffffffffffffffff``) for the free memory pointer. Allocations that would increase its value beyond this limit revert. The old code generator does not have this limit.
- The new code generator imposes a hard limit of ``type(uint64).max``
(``0xffffffffffffffff``) for the free memory pointer. Allocations that would
increase its value beyond this limit revert. The old code generator does not
have this limit.
For example:
.. code-block:: solidity
:force:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >0.8.0;
@ -264,7 +297,7 @@ The old code generator uses code offsets or tags for values of internal function
these offsets are different at construction time and after deployment and the values can cross this border via storage.
Because of that, both offsets are encoded at construction time into the same value (into different bytes).
In the new code generator, function pointers use the AST IDs of the functions as values. Since calls via jumps are not possible,
In the new code generator, function pointers use internal IDs that are allocated in sequence. Since calls via jumps are not possible,
calls through function pointers always have to use an internal dispatch function that uses the ``switch`` statement to select
the right function.
@ -280,6 +313,7 @@ Cleanup
The old code generator only performs cleanup before an operation whose result could be affected by the values of the dirty bits.
The new code generator performs cleanup after any operation that can result in dirty bits.
The hope is that the optimizer will be powerful enough to eliminate redundant cleanup operations.
For example:

View File

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

View File

@ -2,7 +2,7 @@ if (EMSCRIPTEN)
# Specify which functions to export in soljson.js.
# Note that additional Emscripten-generated methods needed by solc-js are
# defined to be exported in cmake/EthCompilerSettings.cmake.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXPORTED_FUNCTIONS='[\"_solidity_license\",\"_solidity_version\",\"_solidity_compile\",\"_solidity_alloc\",\"_solidity_free\",\"_solidity_reset\"]'")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s EXPORTED_FUNCTIONS='[\"_solidity_license\",\"_solidity_version\",\"_solidity_compile\",\"_solidity_alloc\",\"_solidity_free\",\"_solidity_reset\"]'")
add_executable(soljson libsolc.cpp libsolc.h)
target_link_libraries(soljson PRIVATE solidity)
else()

View File

@ -2629,11 +2629,23 @@ void ExpressionCompiler::appendExternalFunctionCall(
bool existenceChecked = false;
// Check the target contract exists (has code) for non-low-level calls.
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.appendConditionalRevert(false, "Target contract does not contain code");
existenceChecked = true;
}
}
if (_functionType.gasSet())
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)
{
retParams.emplace_back(m_context.newYulVariable());
assignRetParams += retParams.back() + " := " + retParamsIn[i] + "\n";
assignRetParams += retParams.at(i) + " := " + retParamsIn.at(i) + "\n";
}
t("retParams", joinHumanReadable(retParams));
t("assignRetParams", assignRetParams);
@ -529,7 +529,7 @@ string IRGenerator::generateFunctionWithModifierInner(FunctionDefinition const&
for (size_t i = 0; i < retParams.size(); ++i)
{
retParamsIn.emplace_back(m_context.newYulVariable());
assignRetParams += retParams.back() + " := " + retParamsIn[i] + "\n";
assignRetParams += retParams.at(i) + " := " + retParamsIn.at(i) + "\n";
}
vector<string> params = retParamsIn;
for (auto const& varDecl: _function.parameters())
@ -1069,9 +1069,6 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
string IRGenerator::memoryInit(bool _useMemoryGuard)
{
// TODO: Remove once we have made sure it is safe, i.e. after "Yul memory objects lite".
// Also restore the tests removed in the commit that adds this comment.
_useMemoryGuard = false;
// This function should be called at the beginning of the EVM call frame
// and thus can assume all memory to be zero, including the contents of
// the "zero memory area" (the position CompilerUtils::zeroPointer points to).

View File

@ -2451,8 +2451,10 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
appendCode() << "mstore(add(" << m_utils.allocateUnboundedFunction() << "() , " << to_string(returnInfo.estimatedReturnSize) << "), 0)\n";
}
Whiskers templ(R"(if iszero(extcodesize(<address>)) { <revertNoCode>() }
Whiskers templ(R"(
<?checkExtcodesize>
if iszero(extcodesize(<address>)) { <revertNoCode>() }
</checkExtcodesize>
// storage for arguments and returned data
let <pos> := <allocateUnbounded>()
mstore(<pos>, <shl28>(<funSel>))
@ -2477,6 +2479,18 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
}
)");
templ("revertNoCode", m_utils.revertReasonIfDebugFunction("Target contract does not contain code"));
// We do not need to check extcodesize if we expect return data: If there is no
// code, the call will return empty data and the ABI decoder will revert.
size_t encodedHeadSize = 0;
for (auto const& t: returnInfo.returnTypes)
encodedHeadSize += t->decodingType()->calldataHeadSize();
bool const checkExtcodesize =
encodedHeadSize == 0 ||
!m_context.evmVersion().supportsReturndata() ||
m_context.revertStrings() >= RevertStrings::Debug;
templ("checkExtcodesize", checkExtcodesize);
templ("pos", m_context.newYulVariable());
templ("end", m_context.newYulVariable());
if (_functionCall.annotation().tryCall)
@ -2532,6 +2546,8 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10;
if (funType.valueSet())
gasNeededByCaller += evmasm::GasCosts::callValueTransferGas;
if (!checkExtcodesize)
gasNeededByCaller += evmasm::GasCosts::callNewAccountGas; // we never know
templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")");
}
// Order is important here, STATICCALL might overlap with DELEGATECALL.

View File

@ -791,7 +791,7 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall)
valueIndex = i;
break;
}
solAssert(valueIndex, "");
if (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).
// Here we combine every context in which an external function can be called with all possible verification conditions
// in its call graph. Each such combination forms a unique verification target.
vector<CHCVerificationTarget> verificationTargets;
map<unsigned, vector<CHCQueryPlaceholder>> targetEntryPoints;
for (auto const& [function, placeholders]: m_queryPlaceholders)
{
auto functionTargets = transactionVerificationTargetsIds(function);
for (auto const& placeholder: placeholders)
for (unsigned id: functionTargets)
{
auto const& target = m_verificationTargets.at(id);
verificationTargets.push_back(CHCVerificationTarget{
{target.type, placeholder.fromPredicate, placeholder.constraints && placeholder.errorExpression == target.errorId},
target.errorId,
target.errorNode
});
}
targetEntryPoints[id].push_back(placeholder);
}
set<unsigned> checkedErrorIds;
for (auto const& target: verificationTargets)
for (auto const& [targetId, placeholders]: targetEntryPoints)
{
string errorType;
ErrorId errorReporterId;
auto const& target = m_verificationTargets.at(targetId);
if (target.type == VerificationTargetType::PopEmptyArray)
{
solAssert(dynamic_cast<FunctionCall const*>(target.errorNode), "");
@ -1692,7 +1687,7 @@ void CHC::checkVerificationTargets()
else
solAssert(false, "");
checkAndReportTarget(target, errorReporterId, errorType + " happens here.", errorType + " might happen here.");
checkAndReportTarget(target, placeholders, errorReporterId, errorType + " happens here.", errorType + " might happen here.");
checkedErrorIds.insert(target.errorId);
}
@ -1750,7 +1745,7 @@ void CHC::checkVerificationTargets()
{
set<unsigned> seenErrors;
msg += "<errorCode> = 0 -> no errors\n";
for (auto const& target: verificationTargets)
for (auto const& [id, target]: m_verificationTargets)
if (!seenErrors.count(target.errorId))
{
seenErrors.insert(target.errorId);
@ -1785,6 +1780,7 @@ void CHC::checkVerificationTargets()
void CHC::checkAndReportTarget(
CHCVerificationTarget const& _target,
vector<CHCQueryPlaceholder> const& _placeholders,
ErrorId _errorReporterId,
string _satMsg,
string _unknownMsg
@ -1794,7 +1790,12 @@ void CHC::checkAndReportTarget(
return;
createErrorBlock();
connectBlocks(_target.value, error(), _target.constraints);
for (auto const& placeholder: _placeholders)
connectBlocks(
placeholder.fromPredicate,
error(),
placeholder.constraints && placeholder.errorExpression == _target.errorId
);
auto const& location = _target.errorNode->location();
auto [result, invariant, model] = query(error(), location);
if (result == CheckResult::UNSATISFIABLE)

View File

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

View File

@ -83,6 +83,7 @@
#include <utility>
#include <map>
#include <limits>
#include <string>
using namespace std;
using namespace solidity;
@ -350,8 +351,22 @@ bool CompilerStack::parse()
else
{
source.ast->annotation().path = path;
for (auto const& import: ASTNode::filteredNodes<ImportDirective>(source.ast->nodes()))
{
solAssert(!import->path().empty(), "Import path cannot be empty.");
// The current value of `path` is the absolute path as seen from this source file.
// We first have to apply remappings before we can store the actual absolute path
// as seen globally.
import->annotation().absolutePath = applyRemapping(util::absolutePath(
import->path(),
path
), path);
}
if (m_stopAfter >= ParsedAndImported)
for (auto const& newSource: loadMissingSources(*source.ast, path))
for (auto const& newSource: loadMissingSources(*source.ast))
{
string const& newPath = newSource.first;
string const& newContents = newSource.second;
@ -1091,7 +1106,7 @@ string const& CompilerStack::Source::ipfsUrl() const
return ipfsUrlCached;
}
StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string const& _sourcePath)
StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast)
{
solAssert(m_stackState < ParsedAndImported, "");
StringMap newSources;
@ -1100,14 +1115,8 @@ StringMap CompilerStack::loadMissingSources(SourceUnit const& _ast, std::string
for (auto const& node: _ast.nodes())
if (ImportDirective const* import = dynamic_cast<ImportDirective*>(node.get()))
{
solAssert(!import->path().empty(), "Import path cannot be empty.");
string const& importPath = *import->annotation().absolutePath;
string importPath = util::absolutePath(import->path(), _sourcePath);
// The current value of `path` is the absolute path as seen from this source file.
// We first have to apply remappings before we can store the actual absolute path
// as seen globally.
importPath = applyRemapping(importPath, _sourcePath);
import->annotation().absolutePath = importPath;
if (m_sources.count(importPath) || newSources.count(importPath))
continue;
@ -1248,7 +1257,9 @@ void CompilerStack::assemble(
m_errorReporter.warning(
5574_error,
_contract.location(),
"Contract code size exceeds 24576 bytes (a limit introduced in Spurious Dragon). "
"Contract code size is "s +
to_string(compiledContract.runtimeObject.bytecode.size()) +
" bytes and exceeds 24576 bytes (a limit introduced in Spurious Dragon). "
"This contract may not be deployable on mainnet. "
"Consider enabling the optimizer (with a low \"runs\" value!), "
"turning off revert strings, or using libraries."

View File

@ -392,9 +392,9 @@ private:
void findAndReportCyclicContractDependencies();
/// Loads the missing sources from @a _ast (named @a _path) using the callback
/// @a m_readFile and stores the absolute paths of all imports in the AST annotations.
/// @a m_readFile
/// @returns the newly loaded sources.
StringMap loadMissingSources(SourceUnit const& _ast, std::string const& _path);
StringMap loadMissingSources(SourceUnit const& _ast);
std::string applyRemapping(std::string const& _path, std::string const& _context);
void resolveImports();

View File

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

View File

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

View File

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

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
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* AST walker that finds all function definitions and stores them into a map indexed by the function names.
*/
// SPDX-License-Identifier: GPL-3.0
#pragma once
#include <libyul/optimiser/ASTWalker.h>
#include <map>
namespace solidity::yul
{
/**
* AST walker that finds all function definitions and stores them into a map indexed by the function names.
* Resolves references to user-defined functions in function calls.
* Assumes the code is correct, i.e. does not check for references to be valid or unique.
*
* Prerequisite: Disambiguator
* Be careful not to iterate over the result - it is not deterministic.
*/
class FunctionDefinitionCollector: ASTWalker
class FunctionReferenceResolver: private ASTWalker
{
public:
static std::map<YulString, FunctionDefinition const*> run(Block const& _block);
explicit FunctionReferenceResolver(Block const& _ast);
std::map<FunctionCall const*, FunctionDefinition const*> const& references() const { return m_functionReferences; }
private:
using ASTWalker::operator();
void operator()(FunctionDefinition const& _functionDefinition) override;
std::map<YulString, FunctionDefinition const*> m_functionDefinitions;
void operator()(FunctionCall const& _functionCall) override;
void operator()(Block const& _block) override;
std::map<FunctionCall const*, FunctionDefinition const*> m_functionReferences;
std::vector<std::map<YulString, FunctionDefinition const*>> m_scopes;
};
}

View File

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

View File

@ -57,7 +57,8 @@ private:
AsmAnalysisInfo const& _analysisInfo,
Dialect const& _dialect
);
CFG::Operation const& visitFunctionCall(FunctionCall const&);
void registerFunction(FunctionDefinition const& _function);
Stack const& visitFunctionCall(FunctionCall const&);
Stack visitAssignmentRightHandSide(Expression const& _expression, size_t _expectedSlotCount);
Scope::Function const& lookupFunction(YulString _name) const;

View File

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

View File

@ -44,7 +44,7 @@ vector<StackTooDeepError> OptimizedEVMCodeTransform::run(
Block const& _block,
EVMDialect const& _dialect,
BuiltinContext& _builtinContext,
bool _useNamedLabelsForFunctions
UseNamedLabels _useNamedLabelsForFunctions
)
{
std::unique_ptr<CFG> dfg = ControlFlowGraphBuilder::build(_analysisInfo, _dialect, _block);
@ -170,15 +170,35 @@ void OptimizedEVMCodeTransform::operator()(CFG::Assignment const& _assignment)
OptimizedEVMCodeTransform::OptimizedEVMCodeTransform(
AbstractAssembly& _assembly,
BuiltinContext& _builtinContext,
bool _useNamedLabelsForFunctions,
UseNamedLabels _useNamedLabelsForFunctions,
CFG const& _dfg,
StackLayout const& _stackLayout
):
m_assembly(_assembly),
m_builtinContext(_builtinContext),
m_useNamedLabelsForFunctions(_useNamedLabelsForFunctions),
m_dfg(_dfg),
m_stackLayout(_stackLayout)
m_stackLayout(_stackLayout),
m_functionLabels([&](){
map<CFG::FunctionInfo const*, AbstractAssembly::LabelID> functionLabels;
set<YulString> assignedFunctionNames;
for (Scope::Function const* function: m_dfg.functions)
{
CFG::FunctionInfo const& functionInfo = m_dfg.functionInfo.at(function);
bool nameAlreadySeen = !assignedFunctionNames.insert(function->name).second;
if (_useNamedLabelsForFunctions == UseNamedLabels::YesAndForceUnique)
yulAssert(!nameAlreadySeen);
bool useNamedLabel = _useNamedLabelsForFunctions != UseNamedLabels::Never && !nameAlreadySeen;
functionLabels[&functionInfo] = useNamedLabel ?
m_assembly.namedLabel(
function->name.str(),
function->arguments.size(),
function->returns.size(),
functionInfo.debugData ? functionInfo.debugData->astID : nullopt
) :
m_assembly.newLabelId();
}
return functionLabels;
}())
{
}
@ -191,15 +211,7 @@ void OptimizedEVMCodeTransform::assertLayoutCompatibility(Stack const& _currentS
AbstractAssembly::LabelID OptimizedEVMCodeTransform::getFunctionLabel(Scope::Function const& _function)
{
CFG::FunctionInfo const& functionInfo = m_dfg.functionInfo.at(&_function);
if (!m_functionLabels.count(&functionInfo))
m_functionLabels[&functionInfo] = m_useNamedLabelsForFunctions ? m_assembly.namedLabel(
functionInfo.function.name.str(),
functionInfo.function.arguments.size(),
functionInfo.function.returns.size(),
{}
) : m_assembly.newLabelId();
return m_functionLabels[&functionInfo];
return m_functionLabels.at(&m_dfg.functionInfo.at(&_function));
}
void OptimizedEVMCodeTransform::validateSlot(StackSlot const& _slot, Expression const& _expression)

View File

@ -43,13 +43,17 @@ struct StackLayout;
class OptimizedEVMCodeTransform
{
public:
/// Use named labels for functions 1) Yes and check that the names are unique
/// 2) For none of the functions 3) for the first function of each name.
enum class UseNamedLabels { YesAndForceUnique, Never, ForFirstFunctionOfEachName };
[[nodiscard]] static std::vector<StackTooDeepError> run(
AbstractAssembly& _assembly,
AsmAnalysisInfo& _analysisInfo,
Block const& _block,
EVMDialect const& _dialect,
BuiltinContext& _builtinContext,
bool _useNamedLabelsForFunctions = false
UseNamedLabels _useNamedLabelsForFunctions
);
/// Generate code for the function call @a _call. Only public for using with std::visit.
@ -62,7 +66,7 @@ private:
OptimizedEVMCodeTransform(
AbstractAssembly& _assembly,
BuiltinContext& _builtinContext,
bool _useNamedLabelsForFunctions,
UseNamedLabels _useNamedLabelsForFunctions,
CFG const& _dfg,
StackLayout const& _stackLayout
);
@ -70,6 +74,7 @@ private:
/// Assert that it is valid to transition from @a _currentStack to @a _desiredStack.
/// That is @a _currentStack matches each slot in @a _desiredStack that is not a JunkSlot exactly.
static void assertLayoutCompatibility(Stack const& _currentStack, Stack const& _desiredStack);
/// @returns The label of the entry point of the given @a _function.
/// Creates and stores a new label, if none exists already.
AbstractAssembly::LabelID getFunctionLabel(Scope::Function const& _function);
@ -94,13 +99,12 @@ private:
AbstractAssembly& m_assembly;
BuiltinContext& m_builtinContext;
bool m_useNamedLabelsForFunctions = true;
CFG const& m_dfg;
StackLayout const& m_stackLayout;
Stack m_stack;
std::map<yul::FunctionCall const*, AbstractAssembly::LabelID> m_returnLabels;
std::map<CFG::BasicBlock const*, AbstractAssembly::LabelID> m_blockLabels;
std::map<CFG::FunctionInfo const*, AbstractAssembly::LabelID> m_functionLabels;
std::map<CFG::FunctionInfo const*, AbstractAssembly::LabelID> const m_functionLabels;
/// Set of blocks already generated. If any of the contained blocks is ever jumped to, m_blockLabels should
/// contain a jump label for it.
std::set<CFG::BasicBlock const*> m_generated;

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

View File

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

View File

@ -20,6 +20,7 @@
#include <libyul/AST.h>
#include <libyul/Utilities.h>
#include <libyul/optimiser/NameCollector.h>
#include <libyul/ControlFlowSideEffectsCollector.h>
#include <libsolutil/CommonData.h>
using namespace std;
@ -27,6 +28,14 @@ using namespace solidity;
using namespace solidity::yul;
using namespace solidity::util;
void ConditionalUnsimplifier::run(OptimiserStepContext& _context, Block& _ast)
{
ConditionalUnsimplifier{
_context.dialect,
ControlFlowSideEffectsCollector{_context.dialect, _ast}.functionSideEffectsNamed()
}(_ast);
}
void ConditionalUnsimplifier::operator()(Switch& _switch)
{
visit(*_switch.expression);
@ -78,7 +87,7 @@ void ConditionalUnsimplifier::operator()(Block& _block)
YulString condition = std::get<Identifier>(*_if.condition).name;
if (
holds_alternative<Assignment>(_stmt2) &&
TerminationFinder(m_dialect).controlFlowKind(_if.body.statements.back()) !=
TerminationFinder(m_dialect, &m_functionSideEffects).controlFlowKind(_if.body.statements.back()) !=
TerminationFinder::ControlFlow::FlowOut
)
{

View File

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

View File

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

View File

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

View File

@ -23,6 +23,7 @@
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/YulString.h>
#include <libyul/ControlFlowSideEffects.h>
#include <map>
#include <set>
@ -36,7 +37,9 @@ struct OptimiserStepContext;
* Optimisation stage that removes unreachable code
*
* Unreachable code is any code within a block which is preceded by a
* leave, return, invalid, break, continue, selfdestruct or revert.
* leave, return, invalid, break, continue, selfdestruct or revert or
* a call to a user-defined function that never returns (either due to
* recursion or a call to return / revert / stop).
*
* Function definitions are retained as they might be called by earlier
* code and thus are considered reachable.
@ -57,9 +60,13 @@ public:
void operator()(Block& _block) override;
private:
DeadCodeEliminator(Dialect const& _dialect): m_dialect(_dialect) {}
DeadCodeEliminator(
Dialect const& _dialect,
std::map<YulString, ControlFlowSideEffects> _sideEffects
): m_dialect(_dialect), m_functionSideEffects(move(_sideEffects)) {}
Dialect const& m_dialect;
std::map<YulString, ControlFlowSideEffects> m_functionSideEffects;
};
}

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();
}
void Assignments::operator()(Assignment const& _assignment)
{
for (auto const& var: _assignment.variableNames)
m_names.emplace(var.name);
}
void AssignmentsSinceContinue::operator()(ForLoop const& _forLoop)
{
m_forLoopDepth++;
@ -109,3 +102,22 @@ void AssignmentsSinceContinue::operator()(FunctionDefinition const&)
{
yulAssert(false, "");
}
std::set<YulString> solidity::yul::assignedVariableNames(Block const& _code)
{
std::set<YulString> names;
forEach<Assignment const>(_code, [&](Assignment const& _assignment) {
for (auto const& var: _assignment.variableNames)
names.emplace(var.name);
});
return names;
}
map<YulString, FunctionDefinition const*> solidity::yul::allFunctionDefinitions(Block const& _block)
{
std::map<YulString, FunctionDefinition const*> result;
forEach<FunctionDefinition const>(_block, [&](FunctionDefinition const& _function) {
result[_function.name] = &_function;
});
return result;
}

View File

@ -91,20 +91,6 @@ private:
std::map<YulString, size_t> m_references;
};
/**
* Specific AST walker that finds all variables that are assigned to.
*/
class Assignments: public ASTWalker
{
public:
using ASTWalker::operator ();
void operator()(Assignment const& _assignment) override;
std::set<YulString> const& names() const { return m_names; }
private:
std::set<YulString> m_names;
};
/**
* Collects all names from a given continue statement on onwards.
*
@ -130,4 +116,12 @@ private:
std::set<YulString> m_names;
};
/// @returns the names of all variables that are assigned to inside @a _code.
/// (ignores variable declarations)
std::set<YulString> assignedVariableNames(Block const& _code);
/// @returns all function definitions anywhere in the AST.
/// Requires disambiguated source.
std::map<YulString, FunctionDefinition const*> allFunctionDefinitions(Block const& _block);
}

View File

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

View File

@ -182,8 +182,19 @@ pair<TerminationFinder::ControlFlow, size_t> TerminationFinder::firstUncondition
TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement const& _statement)
{
if (
holds_alternative<VariableDeclaration>(_statement) &&
std::get<VariableDeclaration>(_statement).value &&
containsNonContinuingFunctionCall(*std::get<VariableDeclaration>(_statement).value)
)
return ControlFlow::Terminate;
else if (
holds_alternative<Assignment>(_statement) &&
containsNonContinuingFunctionCall(*std::get<Assignment>(_statement).value)
)
return ControlFlow::Terminate;
else if (
holds_alternative<ExpressionStatement>(_statement) &&
isTerminatingBuiltin(std::get<ExpressionStatement>(_statement))
containsNonContinuingFunctionCall(std::get<ExpressionStatement>(_statement).expression)
)
return ControlFlow::Terminate;
else if (holds_alternative<Break>(_statement))
@ -196,10 +207,18 @@ TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement cons
return ControlFlow::FlowOut;
}
bool TerminationFinder::isTerminatingBuiltin(ExpressionStatement const& _exprStmnt)
bool TerminationFinder::containsNonContinuingFunctionCall(Expression const& _expr)
{
if (holds_alternative<FunctionCall>(_exprStmnt.expression))
if (auto instruction = toEVMInstruction(m_dialect, std::get<FunctionCall>(_exprStmnt.expression).functionName.name))
return evmasm::SemanticInformation::terminatesControlFlow(*instruction);
if (auto functionCall = std::get_if<FunctionCall>(&_expr))
{
for (auto const& arg: functionCall->arguments)
if (containsNonContinuingFunctionCall(arg))
return true;
if (auto builtin = m_dialect.builtin(functionCall->functionName.name))
return !builtin->controlFlowSideEffects.canContinue;
else if (m_functionSideEffects && m_functionSideEffects->count(functionCall->functionName.name))
return !m_functionSideEffects->at(functionCall->functionName.name).canContinue;
}
return false;
}

View File

@ -205,22 +205,31 @@ private:
std::set<YulString> m_variableReferences;
};
struct ControlFlowSideEffects;
/**
* Helper class to find "irregular" control flow.
* This includes termination, break and continue.
* This includes termination, break, continue and leave.
* In general, it is applied only to "simple" statements. The control-flow
* of loops, switches and if statements is always "FlowOut" with the assumption
* that the caller will descend into them.
*/
class TerminationFinder
{
public:
// TODO check all uses of TerminationFinder!
/// "Terminate" here means that there is no continuing control-flow.
/// If this is applied to a function that can revert or stop, but can also
/// exit regularly, the property is set to "FlowOut".
enum class ControlFlow { FlowOut, Break, Continue, Terminate, Leave };
TerminationFinder(Dialect const& _dialect): m_dialect(_dialect) {}
TerminationFinder(
Dialect const& _dialect,
std::map<YulString, ControlFlowSideEffects> const* _functionSideEffects = nullptr
): m_dialect(_dialect), m_functionSideEffects(_functionSideEffects) {}
/// @returns the index of the first statement in the provided sequence
/// that is an unconditional ``break``, ``continue``, ``leave`` or a
/// call to a terminating builtin function.
/// call to a terminating function.
/// If control flow can continue at the end of the list,
/// returns `FlowOut` and ``size_t(-1)``.
/// The function might return ``FlowOut`` even though control
@ -233,13 +242,14 @@ public:
/// This function could return FlowOut even if control flow never continues.
ControlFlow controlFlowKind(Statement const& _statement);
/// @returns true if the expression statement is a direct
/// call to a builtin terminating function like
/// ``stop``, ``revert`` or ``return``.
bool isTerminatingBuiltin(ExpressionStatement const& _exprStmnt);
/// @returns true if the expression contains a
/// call to a terminating function, i.e. a function that does not have
/// a regular "flow out" control-flow (it might also be recursive).
bool containsNonContinuingFunctionCall(Expression const& _expr);
private:
Dialect const& m_dialect;
std::map<YulString, ControlFlowSideEffects> const* m_functionSideEffects;
};
}

View File

@ -27,6 +27,13 @@
#include <libyul/optimiser/Metrics.h>
#include <libyul/optimiser/Semantics.h>
#include <libyul/backends/evm/ControlFlowGraphBuilder.h>
#include <libyul/backends/evm/StackHelpers.h>
#include <libyul/backends/evm/StackLayoutGenerator.h>
#include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h>
#include <libyul/CompilabilityChecker.h>
#include <libyul/AST.h>
@ -162,6 +169,50 @@ void eliminateVariables(
UnusedPruner::runUntilStabilised(_dialect, _node, _allowMSizeOptimization);
}
void eliminateVariables(
Dialect const& _dialect,
Block& _block,
vector<StackLayoutGenerator::StackTooDeep> const& _unreachables,
bool _allowMSizeOptimization
)
{
RematCandidateSelector selector{_dialect};
selector(_block);
std::map<YulString, size_t> candidates;
for (auto [cost, candidatesWithCost]: selector.candidates())
for (auto candidate: candidatesWithCost)
candidates[get<0>(candidate)] = cost;
set<YulString> varsToEliminate;
// TODO: this currently ignores the fact that variables may reference other variables we want to eliminate.
for (auto const& unreachable: _unreachables)
{
map<size_t, vector<YulString>> suitableCandidates;
size_t neededSlots = unreachable.deficit;
for (auto varName: unreachable.variableChoices)
{
if (varsToEliminate.count(varName))
--neededSlots;
else if (size_t* cost = util::valueOrNullptr(candidates, varName))
if (!util::contains(suitableCandidates[*cost], varName))
suitableCandidates[*cost].emplace_back(varName);
}
for (auto candidatesByCost: suitableCandidates)
{
for (auto candidate: candidatesByCost.second)
if (neededSlots--)
varsToEliminate.emplace(candidate);
else
break;
if (!neededSlots)
break;
}
}
Rematerialiser::run(_dialect, _block, std::move(varsToEliminate), true);
UnusedPruner::runUntilStabilised(_dialect, _block, _allowMSizeOptimization);
}
}
bool StackCompressor::run(
@ -176,7 +227,34 @@ bool StackCompressor::run(
_object.code->statements.size() > 0 && holds_alternative<Block>(_object.code->statements.at(0)),
"Need to run the function grouper before the stack compressor."
);
bool usesOptimizedCodeGenerator = false;
if (auto evmDialect = dynamic_cast<EVMDialect const*>(&_dialect))
usesOptimizedCodeGenerator =
_optimizeStackAllocation &&
evmDialect->evmVersion().canOverchargeGasForCall() &&
evmDialect->providesObjectAccess();
bool allowMSizeOptimzation = !MSizeFinder::containsMSize(_dialect, *_object.code);
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++)
{
map<YulString, int> stackSurplus = CompilabilityChecker(_dialect, _object, _optimizeStackAllocation).stackDeficit;

View File

@ -18,17 +18,21 @@
#include <libyul/optimiser/StackLimitEvader.h>
#include <libyul/optimiser/CallGraphGenerator.h>
#include <libyul/optimiser/FunctionCallFinder.h>
#include <libyul/optimiser/FunctionDefinitionCollector.h>
#include <libyul/optimiser/NameDispenser.h>
#include <libyul/optimiser/NameCollector.h>
#include <libyul/optimiser/StackToMemoryMover.h>
#include <libyul/backends/evm/ControlFlowGraphBuilder.h>
#include <libyul/backends/evm/EVMDialect.h>
#include <libyul/AsmAnalysis.h>
#include <libyul/AST.h>
#include <libyul/CompilabilityChecker.h>
#include <libyul/Exceptions.h>
#include <libyul/Object.h>
#include <libyul/Utilities.h>
#include <libsolutil/Algorithms.h>
#include <libsolutil/CommonData.h>
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/concat.hpp>
#include <range/v3/view/take.hpp>
@ -114,6 +118,45 @@ u256 literalArgumentValue(FunctionCall const& _call)
}
}
void StackLimitEvader::run(
OptimiserStepContext& _context,
Object& _object
)
{
auto const* evmDialect = dynamic_cast<EVMDialect const*>(&_context.dialect);
yulAssert(
evmDialect && evmDialect->providesObjectAccess(),
"StackLimitEvader can only be run on objects using the EVMDialect with object access."
);
if (evmDialect && evmDialect->evmVersion().canOverchargeGasForCall())
{
yul::AsmAnalysisInfo analysisInfo = yul::AsmAnalyzer::analyzeStrictAssertCorrect(*evmDialect, _object);
unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build(analysisInfo, *evmDialect, *_object.code);
run(_context, _object, StackLayoutGenerator::reportStackTooDeep(*cfg));
}
else
run(_context, _object, CompilabilityChecker{
_context.dialect,
_object,
true
}.unreachableVariables);
}
void StackLimitEvader::run(
OptimiserStepContext& _context,
Object& _object,
map<YulString, vector<StackLayoutGenerator::StackTooDeep>> const& _stackTooDeepErrors
)
{
map<YulString, set<YulString>> unreachableVariables;
for (auto&& [function, stackTooDeepErrors]: _stackTooDeepErrors)
// TODO: choose wisely.
for (auto const& stackTooDeepError: stackTooDeepErrors)
unreachableVariables[function] += stackTooDeepError.variableChoices | ranges::views::take(stackTooDeepError.deficit) | ranges::to<set<YulString>>;
run(_context, _object, unreachableVariables);
}
void StackLimitEvader::run(
OptimiserStepContext& _context,
Object& _object,
@ -150,7 +193,7 @@ void StackLimitEvader::run(
if (_unreachableVariables.count(function))
return;
map<YulString, FunctionDefinition const*> functionDefinitions = FunctionDefinitionCollector::run(*_object.code);
map<YulString, FunctionDefinition const*> functionDefinitions = allFunctionDefinitions(*_object.code);
MemoryOffsetAllocator memoryOffsetAllocator{_unreachableVariables, callGraph.functionCalls, functionDefinitions};
uint64_t requiredSlots = memoryOffsetAllocator.run();

View File

@ -22,6 +22,7 @@
#pragma once
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/backends/evm/StackLayoutGenerator.h>
namespace solidity::yul
{
@ -61,6 +62,25 @@ public:
Object& _object,
std::map<YulString, std::set<YulString>> const& _unreachableVariables
);
/// @a _stackTooDeepErrors can be determined by the StackLayoutGenerator.
/// Can only be run on the EVM dialect with objects.
/// Abort and do nothing, if no ``memoryguard`` call or several ``memoryguard`` calls
/// with non-matching arguments are found, or if any of the @a _stackTooDeepErrors
/// are contained in a recursive function.
static void run(
OptimiserStepContext& _context,
Object& _object,
std::map<YulString, std::vector<StackLayoutGenerator::StackTooDeep>> const& _stackTooDeepErrors
);
/// Determines stack too deep errors using the appropriate code generation backend.
/// Can only be run on the EVM dialect with objects.
/// Abort and do nothing, if no ``memoryguard`` call or several ``memoryguard`` calls
/// with non-matching arguments are found, or if any of the unreachable variables
/// are contained in a recursive function.
static void run(
OptimiserStepContext& _context,
Object& _object
);
};
}

View File

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

View File

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

View File

@ -59,6 +59,8 @@ public:
PrintStep,
PrintChanges
};
OptimiserSuite(OptimiserStepContext& _context, Debug _debug = Debug::None): m_context(_context), m_debug(_debug) {}
/// The value nullopt for `_expectedExecutionsPerDeployment` represents creation code.
static void run(
Dialect const& _dialect,
@ -82,20 +84,7 @@ public:
static std::map<char, std::string> const& stepAbbreviationToNameMap();
private:
OptimiserSuite(
Dialect const& _dialect,
std::set<YulString> const& _externallyUsedIdentifiers,
Debug _debug,
Block& _ast,
std::optional<size_t> expectedExecutionsPerDeployment
):
m_dispenser{_dialect, _ast, _externallyUsedIdentifiers},
m_context{_dialect, m_dispenser, _externallyUsedIdentifiers, expectedExecutionsPerDeployment},
m_debug(_debug)
{}
NameDispenser m_dispenser;
OptimiserStepContext m_context;
OptimiserStepContext& m_context;
Debug m_debug;
};

View File

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

View File

@ -34,7 +34,7 @@ else
BUILD_DIR="$1"
fi
# solbuildpackpusher/solidity-buildpack-deps:emscripten-6
# solbuildpackpusher/solidity-buildpack-deps:emscripten-7
docker run -v "$(pwd):/root/project" -w /root/project \
solbuildpackpusher/solidity-buildpack-deps@sha256:092da5817bc032c91a806b4f73db2a1a31e5cc4c066d94d43eedd9f365df7154 \
solbuildpackpusher/solidity-buildpack-deps@sha256:9ffcd0944433fe100e9433f2aa9ba5c21e096e758ad8a05a4a76feaed3d1f463 \
./scripts/ci/build_emscripten.sh "$BUILD_DIR"

View File

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

View File

@ -68,10 +68,6 @@ emcmake cmake \
-DTESTS=0 \
..
make soljson
# Patch soljson.js for backwards compatibility.
# TODO: remove this with 0.7.
# "viiiii" encodes the signature of the callback function.
sed -i -e 's/addFunction(func,sig){/addFunction(func,sig){sig=sig||"viiiii";/' libsolc/soljson.js
cd ..
mkdir -p upload

View File

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

View File

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

View File

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

View File

@ -22,7 +22,8 @@
YULARGS=(--strict-assembly)
FULLARGS=(--optimize --combined-json "abi,asm,ast,bin,bin-runtime,devdoc,hashes,metadata,opcodes,srcmap,srcmap-runtime,userdoc")
OLDARGS=(--optimize --combined-json "abi,asm,ast,bin,bin-runtime,devdoc,interface,metadata,opcodes,srcmap,srcmap-runtime,userdoc")
function compileFull()
function compileFull
{
local expected_exit_code=0
local expect_output='none'

View File

@ -26,10 +26,14 @@
# contains a Makefile in the docker/ subdirectory that can be used to create the
# required base image using:
#
# make version=2.0.12 build
# make version=2.0.33 build
#
FROM emscripten/emsdk:2.0.12 AS base
LABEL version="6"
# Note that emscripten is supposed to automatically install to $(em-config CACHE)/sysroot, but
# apparently this currently breaks due to conflicting compatibility headers.
# Using $(em-config CACHE)/sysroot/usr seems to work, though, and still has cmake find the
# dependencies automatically.
FROM emscripten/emsdk:2.0.33 AS base
LABEL version="7"
ADD emscripten.jam /usr/src
RUN set -ex; \
@ -39,8 +43,8 @@ RUN set -ex; \
mkdir build; \
cd build; \
emcmake cmake \
-DCMAKE_INSTALL_PREFIX=$(em-config CACHE)/sysroot/usr \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DCMAKE_INSTALL_PREFIX=/emsdk/upstream/emscripten/system \
-DZ3_BUILD_LIBZ3_SHARED=OFF \
-DZ3_ENABLE_EXAMPLE_TARGETS=OFF \
-DZ3_BUILD_TEST_EXECUTABLES=OFF \
@ -63,5 +67,5 @@ RUN set -ex; \
./b2 toolset=emscripten link=static variant=release threading=single runtime-link=static \
--with-system --with-filesystem --with-test --with-program_options \
cxxflags="-s DISABLE_EXCEPTION_CATCHING=0 -Wno-unused-local-typedef -Wno-variadic-macros -Wno-c99-extensions -Wno-all" \
--prefix=/emsdk/upstream/emscripten/system install; \
--prefix=$(em-config CACHE)/sysroot/usr install; \
rm -r /usr/src/boost_1_75_0

View File

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

View File

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

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__':
script_description = (
"Reads Solidity, C++ or RST source files and extracts compilable solidity and yul code blocks from them. "
"Can be used to generate test cases to validade code examples. "
"Can be used to generate test cases to validate code examples. "
)
parser = ArgumentParser(description=script_description)

View File

@ -50,14 +50,14 @@
# FIXME: Can't use set -u because the old Bash on macOS treats empty arrays as unbound variables
set -eo pipefail
die()
function die
{
# shellcheck disable=SC2059
>&2 printf "ERROR: $1\n" "${@:2}"
exit 1
}
get_reported_solc_version()
function get_reported_solc_version
{
local solc_binary="$1"
@ -70,7 +70,7 @@ get_reported_solc_version()
echo "$version_banner" | tail -n 1 | sed -n -E 's/^Version: (.*)$/\1/p'
}
validate_reported_version()
function validate_reported_version
{
local reported_version="$1"
local expected_version_and_commit="$2"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -23,6 +23,7 @@
*/
#include <solc/CommandLineInterface.h>
#include "license.h"
#include "solidity/BuildInfo.h"
#include <libsolidity/interface/Version.h>
@ -405,6 +406,13 @@ bool CommandLineInterface::readInputFiles()
{
solAssert(!m_standardJsonInput.has_value(), "");
if (
m_options.input.mode == InputMode::Help ||
m_options.input.mode == InputMode::License ||
m_options.input.mode == InputMode::Version
)
return true;
m_fileReader.setBasePath(m_options.input.basePath);
if (m_fileReader.basePath() != "")
@ -573,8 +581,18 @@ void CommandLineInterface::createJson(string const& _fileName, string const& _js
bool CommandLineInterface::parseArguments(int _argc, char const* const* _argv)
{
CommandLineParser parser(sout(/* _markAsUsed */ false), serr(/* _markAsUsed */ false));
bool success = parser.parse(_argc, _argv, isatty(fileno(stdin)));
CommandLineParser parser(serr(/* _markAsUsed */ false));
if (isatty(fileno(stdin)) && _argc == 1)
{
// If the terminal is taking input from the user, provide more user-friendly output.
CommandLineParser::printHelp(sout());
// In this case we want to exit with an error but not display any error message.
return false;
}
bool success = parser.parse(_argc, _argv);
if (!success)
return false;
m_hasOutput = m_hasOutput || parser.hasOutput();
@ -587,6 +605,15 @@ bool CommandLineInterface::processInput()
{
switch (m_options.input.mode)
{
case InputMode::Help:
CommandLineParser::printHelp(sout());
break;
case InputMode::License:
printLicense();
break;
case InputMode::Version:
printVersion();
break;
case InputMode::StandardJson:
{
solAssert(m_standardJsonInput.has_value(), "");
@ -594,21 +621,38 @@ bool CommandLineInterface::processInput()
StandardCompiler compiler(m_fileReader.reader(), m_options.formatting.json);
sout() << compiler.compile(move(m_standardJsonInput.value())) << endl;
m_standardJsonInput.reset();
return true;
break;
}
case InputMode::Assembler:
{
return assemble(m_options.assembly.inputLanguage, m_options.assembly.targetMachine);
}
if (!assemble(m_options.assembly.inputLanguage, m_options.assembly.targetMachine))
return false;
break;
case InputMode::Linker:
return link();
if (!link())
return false;
writeLinkedFiles();
break;
case InputMode::Compiler:
case InputMode::CompilerWithASTImport:
return compile();
if (!compile())
return false;
outputCompilationResults();
}
solAssert(false, "");
return false;
return !m_outputFailed;
}
void CommandLineInterface::printVersion()
{
sout() << "solc, the solidity compiler commandline interface" << endl;
sout() << "Version: " << solidity::frontend::VersionString << endl;
}
void CommandLineInterface::printLicense()
{
sout() << otherLicenses << endl;
// This is a static variable generated by cmake from LICENSE.txt
sout() << licenseText << endl;
}
bool CommandLineInterface::compile()
@ -843,21 +887,6 @@ void CommandLineInterface::handleAst()
}
}
bool CommandLineInterface::actOnInput()
{
if (m_options.input.mode == InputMode::StandardJson || m_options.input.mode == InputMode::Assembler)
// Already done in "processInput" phase.
return true;
else if (m_options.input.mode == InputMode::Linker)
writeLinkedFiles();
else
{
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
outputCompilationResults();
}
return !m_outputFailed;
}
bool CommandLineInterface::link()
{
solAssert(m_options.input.mode == InputMode::Linker, "");
@ -1013,35 +1042,53 @@ bool CommandLineInterface::assemble(yul::AssemblyStack::Language _language, yul:
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() << stack.print() << endl;
}
if (_language != yul::AssemblyStack::Language::Ewasm && _targetMachine == yul::AssemblyStack::Machine::Ewasm)
{
stack.translate(yul::AssemblyStack::Language::Ewasm);
stack.optimize();
if (m_options.compiler.outputs.ewasmIR)
{
sout() << endl << "==========================" << endl;
sout() << endl << "Translated source:" << endl;
sout() << stack.print() << endl;
}
}
yul::MachineAssemblyObject object;
object = stack.assemble(_targetMachine);
object.bytecode->link(m_options.linker.libraries);
if (m_options.compiler.outputs.binary)
{
sout() << endl << "Binary representation:" << endl;
if (object.bytecode)
sout() << object.bytecode->toHex() << endl;
else
serr() << "No binary representation found." << endl;
}
solAssert(_targetMachine == yul::AssemblyStack::Machine::Ewasm || _targetMachine == yul::AssemblyStack::Machine::EVM, "");
if (
(_targetMachine == yul::AssemblyStack::Machine::EVM && m_options.compiler.outputs.asm_) ||
(_targetMachine == yul::AssemblyStack::Machine::Ewasm && m_options.compiler.outputs.ewasm)
)
{
sout() << endl << "Text representation:" << endl;
if (!object.assembly.empty())
sout() << object.assembly << endl;
else
serr() << "No text representation found." << endl;
}
}
return true;
}

View File

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

View File

@ -16,8 +16,6 @@
*/
// SPDX-License-Identifier: GPL-3.0
#include "license.h"
#include <solc/CommandLineParser.h>
#include <libyul/optimiser/Suite.h>
#include <liblangutil/EVMVersion.h>
@ -36,19 +34,12 @@ namespace po = boost::program_options;
namespace solidity::frontend
{
ostream& CommandLineParser::sout()
{
m_hasOutput = true;
return m_sout;
}
ostream& CommandLineParser::serr()
{
m_hasOutput = true;
return m_serr;
}
#define cout
#define cerr
static string const g_strAllowPaths = "allow-paths";
@ -147,26 +138,6 @@ static map<InputMode, string> const g_inputModeName = {
{InputMode::Linker, "linker"},
};
void CommandLineParser::printVersionAndExit()
{
sout() <<
"solc, the solidity compiler commandline interface" <<
endl <<
"Version: " <<
solidity::frontend::VersionString <<
endl;
exit(EXIT_SUCCESS);
}
void CommandLineParser::printLicenseAndExit()
{
sout() << otherLicenses << endl;
// This is a static variable generated by cmake from LICENSE.txt
sout() << licenseText << endl;
exit(EXIT_SUCCESS);
}
bool CommandLineParser::checkMutuallyExclusive(vector<string> const& _optionNames)
{
if (countEnabledOptions(_optionNames) > 1)
@ -297,11 +268,11 @@ OptimiserSettings CommandLineOptions::optimiserSettings() const
return settings;
}
bool CommandLineParser::parse(int _argc, char const* const* _argv, bool _interactiveTerminal)
bool CommandLineParser::parse(int _argc, char const* const* _argv)
{
m_hasOutput = false;
if (!parseArgs(_argc, _argv, _interactiveTerminal))
if (!parseArgs(_argc, _argv))
return false;
return processArgs();
@ -463,17 +434,30 @@ bool CommandLineParser::parseOutputSelection()
{
static auto outputSupported = [](InputMode _mode, string_view _outputName)
{
static set<string> const compilerModeOutputs =
static set<string> const compilerModeOutputs = (
CompilerOutputs::componentMap() |
ranges::views::keys |
ranges::to<set>();
ranges::to<set>()
) - set<string>{CompilerOutputs::componentName(&CompilerOutputs::ewasmIR)};
static set<string> const assemblerModeOutputs = {
CompilerOutputs::componentName(&CompilerOutputs::asm_),
CompilerOutputs::componentName(&CompilerOutputs::binary),
CompilerOutputs::componentName(&CompilerOutputs::irOptimized),
CompilerOutputs::componentName(&CompilerOutputs::ewasm),
CompilerOutputs::componentName(&CompilerOutputs::ewasmIR),
};
switch (_mode)
{
case InputMode::Help:
case InputMode::License:
case InputMode::Version:
solAssert(false);
case InputMode::Compiler:
case InputMode::CompilerWithASTImport:
return contains(compilerModeOutputs, _outputName);
case InputMode::Assembler:
return contains(assemblerModeOutputs, _outputName);
case InputMode::StandardJson:
case InputMode::Linker:
return false;
@ -485,6 +469,17 @@ bool CommandLineParser::parseOutputSelection()
for (auto&& [optionName, outputComponent]: CompilerOutputs::componentMap())
m_options.compiler.outputs.*outputComponent = (m_args.count(optionName) > 0);
if (m_options.input.mode == InputMode::Assembler && m_options.compiler.outputs == CompilerOutputs{})
{
// In assembly mode keep the default outputs enabled for backwards-compatibility.
// TODO: Remove this (must be done in a breaking release).
m_options.compiler.outputs.asm_ = true;
m_options.compiler.outputs.binary = true;
m_options.compiler.outputs.irOptimized = true;
m_options.compiler.outputs.ewasm = true;
m_options.compiler.outputs.ewasmIR = true;
}
vector<string> unsupportedOutputs;
for (auto&& [optionName, outputComponent]: CompilerOutputs::componentMap())
if (m_options.compiler.outputs.*outputComponent && !outputSupported(m_options.input.mode, optionName))
@ -705,6 +700,7 @@ General Information)").c_str(),
(CompilerOutputs::componentName(&CompilerOutputs::ir).c_str(), "Intermediate Representation (IR) of all contracts (EXPERIMENTAL).")
(CompilerOutputs::componentName(&CompilerOutputs::irOptimized).c_str(), "Optimized intermediate Representation (IR) of all contracts (EXPERIMENTAL).")
(CompilerOutputs::componentName(&CompilerOutputs::ewasm).c_str(), "Ewasm text representation of all contracts (EXPERIMENTAL).")
(CompilerOutputs::componentName(&CompilerOutputs::ewasmIR).c_str(), "Intermediate representation (IR) converted to a form that can be translated directly into Ewasm text representation (EXPERIMENTAL).")
(CompilerOutputs::componentName(&CompilerOutputs::signatureHashes).c_str(), "Function signature hashes of the contracts.")
(CompilerOutputs::componentName(&CompilerOutputs::natspecUser).c_str(), "Natspec user documentation of all contracts.")
(CompilerOutputs::componentName(&CompilerOutputs::natspecDev).c_str(), "Natspec developer documentation of all contracts.")
@ -835,7 +831,7 @@ po::positional_options_description CommandLineParser::positionalOptionsDescripti
return filesPositions;
}
bool CommandLineParser::parseArgs(int _argc, char const* const* _argv, bool _interactiveTerminal)
bool CommandLineParser::parseArgs(int _argc, char const* const* _argv)
{
po::options_description allOptions = optionsDescription();
po::positional_options_description filesPositions = positionalOptionsDescription();
@ -854,18 +850,6 @@ bool CommandLineParser::parseArgs(int _argc, char const* const* _argv, bool _int
return false;
}
if (m_args.count(g_strHelp) || (_interactiveTerminal && _argc == 1))
{
sout() << allOptions;
return false;
}
if (m_args.count(g_strVersion))
printVersionAndExit();
if (m_args.count(g_strLicense))
printLicenseAndExit();
po::notify(m_args);
return true;
@ -874,6 +858,9 @@ bool CommandLineParser::parseArgs(int _argc, char const* const* _argv, bool _int
bool CommandLineParser::processArgs()
{
if (!checkMutuallyExclusive({
g_strHelp,
g_strLicense,
g_strVersion,
g_strStandardJSON,
g_strLink,
g_strAssemble,
@ -883,7 +870,13 @@ bool CommandLineParser::processArgs()
}))
return false;
if (m_args.count(g_strStandardJSON) > 0)
if (m_args.count(g_strHelp) > 0)
m_options.input.mode = InputMode::Help;
else if (m_args.count(g_strLicense) > 0)
m_options.input.mode = InputMode::License;
else if (m_args.count(g_strVersion) > 0)
m_options.input.mode = InputMode::Version;
else if (m_args.count(g_strStandardJSON) > 0)
m_options.input.mode = InputMode::StandardJson;
else if (m_args.count(g_strAssemble) > 0 || m_args.count(g_strStrictAssembly) > 0 || m_args.count(g_strYul) > 0)
m_options.input.mode = InputMode::Assembler;
@ -894,6 +887,13 @@ bool CommandLineParser::processArgs()
else
m_options.input.mode = InputMode::Compiler;
if (
m_options.input.mode == InputMode::Help ||
m_options.input.mode == InputMode::License ||
m_options.input.mode == InputMode::Version
)
return true;
map<string, set<InputMode>> validOptionInputModeCombinations = {
// TODO: This should eventually contain all options.
{g_strErrorRecovery, {InputMode::Compiler, InputMode::CompilerWithASTImport}},
@ -915,11 +915,12 @@ bool CommandLineParser::processArgs()
if (!checkMutuallyExclusive({g_strColor, g_strNoColor}))
return false;
array<string, 8> const conflictingWithStopAfter{
array<string, 9> const conflictingWithStopAfter{
CompilerOutputs::componentName(&CompilerOutputs::binary),
CompilerOutputs::componentName(&CompilerOutputs::ir),
CompilerOutputs::componentName(&CompilerOutputs::irOptimized),
CompilerOutputs::componentName(&CompilerOutputs::ewasm),
CompilerOutputs::componentName(&CompilerOutputs::ewasmIR),
g_strGas,
CompilerOutputs::componentName(&CompilerOutputs::asm_),
CompilerOutputs::componentName(&CompilerOutputs::asmJson),

View File

@ -48,6 +48,9 @@ namespace solidity::frontend
enum class InputMode
{
Help,
License,
Version,
Compiler,
CompilerWithASTImport,
StandardJson,
@ -75,6 +78,7 @@ struct CompilerOutputs
{"ir", &CompilerOutputs::ir},
{"ir-optimized", &CompilerOutputs::irOptimized},
{"ewasm", &CompilerOutputs::ewasm},
{"ewasm-ir", &CompilerOutputs::ewasmIR},
{"hashes", &CompilerOutputs::signatureHashes},
{"userdoc", &CompilerOutputs::natspecUser},
{"devdoc", &CompilerOutputs::natspecDev},
@ -94,6 +98,7 @@ struct CompilerOutputs
bool ir = false;
bool irOptimized = false;
bool ewasm = false;
bool ewasmIR = false;
bool signatureHashes = false;
bool natspecUser = false;
bool natspecDev = false;
@ -230,34 +235,28 @@ struct CommandLineOptions
/// Parses the command-line arguments and produces a filled-out CommandLineOptions structure.
/// Validates provided values and prints error messages in case of errors.
///
/// The class is also responsible for handling options that only result in printing informational
/// text, without the need to invoke the compiler - printing usage banner, version or license.
class CommandLineParser
{
public:
explicit CommandLineParser(std::ostream& _sout, std::ostream& _serr):
m_sout(_sout),
explicit CommandLineParser(std::ostream& _serr):
m_serr(_serr)
{}
/// Parses the command-line arguments and fills out the internal CommandLineOptions structure.
/// Performs validation and prints error messages. If requested, prints usage banner, version
/// or license.
/// @param interactiveTerminal specifies whether the terminal is taking input from the user.
/// This is used to determine whether to provide more user-friendly output in some situations.
/// E.g. whether to print help text when no arguments are provided.
/// Performs validation and prints error messages.
/// @return true if there were no validation errors when parsing options and the
/// CommandLineOptions structure has been fully initialized. false if there were errors - in
/// this case CommandLineOptions may be only partially filled out. May also return false if
/// there is not further processing necessary and the program should just exit.
bool parse(int _argc, char const* const* _argv, bool _interactiveTerminal);
bool parse(int _argc, char const* const* _argv);
CommandLineOptions const& options() const { return m_options; }
/// Returns true if the parser has written anything to any of its output streams.
bool hasOutput() const { return m_hasOutput; }
static void printHelp(std::ostream& _out) { _out << optionsDescription(); }
private:
/// @returns a specification of all named command-line options accepted by the compiler.
/// The object can be used to parse command-line arguments or to generate the help screen.
@ -270,7 +269,7 @@ private:
/// Uses boost::program_options to parse the command-line arguments and leaves the result in @a m_args.
/// Also handles the arguments that result in information being printed followed by immediate exit.
/// @returns false if parsing fails due to syntactical errors or the arguments not matching the description.
bool parseArgs(int _argc, char const* const* _argv, bool _interactiveTerminal);
bool parseArgs(int _argc, char const* const* _argv);
/// Validates parsed arguments stored in @a m_args and fills out the internal CommandLineOptions
/// structure.
@ -294,20 +293,13 @@ private:
bool parseOutputSelection();
bool checkMutuallyExclusive(std::vector<std::string> const& _optionNames);
[[noreturn]] void printVersionAndExit();
[[noreturn]] void printLicenseAndExit();
size_t countEnabledOptions(std::vector<std::string> const& _optionNames) const;
static std::string joinOptionNames(std::vector<std::string> const& _optionNames, std::string _separator = ", ");
/// Returns the stream that should receive normal output. Sets m_hasOutput to true if the
/// stream has ever been used.
std::ostream& sout();
/// Returns the stream that should receive error output. Sets m_hasOutput to true if the
/// stream has ever been used.
std::ostream& serr();
std::ostream& m_sout;
std::ostream& m_serr;
bool m_hasOutput = false;

View File

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

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.
# An failure is expected.
function test_solc_behaviour()
function test_solc_behaviour
{
local filename="${1}"
local solc_args
@ -288,7 +288,7 @@ EOF
}
function test_solc_assembly_output()
function test_solc_assembly_output
{
local input="${1}"
local expected="${2}"

View File

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

View File

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

View File

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

View File

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

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

View File

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

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