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