Merge pull request #9805 from ethereum/develop

Merge develop into breaking.
This commit is contained in:
chriseth 2020-09-14 20:48:03 +02:00 committed by GitHub
commit efe829b4b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
538 changed files with 3806 additions and 1960 deletions

View File

@ -9,23 +9,23 @@ version: 2.1
parameters: parameters:
ubuntu-1804-docker-image: ubuntu-1804-docker-image:
type: string type: string
# solbuildpackpusher/solidity-buildpack-deps:ubuntu1804-2 # solbuildpackpusher/solidity-buildpack-deps:ubuntu1804-3
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:9ab317e583c395e50884ba82e9f99811c374344cea4c550725be8ec836e07acc" default: "solbuildpackpusher/solidity-buildpack-deps@sha256:19f613d2ac47fedff654dacef984d8a64726c4d67ae8f2667a85ee7d97ac4c1c"
ubuntu-2004-docker-image: ubuntu-2004-docker-image:
type: string type: string
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-2 # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-3
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:cbfa42d8ecbe94391ba8837e218869242666de7a0da6ccac065a856c85b6a6a0" default: "solbuildpackpusher/solidity-buildpack-deps@sha256:aeedbe7390a7383815f0cf0f8a1b8bf84dc5e334a3b0043ebcdf8b1bdbe80a81"
ubuntu-2004-clang-docker-image: ubuntu-2004-clang-docker-image:
type: string type: string
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-2 # solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-3
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:7a4d5271b5552139d9f2caefc50d42f401bf74132cf8f253e199e11c80ab42de" default: "solbuildpackpusher/solidity-buildpack-deps@sha256:2593c15689dee5b5bdfff96a36c8c68a468cd3b147c41f75b820b8fabc257be9"
ubuntu-1604-clang-ossfuzz-docker-image: ubuntu-1604-clang-ossfuzz-docker-image:
type: string type: string
# solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-3 # solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-4
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:6fa6914bd81abcac4b162c738e6ff05d87cefe7655e3859c7a827e5a8ec20dc7" default: "solbuildpackpusher/solidity-buildpack-deps@sha256:842126b164b3542f05bff2611459e21edc7e3e2c81ca9d1f43396c8cf066f7ca"
emscripten-docker-image: emscripten-docker-image:
type: string type: string
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:d557d015918c3cf68b0d22839bab41013f0757b651a7fef21595f89721dbebcc" default: "solbuildpackpusher/solidity-buildpack-deps@sha256:23dad3b34deae8107c8551804ef299f6a89c23ed506e8118fac151e2bdc9018c"
defaults: defaults:
@ -208,6 +208,11 @@ defaults:
requires: requires:
- b_ubu_release - b_ubu_release
- workflow_archlinux: &workflow_archlinux
<<: *workflow_trigger_on_tags
requires:
- b_archlinux
- workflow_ubuntu2004_codecov: &workflow_ubuntu2004_codecov - workflow_ubuntu2004_codecov: &workflow_ubuntu2004_codecov
<<: *workflow_trigger_on_tags <<: *workflow_trigger_on_tags
requires: requires:
@ -657,6 +662,25 @@ jobs:
t_ubu_soltest: &t_ubu_soltest t_ubu_soltest: &t_ubu_soltest
<<: *test_ubuntu2004 <<: *test_ubuntu2004
t_archlinux_soltest: &t_archlinux_soltest
docker:
- image: archlinux/base
environment:
EVM: constantinople
OPTIMIZE: 0
TERM: xterm
steps:
- run:
name: Install runtime dependencies
command: |
pacman --noconfirm -Syu --noprogressbar --needed base-devel boost cmake z3 cvc4 git openssh tar
- checkout
- attach_workspace:
at: build
- run: *run_soltest
- store_test_results: *store_test_results
- store_artifacts: *artifacts_test_results
t_ubu_soltest_enforce_yul: &t_ubu_soltest_enforce_yul t_ubu_soltest_enforce_yul: &t_ubu_soltest_enforce_yul
docker: docker:
- image: << pipeline.parameters.ubuntu-2004-docker-image >> - image: << pipeline.parameters.ubuntu-2004-docker-image >>
@ -870,7 +894,6 @@ workflows:
# build-only # build-only
- b_docs: *workflow_trigger_on_tags - b_docs: *workflow_trigger_on_tags
- b_archlinux: *workflow_trigger_on_tags
- b_ubu_cxx20: *workflow_trigger_on_tags - b_ubu_cxx20: *workflow_trigger_on_tags
- b_ubu_ossfuzz: *workflow_trigger_on_tags - b_ubu_ossfuzz: *workflow_trigger_on_tags
@ -879,6 +902,10 @@ workflows:
- t_osx_cli: *workflow_osx - t_osx_cli: *workflow_osx
- t_osx_soltest: *workflow_osx - t_osx_soltest: *workflow_osx
# ArchLinux build and tests
- b_archlinux: *workflow_trigger_on_tags
- t_archlinux_soltest: *workflow_archlinux
# Ubuntu build and tests # Ubuntu build and tests
- b_ubu: *workflow_trigger_on_tags - b_ubu: *workflow_trigger_on_tags
- b_ubu18: *workflow_trigger_on_tags - b_ubu18: *workflow_trigger_on_tags

View File

@ -43,13 +43,13 @@ then
./scripts/install_obsolete_jsoncpp_1_7_4.sh ./scripts/install_obsolete_jsoncpp_1_7_4.sh
# z3 # z3
wget https://github.com/Z3Prover/z3/releases/download/z3-4.8.8/z3-4.8.8-x64-osx-10.14.6.zip wget https://github.com/Z3Prover/z3/releases/download/z3-4.8.9/z3-4.8.9-x64-osx-10.14.6.zip
unzip z3-4.8.8-x64-osx-10.14.6.zip unzip z3-4.8.9-x64-osx-10.14.6.zip
rm -f z3-4.8.8-x64-osx-10.14.6.zip rm -f z3-4.8.9-x64-osx-10.14.6.zip
cp z3-4.8.8-x64-osx-10.14.6/bin/libz3.a /usr/local/lib cp z3-4.8.9-x64-osx-10.14.6/bin/libz3.a /usr/local/lib
cp z3-4.8.8-x64-osx-10.14.6/bin/z3 /usr/local/bin cp z3-4.8.9-x64-osx-10.14.6/bin/z3 /usr/local/bin
cp z3-4.8.8-x64-osx-10.14.6/include/* /usr/local/include cp z3-4.8.9-x64-osx-10.14.6/include/* /usr/local/include
rm -rf z3-4.8.8-x64-osx-10.14.6 rm -rf z3-4.8.9-x64-osx-10.14.6
# evmone # evmone
wget https://github.com/ethereum/evmone/releases/download/v0.4.0/evmone-0.4.0-darwin-x86_64.tar.gz wget https://github.com/ethereum/evmone/releases/download/v0.4.0/evmone-0.4.0-darwin-x86_64.tar.gz

View File

@ -113,7 +113,7 @@ matrix:
before_install: before_install:
- nvm install 10 - nvm install 10
- nvm use 10 - nvm use 10
- docker pull solbuildpackpusher/solidity-buildpack-deps@sha256:d557d015918c3cf68b0d22839bab41013f0757b651a7fef21595f89721dbebcc - docker pull solbuildpackpusher/solidity-buildpack-deps@sha256:23dad3b34deae8107c8551804ef299f6a89c23ed506e8118fac151e2bdc9018c
env: env:
- SOLC_EMSCRIPTEN=On - SOLC_EMSCRIPTEN=On
- SOLC_INSTALL_DEPS_TRAVIS=Off - SOLC_INSTALL_DEPS_TRAVIS=Off

View File

@ -8,12 +8,19 @@ Language Features:
Compiler Features: Compiler Features:
* SMTChecker: Support shifts.
* SMTChecker: Support structs. * SMTChecker: Support structs.
* SMTChecker: Support ``type(T).min`` and ``type(T).max``.
* Yul Optimizer: Prune unused parameters in functions. * Yul Optimizer: Prune unused parameters in functions.
* Yul Optimizer: Inline into functions further down in the call graph first.
* Yul Optimizer: Try to simplify function names.
Bugfixes: Bugfixes:
* Type Checker: Disallow ``virtual`` for modifiers in libraries. * Type Checker: Disallow ``virtual`` for modifiers in libraries.
* ViewPureChecker: Prevent visibility check on constructors. * ViewPureChecker: Prevent visibility check on constructors.
* Type system: Fix internal error on implicit conversion of contract instance to the type of its ``super``.
* Type system: Fix named parameters in overloaded function and event calls being matched incorrectly if the order differs from the declaration.
### 0.7.1 (2020-09-02) ### 0.7.1 (2020-09-02)
@ -43,6 +50,7 @@ Bugfixes:
* SMTChecker: Fix internal error on lvalue unary operators with tuples. * SMTChecker: Fix internal error on lvalue unary operators with tuples.
* SMTChecker: Fix internal error on tuple assignment. * SMTChecker: Fix internal error on tuple assignment.
* SMTChecker: Fix internal error on tuples of one element that have tuple type. * SMTChecker: Fix internal error on tuples of one element that have tuple type.
* SMTChecker: Fix internal error when using imported code.
* SMTChecker: Fix soundness of array ``pop``. * SMTChecker: Fix soundness of array ``pop``.
* Type Checker: Disallow ``using for`` directive inside interfaces. * Type Checker: Disallow ``using for`` directive inside interfaces.
* Type Checker: Disallow signed literals as exponent in exponentiation operator. * Type Checker: Disallow signed literals as exponent in exponentiation operator.

View File

@ -19,7 +19,7 @@ Solidity also supports exception handling in the form of ``try``/``catch``-state
but only for :ref:`external function calls <external-function-calls>` and but only for :ref:`external function calls <external-function-calls>` and
contract creation calls. contract creation calls.
Parentheses can *not* be omitted for conditionals, but curly brances can be omitted Parentheses can *not* be omitted for conditionals, but curly braces can be omitted
around single-statement bodies. around single-statement bodies.
Note that there is no type conversion from non-boolean to boolean types as Note that there is no type conversion from non-boolean to boolean types as

View File

@ -61,12 +61,11 @@ For a contract that fulfils payments, the signed message must include:
2. The amount to be transferred. 2. The amount to be transferred.
3. Protection against replay attacks. 3. Protection against replay attacks.
A replay attack is when a signed message is reused to claim authorization for A replay attack is when a signed message is reused to claim
a second action. authorization for a second action. To avoid replay attacks
To avoid replay attacks we use the same as in Ethereum transactions we use the same technique as in Ethereum transactions themselves,
themselves, a so-called nonce, which is the number of transactions sent by an a so-called nonce, which is the number of transactions sent by
account. an account. The smart contract checks if a nonce is used multiple times.
The smart contract checks if a nonce is used multiple times.
Another type of replay attack can occur when the owner Another type of replay attack can occur when the owner
deploys a ``ReceiverPays`` smart contract, makes some deploys a ``ReceiverPays`` smart contract, makes some

View File

@ -243,7 +243,7 @@ Since variables are stored on the stack, they do not directly
influence memory or storage, but they can be used as pointers influence memory or storage, but they can be used as pointers
to memory or storage locations in the built-in functions to memory or storage locations in the built-in functions
``mstore``, ``mload``, ``sstore`` and ``sload``. ``mstore``, ``mload``, ``sstore`` and ``sload``.
Future dialects migh introduce specific types for such pointers. Future dialects might introduce specific types for such pointers.
When a variable is referenced, its current value is copied. When a variable is referenced, its current value is copied.
For the EVM, this translates to a ``DUP`` instruction. For the EVM, this translates to a ``DUP`` instruction.

View File

@ -71,51 +71,51 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1(
{ {
using Word = typename Pattern::Word; using Word = typename Pattern::Word;
using Builtins = typename Pattern::Builtins; using Builtins = typename Pattern::Builtins;
return std::vector<SimplificationRule<Pattern>> { return std::vector<SimplificationRule<Pattern>>{
// arithmetic on constants // arithmetic on constants
{Builtins::ADD(A, B), [=]{ return A.d() + B.d(); }, false}, {Builtins::ADD(A, B), [=]{ return A.d() + B.d(); }},
{Builtins::MUL(A, B), [=]{ return A.d() * B.d(); }, false}, {Builtins::MUL(A, B), [=]{ return A.d() * B.d(); }},
{Builtins::SUB(A, B), [=]{ return A.d() - B.d(); }, false}, {Builtins::SUB(A, B), [=]{ return A.d() - B.d(); }},
{Builtins::DIV(A, B), [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }, false}, {Builtins::DIV(A, B), [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }},
{Builtins::SDIV(A, B), [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }, false}, {Builtins::SDIV(A, B), [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }},
{Builtins::MOD(A, B), [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }, false}, {Builtins::MOD(A, B), [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }},
{Builtins::SMOD(A, B), [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }, false}, {Builtins::SMOD(A, B), [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }},
{Builtins::EXP(A, B), [=]{ return Word(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << Pattern::WordSize)); }, false}, {Builtins::EXP(A, B), [=]{ return Word(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << Pattern::WordSize)); }},
{Builtins::NOT(A), [=]{ return ~A.d(); }, false}, {Builtins::NOT(A), [=]{ return ~A.d(); }},
{Builtins::LT(A, B), [=]() -> Word { return A.d() < B.d() ? 1 : 0; }, false}, {Builtins::LT(A, B), [=]() -> Word { return A.d() < B.d() ? 1 : 0; }},
{Builtins::GT(A, B), [=]() -> Word { return A.d() > B.d() ? 1 : 0; }, false}, {Builtins::GT(A, B), [=]() -> Word { return A.d() > B.d() ? 1 : 0; }},
{Builtins::SLT(A, B), [=]() -> Word { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }, false}, {Builtins::SLT(A, B), [=]() -> Word { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }},
{Builtins::SGT(A, B), [=]() -> Word { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }, false}, {Builtins::SGT(A, B), [=]() -> Word { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }},
{Builtins::EQ(A, B), [=]() -> Word { return A.d() == B.d() ? 1 : 0; }, false}, {Builtins::EQ(A, B), [=]() -> Word { return A.d() == B.d() ? 1 : 0; }},
{Builtins::ISZERO(A), [=]() -> Word { return A.d() == 0 ? 1 : 0; }, false}, {Builtins::ISZERO(A), [=]() -> Word { return A.d() == 0 ? 1 : 0; }},
{Builtins::AND(A, B), [=]{ return A.d() & B.d(); }, false}, {Builtins::AND(A, B), [=]{ return A.d() & B.d(); }},
{Builtins::OR(A, B), [=]{ return A.d() | B.d(); }, false}, {Builtins::OR(A, B), [=]{ return A.d() | B.d(); }},
{Builtins::XOR(A, B), [=]{ return A.d() ^ B.d(); }, false}, {Builtins::XOR(A, B), [=]{ return A.d() ^ B.d(); }},
{Builtins::BYTE(A, B), [=]{ {Builtins::BYTE(A, B), [=]{
return return
A.d() >= Pattern::WordSize / 8 ? A.d() >= Pattern::WordSize / 8 ?
0 : 0 :
(B.d() >> unsigned(8 * (Pattern::WordSize / 8 - 1 - A.d()))) & 0xff; (B.d() >> unsigned(8 * (Pattern::WordSize / 8 - 1 - A.d()))) & 0xff;
}, false}, }},
{Builtins::ADDMOD(A, B, C), [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) + bigint(B.d())) % C.d()); }, false}, {Builtins::ADDMOD(A, B, C), [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) + bigint(B.d())) % C.d()); }},
{Builtins::MULMOD(A, B, C), [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) * bigint(B.d())) % C.d()); }, false}, {Builtins::MULMOD(A, B, C), [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) * bigint(B.d())) % C.d()); }},
{Builtins::SIGNEXTEND(A, B), [=]() -> Word { {Builtins::SIGNEXTEND(A, B), [=]() -> Word {
if (A.d() >= Pattern::WordSize / 8 - 1) if (A.d() >= Pattern::WordSize / 8 - 1)
return B.d(); return B.d();
unsigned testBit = unsigned(A.d()) * 8 + 7; unsigned testBit = unsigned(A.d()) * 8 + 7;
Word mask = (Word(1) << testBit) - 1; Word mask = (Word(1) << testBit) - 1;
return boost::multiprecision::bit_test(B.d(), testBit) ? B.d() | ~mask : B.d() & mask; return boost::multiprecision::bit_test(B.d(), testBit) ? B.d() | ~mask : B.d() & mask;
}, false}, }},
{Builtins::SHL(A, B), [=]{ {Builtins::SHL(A, B), [=]{
if (A.d() >= Pattern::WordSize) if (A.d() >= Pattern::WordSize)
return Word(0); return Word(0);
return shlWorkaround(B.d(), unsigned(A.d())); return shlWorkaround(B.d(), unsigned(A.d()));
}, false}, }},
{Builtins::SHR(A, B), [=]{ {Builtins::SHR(A, B), [=]{
if (A.d() >= Pattern::WordSize) if (A.d() >= Pattern::WordSize)
return Word(0); return Word(0);
return B.d() >> unsigned(A.d()); return B.d() >> unsigned(A.d());
}, false} }}
}; };
} }
@ -133,48 +133,48 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2(
using Builtins = typename Pattern::Builtins; using Builtins = typename Pattern::Builtins;
return std::vector<SimplificationRule<Pattern>> { return std::vector<SimplificationRule<Pattern>> {
// invariants involving known constants // invariants involving known constants
{Builtins::ADD(X, 0), [=]{ return X; }, false}, {Builtins::ADD(X, 0), [=]{ return X; }},
{Builtins::ADD(0, X), [=]{ return X; }, false}, {Builtins::ADD(0, X), [=]{ return X; }},
{Builtins::SUB(X, 0), [=]{ return X; }, false}, {Builtins::SUB(X, 0), [=]{ return X; }},
{Builtins::SUB(~Word(0), X), [=]() -> Pattern { return Builtins::NOT(X); }, false}, {Builtins::SUB(~Word(0), X), [=]() -> Pattern { return Builtins::NOT(X); }},
{Builtins::MUL(X, 0), [=]{ return Word(0); }, true}, {Builtins::MUL(X, 0), [=]{ return Word(0); }},
{Builtins::MUL(0, X), [=]{ return Word(0); }, true}, {Builtins::MUL(0, X), [=]{ return Word(0); }},
{Builtins::MUL(X, 1), [=]{ return X; }, false}, {Builtins::MUL(X, 1), [=]{ return X; }},
{Builtins::MUL(1, X), [=]{ return X; }, false}, {Builtins::MUL(1, X), [=]{ return X; }},
{Builtins::MUL(X, Word(-1)), [=]() -> Pattern { return Builtins::SUB(0, X); }, false}, {Builtins::MUL(X, Word(-1)), [=]() -> Pattern { return Builtins::SUB(0, X); }},
{Builtins::MUL(Word(-1), X), [=]() -> Pattern { return Builtins::SUB(0, X); }, false}, {Builtins::MUL(Word(-1), X), [=]() -> Pattern { return Builtins::SUB(0, X); }},
{Builtins::DIV(X, 0), [=]{ return Word(0); }, true}, {Builtins::DIV(X, 0), [=]{ return Word(0); }},
{Builtins::DIV(0, X), [=]{ return Word(0); }, true}, {Builtins::DIV(0, X), [=]{ return Word(0); }},
{Builtins::DIV(X, 1), [=]{ return X; }, false}, {Builtins::DIV(X, 1), [=]{ return X; }},
{Builtins::SDIV(X, 0), [=]{ return Word(0); }, true}, {Builtins::SDIV(X, 0), [=]{ return Word(0); }},
{Builtins::SDIV(0, X), [=]{ return Word(0); }, true}, {Builtins::SDIV(0, X), [=]{ return Word(0); }},
{Builtins::SDIV(X, 1), [=]{ return X; }, false}, {Builtins::SDIV(X, 1), [=]{ return X; }},
{Builtins::AND(X, ~Word(0)), [=]{ return X; }, false}, {Builtins::AND(X, ~Word(0)), [=]{ return X; }},
{Builtins::AND(~Word(0), X), [=]{ return X; }, false}, {Builtins::AND(~Word(0), X), [=]{ return X; }},
{Builtins::AND(X, 0), [=]{ return Word(0); }, true}, {Builtins::AND(X, 0), [=]{ return Word(0); }},
{Builtins::AND(0, X), [=]{ return Word(0); }, true}, {Builtins::AND(0, X), [=]{ return Word(0); }},
{Builtins::OR(X, 0), [=]{ return X; }, false}, {Builtins::OR(X, 0), [=]{ return X; }},
{Builtins::OR(0, X), [=]{ return X; }, false}, {Builtins::OR(0, X), [=]{ return X; }},
{Builtins::OR(X, ~Word(0)), [=]{ return ~Word(0); }, true}, {Builtins::OR(X, ~Word(0)), [=]{ return ~Word(0); }},
{Builtins::OR(~Word(0), X), [=]{ return ~Word(0); }, true}, {Builtins::OR(~Word(0), X), [=]{ return ~Word(0); }},
{Builtins::XOR(X, 0), [=]{ return X; }, false}, {Builtins::XOR(X, 0), [=]{ return X; }},
{Builtins::XOR(0, X), [=]{ return X; }, false}, {Builtins::XOR(0, X), [=]{ return X; }},
{Builtins::MOD(X, 0), [=]{ return Word(0); }, true}, {Builtins::MOD(X, 0), [=]{ return Word(0); }},
{Builtins::MOD(0, X), [=]{ return Word(0); }, true}, {Builtins::MOD(0, X), [=]{ return Word(0); }},
{Builtins::EQ(X, 0), [=]() -> Pattern { return Builtins::ISZERO(X); }, false }, {Builtins::EQ(X, 0), [=]() -> Pattern { return Builtins::ISZERO(X); },},
{Builtins::EQ(0, X), [=]() -> Pattern { return Builtins::ISZERO(X); }, false }, {Builtins::EQ(0, X), [=]() -> Pattern { return Builtins::ISZERO(X); },},
{Builtins::SHL(0, X), [=]{ return X; }, false}, {Builtins::SHL(0, X), [=]{ return X; }},
{Builtins::SHR(0, X), [=]{ return X; }, false}, {Builtins::SHR(0, X), [=]{ return X; }},
{Builtins::SHL(X, 0), [=]{ return Word(0); }, true}, {Builtins::SHL(X, 0), [=]{ return Word(0); }},
{Builtins::SHR(X, 0), [=]{ return Word(0); }, true}, {Builtins::SHR(X, 0), [=]{ return Word(0); }},
{Builtins::GT(X, 0), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }, false}, {Builtins::GT(X, 0), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }},
{Builtins::LT(0, X), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }, false}, {Builtins::LT(0, X), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }},
{Builtins::GT(X, ~Word(0)), [=]{ return Word(0); }, true}, {Builtins::GT(X, ~Word(0)), [=]{ return Word(0); }},
{Builtins::LT(~Word(0), X), [=]{ return Word(0); }, true}, {Builtins::LT(~Word(0), X), [=]{ return Word(0); }},
{Builtins::GT(0, X), [=]{ return Word(0); }, true}, {Builtins::GT(0, X), [=]{ return Word(0); }},
{Builtins::LT(X, 0), [=]{ return Word(0); }, true}, {Builtins::LT(X, 0), [=]{ return Word(0); }},
{Builtins::AND(Builtins::BYTE(X, Y), Word(0xff)), [=]() -> Pattern { return Builtins::BYTE(X, Y); }, false}, {Builtins::AND(Builtins::BYTE(X, Y), Word(0xff)), [=]() -> Pattern { return Builtins::BYTE(X, Y); }},
{Builtins::BYTE(Word(Pattern::WordSize / 8 - 1), X), [=]() -> Pattern { return Builtins::AND(X, Word(0xff)); }, false} {Builtins::BYTE(Word(Pattern::WordSize / 8 - 1), X), [=]() -> Pattern { return Builtins::AND(X, Word(0xff)); }},
}; };
} }
@ -191,16 +191,16 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart3(
using Builtins = typename Pattern::Builtins; using Builtins = typename Pattern::Builtins;
return std::vector<SimplificationRule<Pattern>> { return std::vector<SimplificationRule<Pattern>> {
// operations involving an expression and itself // operations involving an expression and itself
{Builtins::AND(X, X), [=]{ return X; }, true}, {Builtins::AND(X, X), [=]{ return X; }},
{Builtins::OR(X, X), [=]{ return X; }, true}, {Builtins::OR(X, X), [=]{ return X; }},
{Builtins::XOR(X, X), [=]{ return Word(0); }, true}, {Builtins::XOR(X, X), [=]{ return Word(0); }},
{Builtins::SUB(X, X), [=]{ return Word(0); }, true}, {Builtins::SUB(X, X), [=]{ return Word(0); }},
{Builtins::EQ(X, X), [=]{ return Word(1); }, true}, {Builtins::EQ(X, X), [=]{ return Word(1); }},
{Builtins::LT(X, X), [=]{ return Word(0); }, true}, {Builtins::LT(X, X), [=]{ return Word(0); }},
{Builtins::SLT(X, X), [=]{ return Word(0); }, true}, {Builtins::SLT(X, X), [=]{ return Word(0); }},
{Builtins::GT(X, X), [=]{ return Word(0); }, true}, {Builtins::GT(X, X), [=]{ return Word(0); }},
{Builtins::SGT(X, X), [=]{ return Word(0); }, true}, {Builtins::SGT(X, X), [=]{ return Word(0); }},
{Builtins::MOD(X, X), [=]{ return Word(0); }, true} {Builtins::MOD(X, X), [=]{ return Word(0); }}
}; };
} }
@ -217,23 +217,23 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart4(
using Builtins = typename Pattern::Builtins; using Builtins = typename Pattern::Builtins;
return std::vector<SimplificationRule<Pattern>> { return std::vector<SimplificationRule<Pattern>> {
// logical instruction combinations // logical instruction combinations
{Builtins::NOT(Builtins::NOT(X)), [=]{ return X; }, false}, {Builtins::NOT(Builtins::NOT(X)), [=]{ return X; }},
{Builtins::XOR(X, Builtins::XOR(X, Y)), [=]{ return Y; }, true}, {Builtins::XOR(X, Builtins::XOR(X, Y)), [=]{ return Y; }},
{Builtins::XOR(X, Builtins::XOR(Y, X)), [=]{ return Y; }, true}, {Builtins::XOR(X, Builtins::XOR(Y, X)), [=]{ return Y; }},
{Builtins::XOR(Builtins::XOR(X, Y), X), [=]{ return Y; }, true}, {Builtins::XOR(Builtins::XOR(X, Y), X), [=]{ return Y; }},
{Builtins::XOR(Builtins::XOR(Y, X), X), [=]{ return Y; }, true}, {Builtins::XOR(Builtins::XOR(Y, X), X), [=]{ return Y; }},
{Builtins::OR(X, Builtins::AND(X, Y)), [=]{ return X; }, true}, {Builtins::OR(X, Builtins::AND(X, Y)), [=]{ return X; }},
{Builtins::OR(X, Builtins::AND(Y, X)), [=]{ return X; }, true}, {Builtins::OR(X, Builtins::AND(Y, X)), [=]{ return X; }},
{Builtins::OR(Builtins::AND(X, Y), X), [=]{ return X; }, true}, {Builtins::OR(Builtins::AND(X, Y), X), [=]{ return X; }},
{Builtins::OR(Builtins::AND(Y, X), X), [=]{ return X; }, true}, {Builtins::OR(Builtins::AND(Y, X), X), [=]{ return X; }},
{Builtins::AND(X, Builtins::OR(X, Y)), [=]{ return X; }, true}, {Builtins::AND(X, Builtins::OR(X, Y)), [=]{ return X; }},
{Builtins::AND(X, Builtins::OR(Y, X)), [=]{ return X; }, true}, {Builtins::AND(X, Builtins::OR(Y, X)), [=]{ return X; }},
{Builtins::AND(Builtins::OR(X, Y), X), [=]{ return X; }, true}, {Builtins::AND(Builtins::OR(X, Y), X), [=]{ return X; }},
{Builtins::AND(Builtins::OR(Y, X), X), [=]{ return X; }, true}, {Builtins::AND(Builtins::OR(Y, X), X), [=]{ return X; }},
{Builtins::AND(X, Builtins::NOT(X)), [=]{ return Word(0); }, true}, {Builtins::AND(X, Builtins::NOT(X)), [=]{ return Word(0); }},
{Builtins::AND(Builtins::NOT(X), X), [=]{ return Word(0); }, true}, {Builtins::AND(Builtins::NOT(X), X), [=]{ return Word(0); }},
{Builtins::OR(X, Builtins::NOT(X)), [=]{ return ~Word(0); }, true}, {Builtins::OR(X, Builtins::NOT(X)), [=]{ return ~Word(0); }},
{Builtins::OR(Builtins::NOT(X), X), [=]{ return ~Word(0); }, true}, {Builtins::OR(Builtins::NOT(X), X), [=]{ return ~Word(0); }},
}; };
} }
@ -249,14 +249,14 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart4_5(
using Builtins = typename Pattern::Builtins; using Builtins = typename Pattern::Builtins;
return std::vector<SimplificationRule<Pattern>>{ return std::vector<SimplificationRule<Pattern>>{
// idempotent operations // idempotent operations
{Builtins::AND(Builtins::AND(X, Y), Y), [=]{ return Builtins::AND(X, Y); }, true}, {Builtins::AND(Builtins::AND(X, Y), Y), [=]{ return Builtins::AND(X, Y); }},
{Builtins::AND(Y, Builtins::AND(X, Y)), [=]{ return Builtins::AND(X, Y); }, true}, {Builtins::AND(Y, Builtins::AND(X, Y)), [=]{ return Builtins::AND(X, Y); }},
{Builtins::AND(Builtins::AND(Y, X), Y), [=]{ return Builtins::AND(Y, X); }, true}, {Builtins::AND(Builtins::AND(Y, X), Y), [=]{ return Builtins::AND(Y, X); }},
{Builtins::AND(Y, Builtins::AND(Y, X)), [=]{ return Builtins::AND(Y, X); }, true}, {Builtins::AND(Y, Builtins::AND(Y, X)), [=]{ return Builtins::AND(Y, X); }},
{Builtins::OR(Builtins::OR(X, Y), Y), [=]{ return Builtins::OR(X, Y); }, true}, {Builtins::OR(Builtins::OR(X, Y), Y), [=]{ return Builtins::OR(X, Y); }},
{Builtins::OR(Y, Builtins::OR(X, Y)), [=]{ return Builtins::OR(X, Y); }, true}, {Builtins::OR(Y, Builtins::OR(X, Y)), [=]{ return Builtins::OR(X, Y); }},
{Builtins::OR(Builtins::OR(Y, X), Y), [=]{ return Builtins::OR(Y, X); }, true}, {Builtins::OR(Builtins::OR(Y, X), Y), [=]{ return Builtins::OR(Y, X); }},
{Builtins::OR(Y, Builtins::OR(Y, X)), [=]{ return Builtins::OR(Y, X); }, true}, {Builtins::OR(Y, Builtins::OR(Y, X)), [=]{ return Builtins::OR(Y, X); }},
}; };
} }
@ -280,8 +280,7 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5(
Word value = Word(1) << i; Word value = Word(1) << i;
rules.push_back({ rules.push_back({
Builtins::MOD(X, value), Builtins::MOD(X, value),
[=]() -> Pattern { return Builtins::AND(X, value - 1); }, [=]() -> Pattern { return Builtins::AND(X, value - 1); }
false
}); });
} }
@ -289,7 +288,6 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5(
rules.push_back({ rules.push_back({
Builtins::SHL(A, X), Builtins::SHL(A, X),
[=]() -> Pattern { return Word(0); }, [=]() -> Pattern { return Word(0); },
true,
[=]() { return A.d() >= Pattern::WordSize; } [=]() { return A.d() >= Pattern::WordSize; }
}); });
@ -297,7 +295,6 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5(
rules.push_back({ rules.push_back({
Builtins::SHR(A, X), Builtins::SHR(A, X),
[=]() -> Pattern { return Word(0); }, [=]() -> Pattern { return Word(0); },
true,
[=]() { return A.d() >= Pattern::WordSize; } [=]() { return A.d() >= Pattern::WordSize; }
}); });
@ -305,7 +302,6 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5(
rules.push_back({ rules.push_back({
Builtins::BYTE(A, X), Builtins::BYTE(A, X),
[=]() -> Pattern { return Word(0); }, [=]() -> Pattern { return Word(0); },
true,
[=]() { return A.d() >= Pattern::WordSize / 8; } [=]() { return A.d() >= Pattern::WordSize / 8; }
}); });
@ -320,13 +316,11 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5(
Word const mask = (Word(1) << 160) - 1; Word const mask = (Word(1) << 160) - 1;
rules.push_back({ rules.push_back({
Builtins::AND(Pattern{instr}, mask), Builtins::AND(Pattern{instr}, mask),
[=]() -> Pattern { return {instr}; }, [=]() -> Pattern { return {instr}; }
false
}); });
rules.push_back({ rules.push_back({
Builtins::AND(mask, Pattern{instr}), Builtins::AND(mask, Pattern{instr}),
[=]() -> Pattern { return {instr}; }, [=]() -> Pattern { return {instr}; }
false
}); });
} }
@ -357,21 +351,18 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart6(
typename Builtins::PatternGeneratorInstance op{instr}; typename Builtins::PatternGeneratorInstance op{instr};
rules.push_back({ rules.push_back({
Builtins::ISZERO(Builtins::ISZERO(op(X, Y))), Builtins::ISZERO(Builtins::ISZERO(op(X, Y))),
[=]() -> Pattern { return op(X, Y); }, [=]() -> Pattern { return op(X, Y); }
false
}); });
} }
rules.push_back({ rules.push_back({
Builtins::ISZERO(Builtins::ISZERO(Builtins::ISZERO(X))), Builtins::ISZERO(Builtins::ISZERO(Builtins::ISZERO(X))),
[=]() -> Pattern { return Builtins::ISZERO(X); }, [=]() -> Pattern { return Builtins::ISZERO(X); }
false
}); });
rules.push_back({ rules.push_back({
Builtins::ISZERO(Builtins::XOR(X, Y)), Builtins::ISZERO(Builtins::XOR(X, Y)),
[=]() -> Pattern { return Builtins::EQ(X, Y); }, [=]() -> Pattern { return Builtins::EQ(X, Y); }
false
}); });
return rules; return rules;
@ -409,23 +400,19 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
rules += std::vector<SimplificationRule<Pattern>>{{ rules += std::vector<SimplificationRule<Pattern>>{{
// (X+A)+B -> X+(A+B) // (X+A)+B -> X+(A+B)
op(opXA, B), op(opXA, B),
[=]() -> Pattern { return op(X, fun(A.d(), B.d())); }, [=]() -> Pattern { return op(X, fun(A.d(), B.d())); }
false
}, { }, {
// (X+A)+Y -> (X+Y)+A // (X+A)+Y -> (X+Y)+A
op(opXA, Y), op(opXA, Y),
[=]() -> Pattern { return op(op(X, Y), A); }, [=]() -> Pattern { return op(op(X, Y), A); }
false
}, { }, {
// B+(X+A) -> X+(A+B) // B+(X+A) -> X+(A+B)
op(B, opXA), op(B, opXA),
[=]() -> Pattern { return op(X, fun(A.d(), B.d())); }, [=]() -> Pattern { return op(X, fun(A.d(), B.d())); }
false
}, { }, {
// Y+(X+A) -> (Y+X)+A // Y+(X+A) -> (Y+X)+A
op(Y, opXA), op(Y, opXA),
[=]() -> Pattern { return op(op(Y, X), A); }, [=]() -> Pattern { return op(op(Y, X), A); }
false
}}; }};
} }
} }
@ -440,8 +427,7 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
return Builtins::AND(X, Word(0)); return Builtins::AND(X, Word(0));
else else
return Builtins::SHL(Word(sum), X); return Builtins::SHL(Word(sum), X);
}, }
false
}); });
// Combine two SHR by constant // Combine two SHR by constant
@ -454,8 +440,7 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
return Builtins::AND(X, Word(0)); return Builtins::AND(X, Word(0));
else else
return Builtins::SHR(Word(sum), X); return Builtins::SHR(Word(sum), X);
}, }
false
}); });
// Combine SHL-SHR by constant // Combine SHL-SHR by constant
@ -472,7 +457,6 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
else else
return Builtins::AND(X, mask); return Builtins::AND(X, mask);
}, },
false,
[=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; } [=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; }
}); });
@ -490,7 +474,6 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
else else
return Builtins::AND(X, mask); return Builtins::AND(X, mask);
}, },
false,
[=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; } [=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; }
}); });
@ -509,14 +492,12 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
// SH[L/R](B, AND(X, A)) -> AND(SH[L/R](B, X), [ A << B / A >> B ]) // SH[L/R](B, AND(X, A)) -> AND(SH[L/R](B, X), [ A << B / A >> B ])
shiftOp(B, Builtins::AND(X, A)), shiftOp(B, Builtins::AND(X, A)),
replacement, replacement,
false,
[=] { return B.d() < Pattern::WordSize; } [=] { return B.d() < Pattern::WordSize; }
}); });
rules.push_back({ rules.push_back({
// SH[L/R](B, AND(A, X)) -> AND(SH[L/R](B, X), [ A << B / A >> B ]) // SH[L/R](B, AND(A, X)) -> AND(SH[L/R](B, X), [ A << B / A >> B ])
shiftOp(B, Builtins::AND(A, X)), shiftOp(B, Builtins::AND(A, X)),
replacement, replacement,
false,
[=] { return B.d() < Pattern::WordSize; } [=] { return B.d() < Pattern::WordSize; }
}); });
} }
@ -526,17 +507,14 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
Builtins::MUL(X, Builtins::SHL(Y, Word(1))), Builtins::MUL(X, Builtins::SHL(Y, Word(1))),
[=]() -> Pattern { [=]() -> Pattern {
return Builtins::SHL(Y, X); return Builtins::SHL(Y, X);
}, }
// Actually only changes the order, does not remove.
true
}); });
rules.push_back({ rules.push_back({
// MUL(SHL(X, 1), Y) -> SHL(X, Y) // MUL(SHL(X, 1), Y) -> SHL(X, Y)
Builtins::MUL(Builtins::SHL(X, Word(1)), Y), Builtins::MUL(Builtins::SHL(X, Word(1)), Y),
[=]() -> Pattern { [=]() -> Pattern {
return Builtins::SHL(X, Y); return Builtins::SHL(X, Y);
}, }
false
}); });
rules.push_back({ rules.push_back({
@ -544,9 +522,7 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
Builtins::DIV(X, Builtins::SHL(Y, Word(1))), Builtins::DIV(X, Builtins::SHL(Y, Word(1))),
[=]() -> Pattern { [=]() -> Pattern {
return Builtins::SHR(Y, X); return Builtins::SHR(Y, X);
}, }
// Actually only changes the order, does not remove.
true
}); });
std::function<bool()> feasibilityFunction = [=]() { std::function<bool()> feasibilityFunction = [=]() {
@ -560,7 +536,6 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
// AND(A, SHR(B, X)) -> A & ((2^256-1) >> B) == ((2^256-1) >> B) // AND(A, SHR(B, X)) -> A & ((2^256-1) >> B) == ((2^256-1) >> B)
Builtins::AND(A, Builtins::SHR(B, X)), Builtins::AND(A, Builtins::SHR(B, X)),
[=]() -> Pattern { return Builtins::SHR(B, X); }, [=]() -> Pattern { return Builtins::SHR(B, X); },
false,
feasibilityFunction feasibilityFunction
}); });
@ -568,28 +543,24 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
// AND(SHR(B, X), A) -> ((2^256-1) >> B) & A == ((2^256-1) >> B) // AND(SHR(B, X), A) -> ((2^256-1) >> B) & A == ((2^256-1) >> B)
Builtins::AND(Builtins::SHR(B, X), A), Builtins::AND(Builtins::SHR(B, X), A),
[=]() -> Pattern { return Builtins::SHR(B, X); }, [=]() -> Pattern { return Builtins::SHR(B, X); },
false,
feasibilityFunction feasibilityFunction
}); });
rules.push_back({ rules.push_back({
Builtins::BYTE(A, Builtins::SHL(B, X)), Builtins::BYTE(A, Builtins::SHL(B, X)),
[=]() -> Pattern { return Builtins::BYTE(A.d() + B.d() / 8, X); }, [=]() -> Pattern { return Builtins::BYTE(A.d() + B.d() / 8, X); },
false,
[=] { return B.d() % 8 == 0 && A.d() <= 32 && B.d() <= 256; } [=] { return B.d() % 8 == 0 && A.d() <= 32 && B.d() <= 256; }
}); });
rules.push_back({ rules.push_back({
Builtins::BYTE(A, Builtins::SHR(B, X)), Builtins::BYTE(A, Builtins::SHR(B, X)),
[=]() -> Pattern { return Word(0); }, [=]() -> Pattern { return Word(0); },
true,
[=] { return A.d() < B.d() / 8; } [=] { return A.d() < B.d() / 8; }
}); });
rules.push_back({ rules.push_back({
Builtins::BYTE(A, Builtins::SHR(B, X)), Builtins::BYTE(A, Builtins::SHR(B, X)),
[=]() -> Pattern { return Builtins::BYTE(A.d() - B.d() / 8, X); }, [=]() -> Pattern { return Builtins::BYTE(A.d() - B.d() / 8, X); },
false,
[=] { [=] {
return B.d() % 8 == 0 && A.d() < Pattern::WordSize / 8 && B.d() <= Pattern::WordSize && A.d() >= B.d() / 8; return B.d() % 8 == 0 && A.d() < Pattern::WordSize / 8 && B.d() <= Pattern::WordSize && A.d() >= B.d() / 8;
} }
@ -615,76 +586,28 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart8(
{ {
// X - A -> X + (-A) // X - A -> X + (-A)
Builtins::SUB(X, A), Builtins::SUB(X, A),
[=]() -> Pattern { return Builtins::ADD(X, 0 - A.d()); }, [=]() -> Pattern { return Builtins::ADD(X, 0 - A.d()); }
false
}, { }, {
// (X + A) - Y -> (X - Y) + A // (X + A) - Y -> (X - Y) + A
Builtins::SUB(Builtins::ADD(X, A), Y), Builtins::SUB(Builtins::ADD(X, A), Y),
[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); }, [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); }
false
}, { }, {
// (A + X) - Y -> (X - Y) + A // (A + X) - Y -> (X - Y) + A
Builtins::SUB(Builtins::ADD(A, X), Y), Builtins::SUB(Builtins::ADD(A, X), Y),
[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); }, [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); }
false
}, { }, {
// X - (Y + A) -> (X - Y) + (-A) // X - (Y + A) -> (X - Y) + (-A)
Builtins::SUB(X, Builtins::ADD(Y, A)), Builtins::SUB(X, Builtins::ADD(Y, A)),
[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); }, [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); }
false
}, { }, {
// X - (A + Y) -> (X - Y) + (-A) // X - (A + Y) -> (X - Y) + (-A)
Builtins::SUB(X, Builtins::ADD(A, Y)), Builtins::SUB(X, Builtins::ADD(A, Y)),
[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); }, [=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); }
false
} }
}; };
return rules; return rules;
} }
template <class Pattern>
std::vector<SimplificationRule<Pattern>> simplificationRuleListPart9(
Pattern,
Pattern,
Pattern,
Pattern W,
Pattern X,
Pattern Y,
Pattern Z
)
{
using Word = typename Pattern::Word;
using Builtins = typename Pattern::Builtins;
std::vector<SimplificationRule<Pattern>> rules;
assertThrow(Pattern::WordSize > 160, OptimizerException, "");
Word const mask = (Word(1) << 160) - 1;
// CREATE
rules.push_back({
Builtins::AND(Builtins::CREATE(W, X, Y), mask),
[=]() -> Pattern { return Builtins::CREATE(W, X, Y); },
false
});
rules.push_back({
Builtins::AND(mask, Builtins::CREATE(W, X, Y)),
[=]() -> Pattern { return Builtins::CREATE(W, X, Y); },
false
});
// CREATE2
rules.push_back({
Builtins::AND(Builtins::CREATE2(W, X, Y, Z), mask),
[=]() -> Pattern { return Builtins::CREATE2(W, X, Y, Z); },
false
});
rules.push_back({
Builtins::AND(mask, Builtins::CREATE2(W, X, Y, Z)),
[=]() -> Pattern { return Builtins::CREATE2(W, X, Y, Z); },
false
});
return rules;
}
template<class Pattern> template<class Pattern>
std::vector<SimplificationRule<Pattern>> evmRuleList( std::vector<SimplificationRule<Pattern>> evmRuleList(
langutil::EVMVersion _evmVersion, langutil::EVMVersion _evmVersion,
@ -703,7 +626,7 @@ std::vector<SimplificationRule<Pattern>> evmRuleList(
if (_evmVersion.hasSelfBalance()) if (_evmVersion.hasSelfBalance())
rules.push_back({ rules.push_back({
Builtins::BALANCE(Instruction::ADDRESS), Builtins::BALANCE(Instruction::ADDRESS),
[]() -> Pattern { return Instruction::SELFBALANCE; }, false []() -> Pattern { return Instruction::SELFBALANCE; }
}); });
return rules; return rules;
@ -743,7 +666,6 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleList(
rules += simplificationRuleListPart6(A, B, C, W, X); rules += simplificationRuleListPart6(A, B, C, W, X);
rules += simplificationRuleListPart7(A, B, C, W, X); rules += simplificationRuleListPart7(A, B, C, W, X);
rules += simplificationRuleListPart8(A, B, C, W, X); rules += simplificationRuleListPart8(A, B, C, W, X);
rules += simplificationRuleListPart9(A, B, C, W, X, Y, Z);
if (_evmVersion.has_value()) if (_evmVersion.has_value())
rules += evmRuleList(*_evmVersion, A, B, C, W, X, Y, Z); rules += evmRuleList(*_evmVersion, A, B, C, W, X, Y, Z);

View File

@ -30,9 +30,8 @@ namespace solidity::evmasm
/** /**
* Rule that contains a pattern, an action that can be applied * Rule that contains a pattern, an action that can be applied
* after the pattern has matched and a bool that indicates * after the pattern has matched and optional condition to check if the
* whether the action would remove something from the expression * action should be applied.
* than is not a constant literal.
*/ */
template <class Pattern> template <class Pattern>
struct SimplificationRule struct SimplificationRule
@ -40,18 +39,15 @@ struct SimplificationRule
SimplificationRule( SimplificationRule(
Pattern _pattern, Pattern _pattern,
std::function<Pattern()> _action, std::function<Pattern()> _action,
bool _removesNonConstants,
std::function<bool()> _feasible = {} std::function<bool()> _feasible = {}
): ):
pattern(std::move(_pattern)), pattern(std::move(_pattern)),
action(std::move(_action)), action(std::move(_action)),
removesNonConstants(_removesNonConstants),
feasible(std::move(_feasible)) feasible(std::move(_feasible))
{} {}
Pattern pattern; Pattern pattern;
std::function<Pattern()> action; std::function<Pattern()> action;
bool removesNonConstants;
std::function<bool()> feasible; std::function<bool()> feasible;
}; };

View File

@ -50,6 +50,7 @@ void CHCSmtLib2Interface::reset()
{ {
m_accumulatedOutput.clear(); m_accumulatedOutput.clear();
m_variables.clear(); m_variables.clear();
m_unhandledQueries.clear();
} }
void CHCSmtLib2Interface::registerRelation(Expression const& _expr) void CHCSmtLib2Interface::registerRelation(Expression const& _expr)

View File

@ -196,6 +196,12 @@ CVC4::Expr CVC4Interface::toCVC4Expr(Expression const& _expr)
return m_context.mkExpr(CVC4::kind::BITVECTOR_OR, arguments[0], arguments[1]); return m_context.mkExpr(CVC4::kind::BITVECTOR_OR, arguments[0], arguments[1]);
else if (n == "bvxor") else if (n == "bvxor")
return m_context.mkExpr(CVC4::kind::BITVECTOR_XOR, arguments[0], arguments[1]); return m_context.mkExpr(CVC4::kind::BITVECTOR_XOR, arguments[0], arguments[1]);
else if (n == "bvshl")
return m_context.mkExpr(CVC4::kind::BITVECTOR_SHL, arguments[0], arguments[1]);
else if (n == "bvlshr")
return m_context.mkExpr(CVC4::kind::BITVECTOR_LSHR, arguments[0], arguments[1]);
else if (n == "bvashr")
return m_context.mkExpr(CVC4::kind::BITVECTOR_ASHR, arguments[0], arguments[1]);
else if (n == "int2bv") else if (n == "int2bv")
{ {
size_t size = std::stoul(_expr.arguments[1].name); size_t size = std::stoul(_expr.arguments[1].name);
@ -289,6 +295,8 @@ CVC4::Type CVC4Interface::cvc4Sort(Sort const& _sort)
return m_context.booleanType(); return m_context.booleanType();
case Kind::Int: case Kind::Int:
return m_context.integerType(); return m_context.integerType();
case Kind::BitVector:
return m_context.mkBitVectorType(dynamic_cast<BitVectorSort const&>(_sort).size);
case Kind::Function: case Kind::Function:
{ {
FunctionSort const& fSort = dynamic_cast<FunctionSort const&>(_sort); FunctionSort const& fSort = dynamic_cast<FunctionSort const&>(_sort);

View File

@ -38,11 +38,11 @@ using namespace solidity::frontend;
using namespace solidity::smtutil; using namespace solidity::smtutil;
SMTLib2Interface::SMTLib2Interface( SMTLib2Interface::SMTLib2Interface(
map<h256, string> const& _queryResponses, map<h256, string> _queryResponses,
ReadCallback::Callback _smtCallback ReadCallback::Callback _smtCallback
): ):
m_queryResponses(_queryResponses), m_queryResponses(move(_queryResponses)),
m_smtCallback(std::move(_smtCallback)) m_smtCallback(move(_smtCallback))
{ {
reset(); reset();
} }
@ -215,6 +215,8 @@ string SMTLib2Interface::toSmtLibSort(Sort const& _sort)
return "Int"; return "Int";
case Kind::Bool: case Kind::Bool:
return "Bool"; return "Bool";
case Kind::BitVector:
return "(_ BitVec " + to_string(dynamic_cast<BitVectorSort const&>(_sort).size) + ")";
case Kind::Array: case Kind::Array:
{ {
auto const& arraySort = dynamic_cast<ArraySort const&>(_sort); auto const& arraySort = dynamic_cast<ArraySort const&>(_sort);

View File

@ -39,8 +39,8 @@ class SMTLib2Interface: public SolverInterface, public boost::noncopyable
{ {
public: public:
explicit SMTLib2Interface( explicit SMTLib2Interface(
std::map<util::h256, std::string> const& _queryResponses, std::map<util::h256, std::string> _queryResponses = {},
frontend::ReadCallback::Callback _smtCallback frontend::ReadCallback::Callback _smtCallback = {}
); );
void reset() override; void reset() override;
@ -77,7 +77,7 @@ private:
std::map<std::string, SortPointer> m_variables; std::map<std::string, SortPointer> m_variables;
std::set<std::string> m_userSorts; std::set<std::string> m_userSorts;
std::map<util::h256, std::string> const& m_queryResponses; std::map<util::h256, std::string> m_queryResponses;
std::vector<std::string> m_unhandledQueries; std::vector<std::string> m_unhandledQueries;
frontend::ReadCallback::Callback m_smtCallback; frontend::ReadCallback::Callback m_smtCallback;

View File

@ -33,12 +33,12 @@ using namespace solidity::frontend;
using namespace solidity::smtutil; using namespace solidity::smtutil;
SMTPortfolio::SMTPortfolio( SMTPortfolio::SMTPortfolio(
map<h256, string> const& _smtlib2Responses, map<h256, string> _smtlib2Responses,
frontend::ReadCallback::Callback const& _smtCallback, frontend::ReadCallback::Callback _smtCallback,
[[maybe_unused]] SMTSolverChoice _enabledSolvers [[maybe_unused]] SMTSolverChoice _enabledSolvers
) )
{ {
m_solvers.emplace_back(make_unique<SMTLib2Interface>(_smtlib2Responses, _smtCallback)); m_solvers.emplace_back(make_unique<SMTLib2Interface>(move(_smtlib2Responses), move(_smtCallback)));
#ifdef HAVE_Z3 #ifdef HAVE_Z3
if (_enabledSolvers.z3) if (_enabledSolvers.z3)
m_solvers.emplace_back(make_unique<Z3Interface>()); m_solvers.emplace_back(make_unique<Z3Interface>());

View File

@ -40,9 +40,9 @@ class SMTPortfolio: public SolverInterface, public boost::noncopyable
{ {
public: public:
SMTPortfolio( SMTPortfolio(
std::map<util::h256, std::string> const& _smtlib2Responses, std::map<util::h256, std::string> _smtlib2Responses = {},
frontend::ReadCallback::Callback const& _smtCallback, frontend::ReadCallback::Callback _smtCallback = {},
SMTSolverChoice _enabledSolvers SMTSolverChoice _enabledSolvers = SMTSolverChoice::All()
); );
void reset() override; void reset() override;

View File

@ -99,6 +99,9 @@ public:
{"bvand", 2}, {"bvand", 2},
{"bvor", 2}, {"bvor", 2},
{"bvxor", 2}, {"bvxor", 2},
{"bvshl", 2},
{"bvlshr", 2},
{"bvashr", 2},
{"int2bv", 2}, {"int2bv", 2},
{"bv2int", 1}, {"bv2int", 1},
{"select", 2}, {"select", 2},
@ -299,15 +302,30 @@ public:
auto bvSort = _a.sort; auto bvSort = _a.sort;
return Expression("bvand", {std::move(_a), std::move(_b)}, bvSort); return Expression("bvand", {std::move(_a), std::move(_b)}, bvSort);
} }
friend Expression operator|(Expression _a, Expression _b)
{
auto bvSort = _a.sort;
return Expression("bvor", {std::move(_a), std::move(_b)}, bvSort);
}
friend Expression operator^(Expression _a, Expression _b) friend Expression operator^(Expression _a, Expression _b)
{ {
auto bvSort = _a.sort; auto bvSort = _a.sort;
return Expression("bvxor", {std::move(_a), std::move(_b)}, bvSort); return Expression("bvxor", {std::move(_a), std::move(_b)}, bvSort);
} }
friend Expression operator|(Expression _a, Expression _b) friend Expression operator<<(Expression _a, Expression _b)
{ {
auto bvSort = _a.sort; auto bvSort = _a.sort;
return Expression("bvor", {std::move(_a), std::move(_b)}, bvSort); return Expression("bvshl", {std::move(_a), std::move(_b)}, bvSort);
}
friend Expression operator>>(Expression _a, Expression _b)
{
auto bvSort = _a.sort;
return Expression("bvlshr", {std::move(_a), std::move(_b)}, bvSort);
}
static Expression ashr(Expression _a, Expression _b)
{
auto bvSort = _a.sort;
return Expression("bvashr", {std::move(_a), std::move(_b)}, bvSort);
} }
Expression operator()(std::vector<Expression> _arguments) const Expression operator()(std::vector<Expression> _arguments) const
{ {

View File

@ -35,4 +35,6 @@ shared_ptr<IntSort> SortProvider::intSort(bool _signed)
return uintSort; return uintSort;
} }
shared_ptr<BitVectorSort> const SortProvider::bitVectorSort{make_shared<BitVectorSort>(256)};
} }

View File

@ -195,6 +195,7 @@ struct SortProvider
static std::shared_ptr<IntSort> const uintSort; static std::shared_ptr<IntSort> const uintSort;
static std::shared_ptr<IntSort> const sintSort; static std::shared_ptr<IntSort> const sintSort;
static std::shared_ptr<IntSort> intSort(bool _signed = false); static std::shared_ptr<IntSort> intSort(bool _signed = false);
static std::shared_ptr<BitVectorSort> const bitVectorSort;
}; };
} }

View File

@ -189,6 +189,12 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr)
return arguments[0] | arguments[1]; return arguments[0] | arguments[1];
else if (n == "bvxor") else if (n == "bvxor")
return arguments[0] ^ arguments[1]; return arguments[0] ^ arguments[1];
else if (n == "bvshl")
return z3::shl(arguments[0], arguments[1]);
else if (n == "bvlshr")
return z3::lshr(arguments[0], arguments[1]);
else if (n == "bvashr")
return z3::ashr(arguments[0], arguments[1]);
else if (n == "int2bv") else if (n == "int2bv")
{ {
size_t size = std::stoul(_expr.arguments[1].name); size_t size = std::stoul(_expr.arguments[1].name);
@ -245,6 +251,8 @@ z3::sort Z3Interface::z3Sort(Sort const& _sort)
return m_context.bool_sort(); return m_context.bool_sort();
case Kind::Int: case Kind::Int:
return m_context.int_sort(); return m_context.int_sort();
case Kind::BitVector:
return m_context.bv_sort(dynamic_cast<BitVectorSort const&>(_sort).size);
case Kind::Array: case Kind::Array:
{ {
auto const& arraySort = dynamic_cast<ArraySort const&>(_sort); auto const& arraySort = dynamic_cast<ArraySort const&>(_sort);

View File

@ -30,6 +30,8 @@ set(sources
analysis/PostTypeChecker.h analysis/PostTypeChecker.h
analysis/ReferencesResolver.cpp analysis/ReferencesResolver.cpp
analysis/ReferencesResolver.h analysis/ReferencesResolver.h
analysis/Scoper.cpp
analysis/Scoper.h
analysis/StaticAnalyzer.cpp analysis/StaticAnalyzer.cpp
analysis/StaticAnalyzer.h analysis/StaticAnalyzer.h
analysis/SyntaxChecker.cpp analysis/SyntaxChecker.cpp

View File

@ -519,14 +519,13 @@ bool DeclarationRegistrationHelper::visit(SourceUnit& _sourceUnit)
if (!m_scopes[&_sourceUnit]) if (!m_scopes[&_sourceUnit])
// By importing, it is possible that the container already exists. // By importing, it is possible that the container already exists.
m_scopes[&_sourceUnit] = make_shared<DeclarationContainer>(m_currentScope, m_scopes[m_currentScope].get()); m_scopes[&_sourceUnit] = make_shared<DeclarationContainer>(m_currentScope, m_scopes[m_currentScope].get());
m_currentScope = &_sourceUnit; return ASTVisitor::visit(_sourceUnit);
return true;
} }
void DeclarationRegistrationHelper::endVisit(SourceUnit& _sourceUnit) void DeclarationRegistrationHelper::endVisit(SourceUnit& _sourceUnit)
{ {
_sourceUnit.annotation().exportedSymbols = m_scopes[&_sourceUnit]->declarations(); _sourceUnit.annotation().exportedSymbols = m_scopes[&_sourceUnit]->declarations();
closeCurrentScope(); ASTVisitor::endVisit(_sourceUnit);
} }
bool DeclarationRegistrationHelper::visit(ImportDirective& _import) bool DeclarationRegistrationHelper::visit(ImportDirective& _import)
@ -536,8 +535,7 @@ bool DeclarationRegistrationHelper::visit(ImportDirective& _import)
if (!m_scopes[importee]) if (!m_scopes[importee])
m_scopes[importee] = make_shared<DeclarationContainer>(nullptr, m_scopes[nullptr].get()); m_scopes[importee] = make_shared<DeclarationContainer>(nullptr, m_scopes[nullptr].get());
m_scopes[&_import] = m_scopes[importee]; m_scopes[&_import] = m_scopes[importee];
registerDeclaration(_import, false); return ASTVisitor::visit(_import);
return true;
} }
bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract) bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract)
@ -547,122 +545,17 @@ bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract)
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), nullptr, false, true); m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), nullptr, false, true);
m_currentContract = &_contract; m_currentContract = &_contract;
registerDeclaration(_contract, true); return ASTVisitor::visit(_contract);
_contract.annotation().canonicalName = currentCanonicalName();
return true;
} }
void DeclarationRegistrationHelper::endVisit(ContractDefinition&) void DeclarationRegistrationHelper::endVisit(ContractDefinition& _contract)
{ {
// make "this" and "super" invisible. // make "this" and "super" invisible.
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), nullptr, true, true); m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), nullptr, true, true);
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), nullptr, true, true); m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), nullptr, true, true);
m_globalContext.resetCurrentContract(); m_globalContext.resetCurrentContract();
m_currentContract = nullptr; m_currentContract = nullptr;
closeCurrentScope(); ASTVisitor::endVisit(_contract);
}
bool DeclarationRegistrationHelper::visit(StructDefinition& _struct)
{
registerDeclaration(_struct, true);
_struct.annotation().canonicalName = currentCanonicalName();
return true;
}
void DeclarationRegistrationHelper::endVisit(StructDefinition&)
{
closeCurrentScope();
}
bool DeclarationRegistrationHelper::visit(EnumDefinition& _enum)
{
registerDeclaration(_enum, true);
_enum.annotation().canonicalName = currentCanonicalName();
return true;
}
void DeclarationRegistrationHelper::endVisit(EnumDefinition&)
{
closeCurrentScope();
}
bool DeclarationRegistrationHelper::visit(EnumValue& _value)
{
registerDeclaration(_value, false);
return true;
}
bool DeclarationRegistrationHelper::visit(FunctionDefinition& _function)
{
registerDeclaration(_function, true);
m_currentFunction = &_function;
return true;
}
void DeclarationRegistrationHelper::endVisit(FunctionDefinition&)
{
m_currentFunction = nullptr;
closeCurrentScope();
}
bool DeclarationRegistrationHelper::visit(TryCatchClause& _tryCatchClause)
{
_tryCatchClause.annotation().scope = m_currentScope;
enterNewSubScope(_tryCatchClause);
return true;
}
void DeclarationRegistrationHelper::endVisit(TryCatchClause&)
{
closeCurrentScope();
}
bool DeclarationRegistrationHelper::visit(ModifierDefinition& _modifier)
{
registerDeclaration(_modifier, true);
m_currentFunction = &_modifier;
return true;
}
void DeclarationRegistrationHelper::endVisit(ModifierDefinition&)
{
m_currentFunction = nullptr;
closeCurrentScope();
}
bool DeclarationRegistrationHelper::visit(FunctionTypeName& _funTypeName)
{
enterNewSubScope(_funTypeName);
return true;
}
void DeclarationRegistrationHelper::endVisit(FunctionTypeName&)
{
closeCurrentScope();
}
bool DeclarationRegistrationHelper::visit(Block& _block)
{
_block.annotation().scope = m_currentScope;
enterNewSubScope(_block);
return true;
}
void DeclarationRegistrationHelper::endVisit(Block&)
{
closeCurrentScope();
}
bool DeclarationRegistrationHelper::visit(ForStatement& _for)
{
_for.annotation().scope = m_currentScope;
enterNewSubScope(_for);
return true;
}
void DeclarationRegistrationHelper::endVisit(ForStatement&)
{
closeCurrentScope();
} }
void DeclarationRegistrationHelper::endVisit(VariableDeclarationStatement& _variableDeclarationStatement) void DeclarationRegistrationHelper::endVisit(VariableDeclarationStatement& _variableDeclarationStatement)
@ -673,32 +566,42 @@ void DeclarationRegistrationHelper::endVisit(VariableDeclarationStatement& _vari
for (ASTPointer<VariableDeclaration> const& var: _variableDeclarationStatement.declarations()) for (ASTPointer<VariableDeclaration> const& var: _variableDeclarationStatement.declarations())
if (var) if (var)
m_currentFunction->addLocalVariable(*var); m_currentFunction->addLocalVariable(*var);
ASTVisitor::endVisit(_variableDeclarationStatement);
} }
bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration) bool DeclarationRegistrationHelper::visitNode(ASTNode& _node)
{ {
registerDeclaration(_declaration, false); if (auto const* scopable = dynamic_cast<Scopable const*>(&_node))
solAssert(scopable->annotation().scope == m_currentScope, "");
if (auto* declaration = dynamic_cast<Declaration*>(&_node))
registerDeclaration(*declaration);
if (dynamic_cast<ScopeOpener const*>(&_node))
enterNewSubScope(_node);
if (auto* variableScope = dynamic_cast<VariableScope*>(&_node))
m_currentFunction = variableScope;
if (auto* annotation = dynamic_cast<TypeDeclarationAnnotation*>(&_node.annotation()))
annotation->canonicalName = currentCanonicalName();
return true; return true;
} }
bool DeclarationRegistrationHelper::visit(EventDefinition& _event) void DeclarationRegistrationHelper::endVisitNode(ASTNode& _node)
{
registerDeclaration(_event, true);
return true;
}
void DeclarationRegistrationHelper::endVisit(EventDefinition&)
{ {
if (dynamic_cast<ScopeOpener const*>(&_node))
closeCurrentScope(); closeCurrentScope();
if (dynamic_cast<VariableScope*>(&_node))
m_currentFunction = nullptr;
} }
void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _subScope) void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _subScope)
{ {
map<ASTNode const*, shared_ptr<DeclarationContainer>>::iterator iter;
bool newlyAdded;
shared_ptr<DeclarationContainer> container{make_shared<DeclarationContainer>(m_currentScope, m_scopes[m_currentScope].get())}; shared_ptr<DeclarationContainer> container{make_shared<DeclarationContainer>(m_currentScope, m_scopes[m_currentScope].get())};
tie(iter, newlyAdded) = m_scopes.emplace(&_subScope, move(container)); bool newlyAdded = m_scopes.emplace(&_subScope, move(container)).second;
solAssert(newlyAdded, "Unable to add new scope."); // Source units are the only AST nodes for which containers can be created from multiple places
// due to imports.
solAssert(newlyAdded || dynamic_cast<SourceUnit const*>(&_subScope), "Unable to add new scope.");
m_currentScope = &_subScope; m_currentScope = &_subScope;
} }
@ -708,7 +611,7 @@ void DeclarationRegistrationHelper::closeCurrentScope()
m_currentScope = m_scopes[m_currentScope]->enclosingNode(); m_currentScope = m_scopes[m_currentScope]->enclosingNode();
} }
void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope) void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration)
{ {
solAssert(m_currentScope && m_scopes.count(m_currentScope), "No current scope."); solAssert(m_currentScope && m_scopes.count(m_currentScope), "No current scope.");
@ -729,10 +632,8 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio
registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, inactive, m_errorReporter); registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, inactive, m_errorReporter);
_declaration.annotation().scope = m_currentScope; solAssert(_declaration.annotation().scope == m_currentScope, "");
_declaration.annotation().contract = m_currentContract; solAssert(_declaration.annotation().contract == m_currentContract, "");
if (_opensScope)
enterNewSubScope(_declaration);
} }
string DeclarationRegistrationHelper::currentCanonicalName() const string DeclarationRegistrationHelper::currentCanonicalName() const

View File

@ -163,31 +163,15 @@ private:
bool visit(ImportDirective& _import) override; bool visit(ImportDirective& _import) override;
bool visit(ContractDefinition& _contract) override; bool visit(ContractDefinition& _contract) override;
void endVisit(ContractDefinition& _contract) override; void endVisit(ContractDefinition& _contract) override;
bool visit(StructDefinition& _struct) override;
void endVisit(StructDefinition& _struct) override;
bool visit(EnumDefinition& _enum) override;
void endVisit(EnumDefinition& _enum) override;
bool visit(EnumValue& _value) override;
bool visit(FunctionDefinition& _function) override;
void endVisit(FunctionDefinition& _function) override;
bool visit(TryCatchClause& _tryCatchClause) override;
void endVisit(TryCatchClause& _tryCatchClause) override;
bool visit(ModifierDefinition& _modifier) override;
void endVisit(ModifierDefinition& _modifier) override;
bool visit(FunctionTypeName& _funTypeName) override;
void endVisit(FunctionTypeName& _funTypeName) override;
bool visit(Block& _block) override;
void endVisit(Block& _block) override;
bool visit(ForStatement& _forLoop) override;
void endVisit(ForStatement& _forLoop) override;
void endVisit(VariableDeclarationStatement& _variableDeclarationStatement) override; void endVisit(VariableDeclarationStatement& _variableDeclarationStatement) override;
bool visit(VariableDeclaration& _declaration) override;
bool visit(EventDefinition& _event) override; bool visitNode(ASTNode& _node) override;
void endVisit(EventDefinition& _event) override; void endVisitNode(ASTNode& _node) override;
void enterNewSubScope(ASTNode& _subScope); void enterNewSubScope(ASTNode& _subScope);
void closeCurrentScope(); void closeCurrentScope();
void registerDeclaration(Declaration& _declaration, bool _opensScope); void registerDeclaration(Declaration& _declaration);
static bool isOverloadedFunction(Declaration const& _declaration1, Declaration const& _declaration2); static bool isOverloadedFunction(Declaration const& _declaration1, Declaration const& _declaration2);

View File

@ -0,0 +1,63 @@
/*
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 <libsolidity/analysis/Scoper.h>
#include <libsolidity/ast/AST.h>
using namespace std;
using namespace solidity;
using namespace solidity::frontend;
void Scoper::assignScopes(ASTNode const& _astRoot)
{
Scoper scoper;
_astRoot.accept(scoper);
}
bool Scoper::visit(ContractDefinition const& _contract)
{
solAssert(m_contract == nullptr, "");
m_contract = &_contract;
return ASTConstVisitor::visit(_contract);
}
void Scoper::endVisit(ContractDefinition const& _contract)
{
solAssert(m_contract == &_contract, "");
m_contract = nullptr;
ASTConstVisitor::endVisit(_contract);
}
bool Scoper::visitNode(ASTNode const& _node)
{
if (auto const* scopable = dynamic_cast<Scopable const*>(&_node))
{
scopable->annotation().scope = m_scopes.empty() ? nullptr : m_scopes.back();
scopable->annotation().contract = m_contract;
}
if (dynamic_cast<ScopeOpener const*>(&_node))
m_scopes.push_back(&_node);
return true;
}
void Scoper::endVisitNode(ASTNode const& _node)
{
if (dynamic_cast<ScopeOpener const*>(&_node))
m_scopes.pop_back();
}

View File

@ -0,0 +1,45 @@
/*
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
#pragma once
#include <libsolidity/ast/ASTForward.h>
#include <libsolidity/ast/ASTVisitor.h>
namespace solidity::frontend
{
/**
* AST visitor that assigns syntactic scopes.
*/
class Scoper: private ASTConstVisitor
{
public:
static void assignScopes(ASTNode const& _astRoot);
private:
bool visit(ContractDefinition const& _contract) override;
void endVisit(ContractDefinition const& _contract) override;
bool visitNode(ASTNode const& _node) override;
void endVisitNode(ASTNode const& _node) override;
ContractDefinition const* m_contract = nullptr;
std::vector<ASTNode const*> m_scopes;
};
}

View File

@ -156,6 +156,20 @@ bool StaticAnalyzer::visit(VariableDeclaration const& _variable)
// This is not a no-op, the entry might pre-exist. // This is not a no-op, the entry might pre-exist.
m_localVarUseCount[make_pair(_variable.id(), &_variable)] += 0; m_localVarUseCount[make_pair(_variable.id(), &_variable)] += 0;
} }
if (_variable.isStateVariable() || _variable.referenceLocation() == VariableDeclaration::Location::Storage)
{
TypePointer varType = _variable.annotation().type;
for (Type const* subtype: frontend::oversizedSubtypes(*varType))
{
string message = "Type " + subtype->toString(true) +
" covers a large part of storage and thus makes collisions likely."
" Either use mappings or dynamic arrays and allow their size to be increased only"
" in small quantities per transaction.";
m_errorReporter.warning(7325_error, _variable.typeName().location(), message);
}
}
return true; return true;
} }

View File

@ -609,29 +609,6 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
} }
} }
if (varType->dataStoredIn(DataLocation::Storage))
{
auto collisionMessage = [&](string const& variableOrType, bool isVariable) -> string {
return
(isVariable ? "Variable " : "Type ") +
util::escapeAndQuoteString(variableOrType) +
" covers a large part of storage and thus makes collisions likely."
" Either use mappings or dynamic arrays and allow their size to be increased only"
" in small quantities per transaction.";
};
if (varType->storageSizeUpperBound() >= bigint(1) << 64)
{
if (_variable.isStateVariable())
m_errorReporter.warning(3408_error, _variable.location(), collisionMessage(_variable.name(), true));
else
m_errorReporter.warning(2332_error, _variable.typeName().location(), collisionMessage(varType->toString(true), false));
}
vector<Type const*> oversizedSubtypes = frontend::oversizedSubtypes(*varType);
for (Type const* subtype: oversizedSubtypes)
m_errorReporter.warning(7325_error, _variable.typeName().location(), collisionMessage(subtype->canonicalName(), false));
}
return false; return false;
} }

View File

@ -152,10 +152,19 @@ std::vector<T const*> ASTNode::filteredNodes(std::vector<ASTPointer<ASTNode>> co
return ret; return ret;
} }
/**
* Abstract marker class that specifies that this AST node opens a scope.
*/
class ScopeOpener
{
public:
virtual ~ScopeOpener() = default;
};
/** /**
* Source unit containing import directives and contract definitions. * Source unit containing import directives and contract definitions.
*/ */
class SourceUnit: public ASTNode class SourceUnit: public ASTNode, public ScopeOpener
{ {
public: public:
SourceUnit( SourceUnit(
@ -455,7 +464,7 @@ protected:
* document order. It first visits all struct declarations, then all variable declarations and * document order. It first visits all struct declarations, then all variable declarations and
* finally all function declarations. * finally all function declarations.
*/ */
class ContractDefinition: public Declaration, public StructurallyDocumented class ContractDefinition: public Declaration, public StructurallyDocumented, public ScopeOpener
{ {
public: public:
ContractDefinition( ContractDefinition(
@ -593,7 +602,7 @@ private:
ASTPointer<TypeName> m_typeName; ASTPointer<TypeName> m_typeName;
}; };
class StructDefinition: public Declaration class StructDefinition: public Declaration, public ScopeOpener
{ {
public: public:
StructDefinition( StructDefinition(
@ -620,7 +629,7 @@ private:
std::vector<ASTPointer<VariableDeclaration>> m_members; std::vector<ASTPointer<VariableDeclaration>> m_members;
}; };
class EnumDefinition: public Declaration class EnumDefinition: public Declaration, public ScopeOpener
{ {
public: public:
EnumDefinition( EnumDefinition(
@ -765,7 +774,7 @@ protected:
std::vector<ASTPointer<UserDefinedTypeName>> m_overrides; std::vector<ASTPointer<UserDefinedTypeName>> m_overrides;
}; };
class FunctionDefinition: public CallableDeclaration, public StructurallyDocumented, public ImplementationOptional class FunctionDefinition: public CallableDeclaration, public StructurallyDocumented, public ImplementationOptional, public ScopeOpener
{ {
public: public:
FunctionDefinition( FunctionDefinition(
@ -989,7 +998,7 @@ private:
/** /**
* Definition of a function modifier. * Definition of a function modifier.
*/ */
class ModifierDefinition: public CallableDeclaration, public StructurallyDocumented, public ImplementationOptional class ModifierDefinition: public CallableDeclaration, public StructurallyDocumented, public ImplementationOptional, public ScopeOpener
{ {
public: public:
ModifierDefinition( ModifierDefinition(
@ -1061,7 +1070,7 @@ private:
/** /**
* Definition of a (loggable) event. * Definition of a (loggable) event.
*/ */
class EventDefinition: public CallableDeclaration, public StructurallyDocumented class EventDefinition: public CallableDeclaration, public StructurallyDocumented, public ScopeOpener
{ {
public: public:
EventDefinition( EventDefinition(
@ -1199,7 +1208,7 @@ private:
/** /**
* A literal function type. Its source form is "function (paramType1, paramType2) internal / external returns (retType1, retType2)" * A literal function type. Its source form is "function (paramType1, paramType2) internal / external returns (retType1, retType2)"
*/ */
class FunctionTypeName: public TypeName class FunctionTypeName: public TypeName, public ScopeOpener
{ {
public: public:
FunctionTypeName( FunctionTypeName(
@ -1334,7 +1343,7 @@ private:
/** /**
* Brace-enclosed block containing zero or more statements. * Brace-enclosed block containing zero or more statements.
*/ */
class Block: public Statement, public Scopable class Block: public Statement, public Scopable, public ScopeOpener
{ {
public: public:
Block( Block(
@ -1411,7 +1420,7 @@ private:
* unsuccessful cases. * unsuccessful cases.
* Names are only allowed for the unsuccessful cases. * Names are only allowed for the unsuccessful cases.
*/ */
class TryCatchClause: public ASTNode, public Scopable class TryCatchClause: public ASTNode, public Scopable, public ScopeOpener
{ {
public: public:
TryCatchClause( TryCatchClause(
@ -1526,7 +1535,7 @@ private:
/** /**
* For loop statement * For loop statement
*/ */
class ForStatement: public BreakableStatement, public Scopable class ForStatement: public BreakableStatement, public Scopable, public ScopeOpener
{ {
public: public:
ForStatement( ForStatement(

View File

@ -108,10 +108,10 @@ struct ScopableAnnotation
virtual ~ScopableAnnotation() = default; virtual ~ScopableAnnotation() = default;
/// The scope this declaration resides in. Can be nullptr if it is the global scope. /// The scope this declaration resides in. Can be nullptr if it is the global scope.
/// Available only after name and type resolution step. /// Filled by the Scoper.
ASTNode const* scope = nullptr; ASTNode const* scope = nullptr;
/// Pointer to the contract this declaration resides in. Can be nullptr if the current scope /// Pointer to the contract this declaration resides in. Can be nullptr if the current scope
/// is not part of a contract. Available only after name and type resolution step. /// is not part of a contract. Filled by the Scoper.
ContractDefinition const* contract = nullptr; ContractDefinition const* contract = nullptr;
}; };

View File

@ -38,6 +38,7 @@ namespace solidity::frontend
{ {
class ASTNode; class ASTNode;
class ScopeOpener;
class SourceUnit; class SourceUnit;
class PragmaDirective; class PragmaDirective;
class ImportDirective; class ImportDirective;

View File

@ -205,7 +205,7 @@ vector<frontend::Type const*> solidity::frontend::oversizedSubtypes(frontend::Ty
{ {
set<StructDefinition const*> structsSeen; set<StructDefinition const*> structsSeen;
TypeSet oversized; TypeSet oversized;
oversizedSubtypesInner(_type, false, structsSeen, oversized); oversizedSubtypesInner(_type, true, structsSeen, oversized);
vector<frontend::Type const*> res; vector<frontend::Type const*> res;
copy(oversized.cbegin(), oversized.cend(), back_inserter(res)); copy(oversized.cbegin(), oversized.cend(), back_inserter(res));
return res; return res;
@ -1569,12 +1569,15 @@ BoolResult ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
return true; return true;
if (_convertTo.category() == Category::Contract) if (_convertTo.category() == Category::Contract)
{ {
auto const& bases = contractDefinition().annotation().linearizedBaseContracts; auto const& targetContractType = dynamic_cast<ContractType const&>(_convertTo);
if (m_super && bases.size() <= 1) if (targetContractType.isSuper())
return false; return false;
auto const& bases = contractDefinition().annotation().linearizedBaseContracts;
return find( return find(
m_super ? ++bases.begin() : bases.begin(), bases.end(), bases.begin(),
&dynamic_cast<ContractType const&>(_convertTo).contractDefinition() bases.end(),
&targetContractType.contractDefinition()
) != bases.end(); ) != bases.end();
} }
return false; return false;
@ -3482,12 +3485,12 @@ bool FunctionType::canTakeArguments(
size_t matchedNames = 0; size_t matchedNames = 0;
for (auto const& argName: _arguments.names) for (size_t a = 0; a < _arguments.names.size(); a++)
for (size_t i = 0; i < paramNames.size(); i++) for (size_t p = 0; p < paramNames.size(); p++)
if (*argName == paramNames[i]) if (*_arguments.names[a] == paramNames[p])
{ {
matchedNames++; matchedNames++;
if (!_arguments.types[i]->isImplicitlyConvertibleTo(*paramTypes[i])) if (!_arguments.types[a]->isImplicitlyConvertibleTo(*paramTypes[p]))
return false; return false;
} }

View File

@ -1198,10 +1198,9 @@ string YulUtilFunctions::storageArrayIndexAccessFunction(ArrayType const& _type)
slot := array slot := array
} }
<!isBytesArray> <!isBytesArray>
let itemsPerSlot := div(0x20, <storageBytes>)
let dataArea := <dataAreaFunc>(array) let dataArea := <dataAreaFunc>(array)
slot := add(dataArea, div(index, itemsPerSlot)) slot := add(dataArea, div(index, <itemsPerSlot>))
offset := mod(index, itemsPerSlot) offset := mul(mod(index, <itemsPerSlot>), <storageBytes>)
</isBytesArray> </isBytesArray>
<!multipleItemsPerSlot> <!multipleItemsPerSlot>
let dataArea := <dataAreaFunc>(array) let dataArea := <dataAreaFunc>(array)
@ -1217,6 +1216,7 @@ string YulUtilFunctions::storageArrayIndexAccessFunction(ArrayType const& _type)
("isBytesArray", _type.isByteArray()) ("isBytesArray", _type.isByteArray())
("storageSize", _type.baseType()->storageSize().str()) ("storageSize", _type.baseType()->storageSize().str())
("storageBytes", toString(_type.baseType()->storageBytes())) ("storageBytes", toString(_type.baseType()->storageBytes()))
("itemsPerSlot", to_string(32 / _type.baseType()->storageBytes()))
.render(); .render();
}); });
} }

View File

@ -836,11 +836,11 @@ void BMC::checkCondition(
case smtutil::CheckResult::SATISFIABLE: case smtutil::CheckResult::SATISFIABLE:
{ {
std::ostringstream message; std::ostringstream message;
message << _description << " happens here"; message << _description << " happens here.";
if (_callStack.size()) if (_callStack.size())
{ {
std::ostringstream modelMessage; std::ostringstream modelMessage;
modelMessage << " for:\n"; modelMessage << "\nCounterexample:\n";
solAssert(values.size() == expressionNames.size(), ""); solAssert(values.size() == expressionNames.size(), "");
map<string, string> sortedModel; map<string, string> sortedModel;
for (size_t i = 0; i < values.size(); ++i) for (size_t i = 0; i < values.size(); ++i)
@ -859,10 +859,7 @@ void BMC::checkCondition(
); );
} }
else else
{
message << ".";
m_errorReporter.warning(6084_error, _location, message.str(), secondaryLocation); m_errorReporter.warning(6084_error, _location, message.str(), secondaryLocation);
}
break; break;
} }
case smtutil::CheckResult::UNSATISFIABLE: case smtutil::CheckResult::UNSATISFIABLE:

View File

@ -43,19 +43,19 @@ using namespace solidity::frontend;
CHC::CHC( CHC::CHC(
smt::EncodingContext& _context, smt::EncodingContext& _context,
ErrorReporter& _errorReporter, ErrorReporter& _errorReporter,
map<util::h256, string> const& _smtlib2Responses, [[maybe_unused]] map<util::h256, string> const& _smtlib2Responses,
ReadCallback::Callback const& _smtCallback, [[maybe_unused]] ReadCallback::Callback const& _smtCallback,
[[maybe_unused]] smtutil::SMTSolverChoice _enabledSolvers smtutil::SMTSolverChoice _enabledSolvers
): ):
SMTEncoder(_context), SMTEncoder(_context),
m_outerErrorReporter(_errorReporter), m_outerErrorReporter(_errorReporter),
m_enabledSolvers(_enabledSolvers) m_enabledSolvers(_enabledSolvers)
{ {
#ifdef HAVE_Z3 bool usesZ3 = _enabledSolvers.z3;
if (_enabledSolvers.z3) #ifndef HAVE_Z3
m_interface = make_unique<smtutil::Z3CHCInterface>(); usesZ3 = false;
#endif #endif
if (!m_interface) if (!usesZ3)
m_interface = make_unique<smtutil::CHCSmtLib2Interface>(_smtlib2Responses, _smtCallback); m_interface = make_unique<smtutil::CHCSmtLib2Interface>(_smtlib2Responses, _smtCallback);
} }
@ -63,30 +63,8 @@ void CHC::analyze(SourceUnit const& _source)
{ {
solAssert(_source.annotation().experimentalFeatures.count(ExperimentalFeature::SMTChecker), ""); solAssert(_source.annotation().experimentalFeatures.count(ExperimentalFeature::SMTChecker), "");
bool usesZ3 = false;
#ifdef HAVE_Z3
usesZ3 = m_enabledSolvers.z3;
if (usesZ3)
{
auto z3Interface = dynamic_cast<smtutil::Z3CHCInterface const*>(m_interface.get());
solAssert(z3Interface, "");
m_context.setSolver(z3Interface->z3Interface());
}
#endif
if (!usesZ3)
{
auto smtlib2Interface = dynamic_cast<smtutil::CHCSmtLib2Interface const*>(m_interface.get());
solAssert(smtlib2Interface, "");
m_context.setSolver(smtlib2Interface->smtlib2Interface());
}
m_context.clear();
m_context.setAssertionAccumulation(false);
resetSourceAnalysis(); resetSourceAnalysis();
m_genesisPredicate = createSymbolicBlock(arity0FunctionSort(), "genesis");
addRule(genesis(), "genesis");
set<SourceUnit const*, IdCompare> sources; set<SourceUnit const*, IdCompare> sources;
sources.insert(&_source); sources.insert(&_source);
for (auto const& source: _source.referencedSourceUnits(true)) for (auto const& source: _source.referencedSourceUnits(true))
@ -131,7 +109,7 @@ bool CHC::visit(ContractDefinition const& _contract)
void CHC::endVisit(ContractDefinition const& _contract) void CHC::endVisit(ContractDefinition const& _contract)
{ {
auto implicitConstructor = (*m_implicitConstructorPredicate)({}); auto implicitConstructor = (*m_implicitConstructorPredicate)({});
connectBlocks(genesis(), implicitConstructor); addRule(implicitConstructor, implicitConstructor.name);
m_currentBlock = implicitConstructor; m_currentBlock = implicitConstructor;
m_context.addAssertion(m_error.currentValue() == 0); m_context.addAssertion(m_error.currentValue() == 0);
@ -156,7 +134,7 @@ bool CHC::visit(FunctionDefinition const& _function)
{ {
if (!_function.isImplemented()) if (!_function.isImplemented())
{ {
connectBlocks(genesis(), summary(_function)); addRule(summary(_function), "summary_function_" + to_string(_function.id()));
return false; return false;
} }
@ -184,7 +162,7 @@ bool CHC::visit(FunctionDefinition const& _function)
if (_function.isConstructor()) if (_function.isConstructor())
connectBlocks(m_currentBlock, functionPred); connectBlocks(m_currentBlock, functionPred);
else else
connectBlocks(genesis(), functionPred); addRule(functionPred, functionPred.name);
m_context.addAssertion(m_error.currentValue() == 0); m_context.addAssertion(m_error.currentValue() == 0);
for (auto const* var: m_stateVariables) for (auto const* var: m_stateVariables)
@ -676,6 +654,30 @@ void CHC::resetSourceAnalysis()
m_interfaces.clear(); m_interfaces.clear();
m_nondetInterfaces.clear(); m_nondetInterfaces.clear();
Predicate::reset(); Predicate::reset();
m_blockCounter = 0;
bool usesZ3 = false;
#ifdef HAVE_Z3
usesZ3 = m_enabledSolvers.z3;
if (usesZ3)
{
/// z3::fixedpoint does not have a reset mechanism, so we need to create another.
m_interface.reset(new smtutil::Z3CHCInterface());
auto z3Interface = dynamic_cast<smtutil::Z3CHCInterface const*>(m_interface.get());
solAssert(z3Interface, "");
m_context.setSolver(z3Interface->z3Interface());
}
#endif
if (!usesZ3)
{
auto smtlib2Interface = dynamic_cast<smtutil::CHCSmtLib2Interface*>(m_interface.get());
smtlib2Interface->reset();
solAssert(smtlib2Interface, "");
m_context.setSolver(smtlib2Interface->smtlib2Interface());
}
m_context.clear();
m_context.setAssertionAccumulation(false);
} }
void CHC::resetContractAnalysis() void CHC::resetContractAnalysis()
@ -861,27 +863,23 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source)
{ {
for (auto const& node: _source.nodes()) for (auto const& node: _source.nodes())
if (auto const* contract = dynamic_cast<ContractDefinition const*>(node.get())) if (auto const* contract = dynamic_cast<ContractDefinition const*>(node.get()))
for (auto const* base: contract->annotation().linearizedBaseContracts)
{ {
for (auto const* var: SMTEncoder::stateVariablesIncludingInheritedAndPrivate(*base)) string suffix = contract->name() + "_" + to_string(contract->id());
m_interfaces[contract] = createSymbolicBlock(interfaceSort(*contract), "interface_" + suffix);
m_nondetInterfaces[contract] = createSymbolicBlock(nondetInterfaceSort(*contract), "nondet_interface_" + suffix);
for (auto const* var: stateVariablesIncludingInheritedAndPrivate(*contract))
if (!m_context.knownVariable(*var)) if (!m_context.knownVariable(*var))
createVariable(*var); createVariable(*var);
if (!m_interfaces.count(base))
{
solAssert(!m_nondetInterfaces.count(base), "");
string suffix = base->name() + "_" + to_string(base->id());
m_interfaces.emplace(base, createSymbolicBlock(interfaceSort(*base), "interface_" + suffix, base));
m_nondetInterfaces.emplace(base, createSymbolicBlock(nondetInterfaceSort(*base), "nondet_interface_" + suffix, base));
/// Base nondeterministic interface that allows /// Base nondeterministic interface that allows
/// 0 steps to be taken, used as base for the inductive /// 0 steps to be taken, used as base for the inductive
/// rule for each function. /// rule for each function.
auto const* iface = m_nondetInterfaces.at(base); auto const& iface = *m_nondetInterfaces.at(contract);
auto state0 = stateVariablesAtIndex(0, *base); auto state0 = stateVariablesAtIndex(0, *contract);
addRule((*iface)(state0 + state0), "base_nondet"); addRule(iface(state0 + state0), "base_nondet");
}
for (auto const* base: contract->annotation().linearizedBaseContracts)
for (auto const* function: base->definedFunctions()) for (auto const* function: base->definedFunctions())
{ {
for (auto var: function->parameters()) for (auto var: function->parameters())
@ -900,13 +898,11 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source)
!base->isInterface() !base->isInterface()
) )
{ {
auto state1 = stateVariablesAtIndex(1, *base); auto state1 = stateVariablesAtIndex(1, *contract);
auto state2 = stateVariablesAtIndex(2, *base); auto state2 = stateVariablesAtIndex(2, *contract);
auto const* iface = m_nondetInterfaces.at(base); auto nondetPre = iface(state0 + state1);
auto state0 = stateVariablesAtIndex(0, *base); auto nondetPost = iface(state0 + state2);
auto nondetPre = (*iface)(state0 + state1);
auto nondetPost = (*iface)(state0 + state2);
vector<smtutil::Expression> args{m_error.currentValue()}; vector<smtutil::Expression> args{m_error.currentValue()};
args += state1 + args += state1 +
@ -915,7 +911,7 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source)
applyMap(function->parameters(), [this](auto _var) { return valueAtIndex(*_var, 1); }) + applyMap(function->parameters(), [this](auto _var) { return valueAtIndex(*_var, 1); }) +
applyMap(function->returnParameters(), [this](auto _var) { return valueAtIndex(*_var, 1); }); applyMap(function->returnParameters(), [this](auto _var) { return valueAtIndex(*_var, 1); });
connectBlocks(nondetPre, nondetPost, (*m_summaries.at(base).at(function))(args)); connectBlocks(nondetPre, nondetPost, (*m_summaries.at(contract).at(function))(args));
} }
} }
} }
@ -1210,6 +1206,16 @@ void CHC::addVerificationTarget(
smtutil::Expression _errorId smtutil::Expression _errorId
) )
{ {
solAssert(m_currentContract || m_currentFunction, "");
SourceUnit const* source = nullptr;
if (m_currentContract)
source = sourceUnitContaining(*m_currentContract);
else
source = sourceUnitContaining(*m_currentFunction);
solAssert(source, "");
if (!source->annotation().experimentalFeatures.count(ExperimentalFeature::SMTChecker))
return;
m_verificationTargets.emplace(_scope, CHCVerificationTarget{{_type, _from, _constraints}, _errorId}); m_verificationTargets.emplace(_scope, CHCVerificationTarget{{_type, _from, _constraints}, _errorId});
} }
@ -1251,7 +1257,7 @@ void CHC::checkVerificationTargets()
if (target.type == VerificationTarget::Type::PopEmptyArray) if (target.type == VerificationTarget::Type::PopEmptyArray)
{ {
solAssert(dynamic_cast<FunctionCall const*>(scope), ""); solAssert(dynamic_cast<FunctionCall const*>(scope), "");
satMsg = "Empty array \"pop\" detected here"; satMsg = "Empty array \"pop\" detected here.";
unknownMsg = "Empty array \"pop\" might happen here."; unknownMsg = "Empty array \"pop\" might happen here.";
errorReporterId = 2529_error; errorReporterId = 2529_error;
} }
@ -1267,8 +1273,8 @@ void CHC::checkVerificationTargets()
if (!intType) if (!intType)
intType = TypeProvider::uint256(); intType = TypeProvider::uint256();
satMsgUnderflow = "Underflow (resulting value less than " + formatNumberReadable(intType->minValue()) + ") happens here"; satMsgUnderflow = "Underflow (resulting value less than " + formatNumberReadable(intType->minValue()) + ") happens here.";
satMsgOverflow = "Overflow (resulting value larger than " + formatNumberReadable(intType->maxValue()) + ") happens here"; satMsgOverflow = "Overflow (resulting value larger than " + formatNumberReadable(intType->maxValue()) + ") happens here.";
if (target.type == VerificationTarget::Type::Underflow) if (target.type == VerificationTarget::Type::Underflow)
{ {
satMsg = satMsgUnderflow; satMsg = satMsgUnderflow;
@ -1314,7 +1320,7 @@ void CHC::checkAssertTarget(ASTNode const* _scope, CHCVerificationTarget const&
solAssert(it != m_errorIds.end(), ""); solAssert(it != m_errorIds.end(), "");
unsigned errorId = it->second; unsigned errorId = it->second;
checkAndReportTarget(assertion, _target, errorId, 6328_error, "Assertion violation happens here"); checkAndReportTarget(assertion, _target, errorId, 6328_error, "Assertion violation happens here.");
} }
} }
@ -1345,13 +1351,13 @@ void CHC::checkAndReportTarget(
_errorReporterId, _errorReporterId,
_scope->location(), _scope->location(),
_satMsg, _satMsg,
SecondarySourceLocation().append(" for:\n" + *cex, SourceLocation{}) SecondarySourceLocation().append("\nCounterexample:\n" + *cex, SourceLocation{})
); );
else else
m_outerErrorReporter.warning( m_outerErrorReporter.warning(
_errorReporterId, _errorReporterId,
_scope->location(), _scope->location(),
_satMsg + "." _satMsg
); );
} }
else if (!_unknownMsg.empty()) else if (!_unknownMsg.empty())
@ -1459,7 +1465,11 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const&
/// Recurse on the next interface node which represents the previous transaction /// Recurse on the next interface node which represents the previous transaction
/// or stop. /// or stop.
if (interfaceId) if (interfaceId)
{
Predicate const* interfacePredicate = Predicate::predicate(_graph.nodes.at(*interfaceId).first);
solAssert(interfacePredicate && interfacePredicate->isInterface(), "");
node = *interfaceId; node = *interfaceId;
}
else else
break; break;
} }

View File

@ -141,8 +141,6 @@ private:
/// in a given _source. /// in a given _source.
void defineInterfacesAndSummaries(SourceUnit const& _source); void defineInterfacesAndSummaries(SourceUnit const& _source);
/// Genesis predicate.
smtutil::Expression genesis() { return (*m_genesisPredicate)({}); }
/// Interface predicate over current variables. /// Interface predicate over current variables.
smtutil::Expression interface(); smtutil::Expression interface();
smtutil::Expression interface(ContractDefinition const& _contract); smtutil::Expression interface(ContractDefinition const& _contract);
@ -259,9 +257,6 @@ private:
/// Predicates. /// Predicates.
//@{ //@{
/// Genesis predicate.
Predicate const* m_genesisPredicate = nullptr;
/// Implicit constructor predicate. /// Implicit constructor predicate.
/// Explicit constructors are handled as functions. /// Explicit constructors are handled as functions.
Predicate const* m_implicitConstructorPredicate = nullptr; Predicate const* m_implicitConstructorPredicate = nullptr;
@ -293,9 +288,6 @@ private:
"error", "error",
m_context m_context
}; };
/// Maps predicate names to the ASTNodes they came from.
std::map<std::string, ASTNode const*> m_symbolFunction;
//@} //@}
/// Variables. /// Variables.

View File

@ -133,9 +133,6 @@ bool SMTEncoder::visit(FunctionDefinition const& _function)
if (_function.isConstructor()) if (_function.isConstructor())
inlineConstructorHierarchy(dynamic_cast<ContractDefinition const&>(*_function.scope())); inlineConstructorHierarchy(dynamic_cast<ContractDefinition const&>(*_function.scope()));
// Base constructors' parameters should be set by explicit calls,
// but the most derived one needs to be initialized.
if (_function.scope() == m_currentContract)
initializeLocalVariables(_function); initializeLocalVariables(_function);
_function.parameterList().accept(*this); _function.parameterList().accept(*this);
@ -571,7 +568,7 @@ void SMTEncoder::endVisit(BinaryOperation const& _op)
arithmeticOperation(_op); arithmeticOperation(_op);
else if (TokenTraits::isCompareOp(_op.getOperator())) else if (TokenTraits::isCompareOp(_op.getOperator()))
compareOperation(_op); compareOperation(_op);
else if (TokenTraits::isBitOp(_op.getOperator())) else if (TokenTraits::isBitOp(_op.getOperator()) || TokenTraits::isShiftOp(_op.getOperator()))
bitwiseOperation(_op); bitwiseOperation(_op);
else else
m_errorReporter.warning( m_errorReporter.warning(
@ -881,16 +878,31 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess)
auto identifier = dynamic_cast<Identifier const*>(&_memberAccess.expression()); auto identifier = dynamic_cast<Identifier const*>(&_memberAccess.expression());
if (exprType->category() == Type::Category::Magic) if (exprType->category() == Type::Category::Magic)
{ {
string accessedName;
if (identifier) if (identifier)
accessedName = identifier->name(); defineGlobalVariable(identifier->name() + "." + _memberAccess.memberName(), _memberAccess);
else if (auto magicType = dynamic_cast<MagicType const*>(exprType); magicType->kind() == MagicType::Kind::MetaType)
{
auto const& memberName = _memberAccess.memberName();
if (memberName == "min" || memberName == "max")
{
IntegerType const& integerType = dynamic_cast<IntegerType const&>(*magicType->typeArgument());
defineExpr(_memberAccess, memberName == "min" ? integerType.minValue() : integerType.maxValue());
}
else
// NOTE: supporting name, creationCode, runtimeCode would be easy enough, but the bytes/string they return are not
// at all useable in the SMT checker currently
m_errorReporter.warning(
7507_error,
_memberAccess.location(),
"Assertion checker does not yet support this expression."
);
}
else else
m_errorReporter.warning( m_errorReporter.warning(
9551_error, 9551_error,
_memberAccess.location(), _memberAccess.location(),
"Assertion checker does not yet support this expression." "Assertion checker does not yet support this expression."
); );
defineGlobalVariable(accessedName + "." + _memberAccess.memberName(), _memberAccess);
return false; return false;
} }
else if (smt::isNonRecursiveStruct(*exprType)) else if (smt::isNonRecursiveStruct(*exprType))
@ -1422,7 +1434,8 @@ void SMTEncoder::booleanOperation(BinaryOperation const& _op)
void SMTEncoder::bitwiseOperation(BinaryOperation const& _op) void SMTEncoder::bitwiseOperation(BinaryOperation const& _op)
{ {
solAssert(TokenTraits::isBitOp(_op.getOperator()), ""); auto op = _op.getOperator();
solAssert(TokenTraits::isBitOp(op) || TokenTraits::isShiftOp(op), "");
auto commonType = _op.annotation().commonType; auto commonType = _op.annotation().commonType;
solAssert(commonType, ""); solAssert(commonType, "");
@ -1432,15 +1445,32 @@ void SMTEncoder::bitwiseOperation(BinaryOperation const& _op)
auto bvRight = smtutil::Expression::int2bv(expr(_op.rightExpression(), commonType), bvSize); auto bvRight = smtutil::Expression::int2bv(expr(_op.rightExpression(), commonType), bvSize);
optional<smtutil::Expression> result; optional<smtutil::Expression> result;
if (_op.getOperator() == Token::BitAnd) switch (op)
{
case Token::BitAnd:
result = bvLeft & bvRight; result = bvLeft & bvRight;
else if (_op.getOperator() == Token::BitOr) break;
case Token::BitOr:
result = bvLeft | bvRight; result = bvLeft | bvRight;
else if (_op.getOperator() == Token::BitXor) break;
case Token::BitXor:
result = bvLeft ^ bvRight; result = bvLeft ^ bvRight;
break;
case Token::SHL:
result = bvLeft << bvRight;
break;
case Token::SHR:
solAssert(false, "");
case Token::SAR:
result = isSigned ?
smtutil::Expression::ashr(bvLeft, bvRight) :
bvLeft >> bvRight;
break;
default:
solAssert(false, "");
}
solAssert(result, ""); solAssert(result, "");
if (result)
defineExpr(_op, smtutil::Expression::bv2int(*result, isSigned)); defineExpr(_op, smtutil::Expression::bv2int(*result, isSigned));
} }
@ -2022,6 +2052,14 @@ vector<VariableDeclaration const*> SMTEncoder::stateVariablesIncludingInheritedA
return stateVariablesIncludingInheritedAndPrivate(dynamic_cast<ContractDefinition const&>(*_function.scope())); return stateVariablesIncludingInheritedAndPrivate(dynamic_cast<ContractDefinition const&>(*_function.scope()));
} }
SourceUnit const* SMTEncoder::sourceUnitContaining(Scopable const& _scopable)
{
for (auto const* s = &_scopable; s; s = dynamic_cast<Scopable const*>(s->scope()))
if (auto const* source = dynamic_cast<SourceUnit const*>(s->scope()))
return source;
solAssert(false, "");
}
void SMTEncoder::createReturnedExpressions(FunctionCall const& _funCall) void SMTEncoder::createReturnedExpressions(FunctionCall const& _funCall)
{ {
FunctionDefinition const* funDef = functionCallToDefinition(_funCall); FunctionDefinition const* funDef = functionCallToDefinition(_funCall);

View File

@ -65,6 +65,9 @@ public:
static std::vector<VariableDeclaration const*> stateVariablesIncludingInheritedAndPrivate(ContractDefinition const& _contract); static std::vector<VariableDeclaration const*> stateVariablesIncludingInheritedAndPrivate(ContractDefinition const& _contract);
static std::vector<VariableDeclaration const*> stateVariablesIncludingInheritedAndPrivate(FunctionDefinition const& _function); static std::vector<VariableDeclaration const*> stateVariablesIncludingInheritedAndPrivate(FunctionDefinition const& _function);
/// @returns the SourceUnit that contains _scopable.
static SourceUnit const* sourceUnitContaining(Scopable const& _scopable);
protected: protected:
// TODO: Check that we do not have concurrent reads and writes to a variable, // TODO: Check that we do not have concurrent reads and writes to a variable,
// because the order of expression evaluation is undefined // because the order of expression evaluation is undefined

View File

@ -36,6 +36,7 @@
#include <libsolidity/analysis/PostTypeChecker.h> #include <libsolidity/analysis/PostTypeChecker.h>
#include <libsolidity/analysis/StaticAnalyzer.h> #include <libsolidity/analysis/StaticAnalyzer.h>
#include <libsolidity/analysis/SyntaxChecker.h> #include <libsolidity/analysis/SyntaxChecker.h>
#include <libsolidity/analysis/Scoper.h>
#include <libsolidity/analysis/TypeChecker.h> #include <libsolidity/analysis/TypeChecker.h>
#include <libsolidity/analysis/ViewPureChecker.h> #include <libsolidity/analysis/ViewPureChecker.h>
#include <libsolidity/analysis/ImmutableValidator.h> #include <libsolidity/analysis/ImmutableValidator.h>
@ -297,6 +298,10 @@ bool CompilerStack::analyze()
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must call analyze only after parsing was performed.")); BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Must call analyze only after parsing was performed."));
resolveImports(); resolveImports();
for (Source const* source: m_sourceOrder)
if (source->ast)
Scoper::assignScopes(*source->ast);
bool noErrors = true; bool noErrors = true;
try try

View File

@ -32,7 +32,7 @@ namespace solidity::frontend
struct OptimiserSettings struct OptimiserSettings
{ {
static char constexpr DefaultYulOptimiserSteps[] = static char constexpr DefaultYulOptimiserSteps[] =
"dhfoDgvulfnTUtnIf" // None of these can make stack problems worse "NdhfoDgvulfnTUtnIf" // None of these can make stack problems worse
"[" "["
"xarrscLM" // Turn into SSA and simplify "xarrscLM" // Turn into SSA and simplify
"cCTUtTOntnfDIul" // Perform structural simplification "cCTUtTOntnfDIul" // Perform structural simplification
@ -47,7 +47,7 @@ struct OptimiserSettings
"gvif" // Run full inliner "gvif" // Run full inliner
"CTUcarrLsTOtfDncarrIulc" // SSA plus simplify "CTUcarrLsTOtfDncarrIulc" // SSA plus simplify
"]" "]"
"jmuljuljul VcTOcul jmul"; // Make source short and pretty "jmuljuljul VcTOcul jmulN"; // Make source short and pretty
/// No optimisations at all - not recommended. /// No optimisations at all - not recommended.
static OptimiserSettings none() static OptimiserSettings none()

View File

@ -127,6 +127,8 @@ add_library(yul
optimiser/NameDispenser.h optimiser/NameDispenser.h
optimiser/NameDisplacer.cpp optimiser/NameDisplacer.cpp
optimiser/NameDisplacer.h optimiser/NameDisplacer.h
optimiser/NameSimplifier.cpp
optimiser/NameSimplifier.h
optimiser/OptimiserStep.h optimiser/OptimiserStep.h
optimiser/OptimizerUtilities.cpp optimiser/OptimizerUtilities.cpp
optimiser/OptimizerUtilities.h optimiser/OptimizerUtilities.h

View File

@ -22,12 +22,9 @@
#include <libyul/optimiser/ExpressionSimplifier.h> #include <libyul/optimiser/ExpressionSimplifier.h>
#include <libyul/optimiser/SimplificationRules.h> #include <libyul/optimiser/SimplificationRules.h>
#include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/OptimiserStep.h> #include <libyul/optimiser/OptimiserStep.h>
#include <libyul/AsmData.h> #include <libyul/AsmData.h>
#include <libsolutil/CommonData.h>
using namespace std; using namespace std;
using namespace solidity; using namespace solidity;
using namespace solidity::yul; using namespace solidity::yul;
@ -40,17 +37,7 @@ void ExpressionSimplifier::run(OptimiserStepContext& _context, Block& _ast)
void ExpressionSimplifier::visit(Expression& _expression) void ExpressionSimplifier::visit(Expression& _expression)
{ {
ASTModifier::visit(_expression); ASTModifier::visit(_expression);
while (auto match = SimplificationRules::findFirstMatch(_expression, m_dialect, m_value))
{
// Do not apply the rule if it removes non-constant parts of the expression.
// TODO: The check could actually be less strict than "movable".
// We only require "Does not cause side-effects".
// Note: References to variables that are only assigned once are always movable,
// so if the value of the variable is not movable, the expression that references
// the variable still is.
if (match->removesNonConstants && !SideEffectsCollector(m_dialect, _expression).movable()) while (auto const* match = SimplificationRules::findFirstMatch(_expression, m_dialect, m_value))
return;
_expression = match->action().toExpression(locationOf(_expression)); _expression = match->action().toExpression(locationOf(_expression));
}
} }

View File

@ -33,7 +33,8 @@ struct OptimiserStepContext;
/** /**
* Applies simplification rules to all expressions. * Applies simplification rules to all expressions.
* The component will work best if the code is in SSA form, but * The component will work best if the code is in SSA form, but
* this is not required for correctness. * this is not required for correctness. Using CommonSubexpressionEliminator
* also helps this component track equivalent sub-expressions.
* *
* It tracks the current values of variables using the DataFlowAnalyzer * It tracks the current values of variables using the DataFlowAnalyzer
* and takes them into account for replacements. * and takes them into account for replacements.

View File

@ -28,6 +28,7 @@
#include <libyul/optimiser/Metrics.h> #include <libyul/optimiser/Metrics.h>
#include <libyul/optimiser/SSAValueTracker.h> #include <libyul/optimiser/SSAValueTracker.h>
#include <libyul/optimiser/Semantics.h> #include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/CallGraphGenerator.h>
#include <libyul/Exceptions.h> #include <libyul/Exceptions.h>
#include <libyul/AsmData.h> #include <libyul/AsmData.h>
#include <libyul/Dialect.h> #include <libyul/Dialect.h>
@ -41,7 +42,9 @@ using namespace solidity::yul;
void FullInliner::run(OptimiserStepContext& _context, Block& _ast) void FullInliner::run(OptimiserStepContext& _context, Block& _ast)
{ {
FullInliner{_ast, _context.dispenser, _context.dialect}.run(); FullInliner inliner{_ast, _context.dispenser, _context.dialect};
inliner.run(Pass::InlineTiny);
inliner.run(Pass::InlineRest);
} }
FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser, Dialect const& _dialect): FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser, Dialect const& _dialect):
@ -72,14 +75,9 @@ FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser, Dialect const&
} }
} }
void FullInliner::run() void FullInliner::run(Pass _pass)
{ {
for (auto& statement: m_ast.statements) m_pass = _pass;
if (holds_alternative<Block>(statement))
handleBlock({}, std::get<Block>(statement));
// TODO it might be good to determine a visiting order:
// first handle functions that are called from many places.
// Note that the order of inlining can result in very different code. // Note that the order of inlining can result in very different code.
// Since AST IDs and thus function names depend on whether or not a contract // Since AST IDs and thus function names depend on whether or not a contract
@ -87,14 +85,76 @@ void FullInliner::run()
// should have as little an impact as possible. This is the case // should have as little an impact as possible. This is the case
// if we handle inlining in source (and thus, for the IR generator, // if we handle inlining in source (and thus, for the IR generator,
// function name) order. // function name) order.
// We use stable_sort below to keep the inlining order of two functions
// with the same depth.
map<YulString, size_t> depths = callDepths();
vector<FunctionDefinition*> functions;
for (auto& statement: m_ast.statements) for (auto& statement: m_ast.statements)
if (holds_alternative<FunctionDefinition>(statement))
functions.emplace_back(&std::get<FunctionDefinition>(statement));
std::stable_sort(functions.begin(), functions.end(), [depths](
FunctionDefinition const* _a,
FunctionDefinition const* _b
) {
return depths.at(_a->name) < depths.at(_b->name);
});
for (FunctionDefinition* fun: functions)
{ {
if (!holds_alternative<FunctionDefinition>(statement)) handleBlock(fun->name, fun->body);
continue; updateCodeSize(*fun);
FunctionDefinition& fun = std::get<FunctionDefinition>(statement);
handleBlock(fun.name, fun.body);
updateCodeSize(fun);
} }
for (auto& statement: m_ast.statements)
if (holds_alternative<Block>(statement))
handleBlock({}, std::get<Block>(statement));
}
map<YulString, size_t> FullInliner::callDepths() const
{
CallGraph cg = CallGraphGenerator::callGraph(m_ast);
cg.functionCalls.erase(""_yulstring);
// Remove calls to builtin functions.
for (auto& call: cg.functionCalls)
for (auto it = call.second.begin(); it != call.second.end();)
if (m_dialect.builtin(*it))
it = call.second.erase(it);
else
++it;
map<YulString, size_t> depths;
size_t currentDepth = 0;
while (true)
{
vector<YulString> removed;
for (auto it = cg.functionCalls.begin(); it != cg.functionCalls.end();)
{
auto const& [fun, callees] = *it;
if (callees.empty())
{
removed.emplace_back(fun);
depths[fun] = currentDepth;
it = cg.functionCalls.erase(it);
}
else
++it;
}
for (auto& call: cg.functionCalls)
call.second -= removed;
currentDepth++;
if (removed.empty())
break;
}
// Only recursive functions left here.
for (auto const& fun: cg.functionCalls)
depths[fun.first] = currentDepth;
return depths;
} }
bool FullInliner::shallInline(FunctionCall const& _funCall, YulString _callSite) bool FullInliner::shallInline(FunctionCall const& _funCall, YulString _callSite)
@ -115,6 +175,10 @@ bool FullInliner::shallInline(FunctionCall const& _funCall, YulString _callSite)
if (size <= 1) if (size <= 1)
return true; return true;
// In the first pass, only inline tiny functions.
if (m_pass == Pass::InlineTiny)
return false;
// Do not inline into already big functions. // Do not inline into already big functions.
if (m_functionSizes.at(_callSite) > 45) if (m_functionSizes.at(_callSite) > 45)
return false; return false;

View File

@ -91,13 +91,20 @@ public:
void tentativelyUpdateCodeSize(YulString _function, YulString _callSite); void tentativelyUpdateCodeSize(YulString _function, YulString _callSite);
private: private:
enum Pass { InlineTiny, InlineRest };
FullInliner(Block& _ast, NameDispenser& _dispenser, Dialect const& _dialect); FullInliner(Block& _ast, NameDispenser& _dispenser, Dialect const& _dialect);
void run(); void run(Pass _pass);
/// @returns a map containing the maximum depths of a call chain starting at each
/// function. For recursive functions, the value is one larger than for all others.
std::map<YulString, size_t> callDepths() const;
void updateCodeSize(FunctionDefinition const& _fun); void updateCodeSize(FunctionDefinition const& _fun);
void handleBlock(YulString _currentFunctionName, Block& _block); void handleBlock(YulString _currentFunctionName, Block& _block);
bool recursive(FunctionDefinition const& _fun) const; bool recursive(FunctionDefinition const& _fun) const;
Pass m_pass;
/// The AST to be modified. The root block itself will not be modified, because /// The AST to be modified. The root block itself will not be modified, because
/// we store pointers to functions. /// we store pointers to functions.
Block& m_ast; Block& m_ast;

View File

@ -0,0 +1,122 @@
/*
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/NameSimplifier.h>
#include <libyul/optimiser/NameCollector.h>
#include <libyul/AsmData.h>
#include <libyul/Dialect.h>
#include <libyul/optimiser/OptimizerUtilities.h>
#include <libsolutil/CommonData.h>
#include <regex>
using namespace solidity::yul;
using namespace std;
NameSimplifier::NameSimplifier(
OptimiserStepContext& _context,
Block const& _ast
):
m_context(_context),
m_usedNames(_context.reservedIdentifiers)
{
for (YulString name: m_usedNames)
m_translations[name] = name;
set<YulString> allNames = NameCollector(_ast).names();
m_usedNames += allNames;
for (YulString name: allNames)
findSimplification(name);
}
void NameSimplifier::operator()(FunctionDefinition& _funDef)
{
translate(_funDef.name);
renameVariables(_funDef.parameters);
renameVariables(_funDef.returnVariables);
ASTModifier::operator()(_funDef);
}
void NameSimplifier::operator()(VariableDeclaration& _varDecl)
{
renameVariables(_varDecl.variables);
ASTModifier::operator()(_varDecl);
}
void NameSimplifier::renameVariables(vector<TypedName>& _variables)
{
for (TypedName& typedName: _variables)
translate(typedName.name);
}
void NameSimplifier::operator()(Identifier& _identifier)
{
translate(_identifier.name);
}
void NameSimplifier::operator()(FunctionCall& _funCall)
{
// The visitor on its own does not visit the function name.
if (!m_context.dialect.builtin(_funCall.functionName.name))
(*this)(_funCall.functionName);
ASTModifier::operator()(_funCall);
}
void NameSimplifier::findSimplification(YulString _name)
{
if (m_translations.count(_name))
return;
string name = _name.str();
static auto replacements = vector<pair<regex, string>>{
{regex("_\\$\\d+"), ""}, // removes AST IDs
{regex("(abi_..code.*)_to_.*"), "$1"}, // removes _to... for abi functions
{regex("(stringliteral_[0-9a-f][0-9a-f][0-9a-f][0-9a-f])[0-9a-f]*"), "$1"}, // shorten string literal
{regex("tuple_t_"), ""},
{regex("_memory_ptr"), ""},
{regex("_calldata_ptr"), "_calldata"},
{regex("_fromStack"), ""},
{regex("_storage_storage"), "_storage"},
{regex("_memory_memory"), "_memory"},
{regex("t_contract\\$_([^_]*)_"), "$1_"},
{regex("index_access_t_array"), "index_access"},
{regex("[0-9]*_$"), ""}
};
for (auto const& [pattern, substitute]: replacements)
{
string candidate = regex_replace(name, pattern, substitute);
if (
!isRestrictedIdentifier(m_context.dialect, YulString(candidate)) &&
!m_usedNames.count(YulString(candidate))
)
name = candidate;
}
if (name != _name.str())
{
m_usedNames.insert(YulString(name));
m_translations[_name] = YulString(name);
}
}
void NameSimplifier::translate(YulString& _name)
{
auto it = m_translations.find(_name);
if (it != m_translations.end())
_name = it->second;
}

View File

@ -0,0 +1,76 @@
/*
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
#pragma once
#include <libyul/AsmDataForward.h>
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/YulString.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <map>
#include <set>
#include <string>
namespace solidity::yul
{
struct Dialect;
/**
* Pass to "simplify" all identifier names.
*
* The purpose of this is to make generated code more readable, but also
* to remove AST identifiers that could lead to a different sorting order
* and thus influence e.g. the order of function inlining.
*
* Prerequisites: Disambiguator, FunctionHoister, FunctionGrouper
*/
class NameSimplifier: public ASTModifier
{
public:
static constexpr char const* name{"NameSimplifier"};
static void run(OptimiserStepContext& _context, Block& _ast)
{
NameSimplifier{_context, _ast}(_ast);
}
using ASTModifier::operator();
void operator()(VariableDeclaration& _varDecl) override;
void operator()(Identifier& _identifier) override;
void operator()(FunctionCall& _funCall) override;
void operator()(FunctionDefinition& _funDef) override;
private:
NameSimplifier(
OptimiserStepContext& _context,
Block const& _ast
);
/// Tries to rename a list of variables.
void renameVariables(std::vector<TypedName>& _variables);
void findSimplification(YulString _name);
void translate(YulString& _name);
OptimiserStepContext& m_context;
std::set<YulString> m_usedNames;
std::map<YulString, YulString> m_translations;
};
}

View File

@ -171,12 +171,22 @@ bool Pattern::matches(
return false; return false;
assertThrow(m_arguments.size() == instrAndArgs->second->size(), OptimizerException, ""); assertThrow(m_arguments.size() == instrAndArgs->second->size(), OptimizerException, "");
for (size_t i = 0; i < m_arguments.size(); ++i) for (size_t i = 0; i < m_arguments.size(); ++i)
if (!m_arguments[i].matches(instrAndArgs->second->at(i), _dialect, _ssaValues)) {
Expression const& arg = instrAndArgs->second->at(i);
// If this is a direct function call instead of a variable or literal,
// we reject the match because side-effects could prevent us from
// arbitrarily modifying the code.
if (
holds_alternative<FunctionCall>(arg) ||
!m_arguments[i].matches(arg, _dialect, _ssaValues)
)
return false; return false;
} }
}
else else
{ {
assertThrow(m_arguments.empty(), OptimizerException, "\"Any\" should not have arguments."); assertThrow(m_arguments.empty(), OptimizerException, "\"Any\" should not have arguments.");
assertThrow(!holds_alternative<FunctionCall>(*expr), OptimizerException, "\"Any\" at top-level.");
} }
if (m_matchGroup) if (m_matchGroup)
@ -197,9 +207,14 @@ bool Pattern::matches(
assertThrow(m_kind == PatternKind::Any, OptimizerException, "Match group repetition for non-any."); assertThrow(m_kind == PatternKind::Any, OptimizerException, "Match group repetition for non-any.");
Expression const* firstMatch = (*m_matchGroups)[m_matchGroup]; Expression const* firstMatch = (*m_matchGroups)[m_matchGroup];
assertThrow(firstMatch, OptimizerException, "Match set but to null."); assertThrow(firstMatch, OptimizerException, "Match set but to null.");
return assertThrow(
SyntacticallyEqual{}(*firstMatch, _expr) && !holds_alternative<FunctionCall>(_expr) &&
SideEffectsCollector(_dialect, _expr).movable(); !holds_alternative<FunctionCall>(*firstMatch),
OptimizerException,
"Group matches have to be literals or variables."
);
return SyntacticallyEqual{}(*firstMatch, _expr);
} }
else if (m_kind == PatternKind::Any) else if (m_kind == PatternKind::Any)
(*m_matchGroups)[m_matchGroup] = &_expr; (*m_matchGroups)[m_matchGroup] = &_expr;

View File

@ -57,6 +57,7 @@
#include <libyul/optimiser/LoadResolver.h> #include <libyul/optimiser/LoadResolver.h>
#include <libyul/optimiser/LoopInvariantCodeMotion.h> #include <libyul/optimiser/LoopInvariantCodeMotion.h>
#include <libyul/optimiser/Metrics.h> #include <libyul/optimiser/Metrics.h>
#include <libyul/optimiser/NameSimplifier.h>
#include <libyul/backends/evm/ConstantOptimiser.h> #include <libyul/backends/evm/ConstantOptimiser.h>
#include <libyul/AsmAnalysis.h> #include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h> #include <libyul/AsmAnalysisInfo.h>
@ -181,6 +182,7 @@ map<string, unique_ptr<OptimiserStep>> const& OptimiserSuite::allSteps()
LiteralRematerialiser, LiteralRematerialiser,
LoadResolver, LoadResolver,
LoopInvariantCodeMotion, LoopInvariantCodeMotion,
NameSimplifier,
RedundantAssignEliminator, RedundantAssignEliminator,
Rematerialiser, Rematerialiser,
SSAReverser, SSAReverser,
@ -218,6 +220,7 @@ map<string, char> const& OptimiserSuite::stepNameToAbbreviationMap()
{LiteralRematerialiser::name, 'T'}, {LiteralRematerialiser::name, 'T'},
{LoadResolver::name, 'L'}, {LoadResolver::name, 'L'},
{LoopInvariantCodeMotion::name, 'M'}, {LoopInvariantCodeMotion::name, 'M'},
{NameSimplifier::name, 'N'},
{RedundantAssignEliminator::name, 'r'}, {RedundantAssignEliminator::name, 'r'},
{Rematerialiser::name, 'm'}, {Rematerialiser::name, 'm'},
{SSAReverser::name, 'V'}, {SSAReverser::name, 'V'},

View File

@ -35,5 +35,5 @@ else
fi fi
docker run -v $(pwd):/root/project -w /root/project \ docker run -v $(pwd):/root/project -w /root/project \
solbuildpackpusher/solidity-buildpack-deps@sha256:d557d015918c3cf68b0d22839bab41013f0757b651a7fef21595f89721dbebcc \ solbuildpackpusher/solidity-buildpack-deps@sha256:23dad3b34deae8107c8551804ef299f6a89c23ed506e8118fac151e2bdc9018c\
./scripts/travis-emscripten/build_emscripten.sh $BUILD_DIR ./scripts/travis-emscripten/build_emscripten.sh $BUILD_DIR

View File

@ -29,12 +29,12 @@
# make version=1.39.15 build # make version=1.39.15 build
# #
FROM emscripten/emsdk:1.39.15 AS base FROM emscripten/emsdk:1.39.15 AS base
LABEL version="1" LABEL version="2"
ADD emscripten.jam /usr/src ADD emscripten.jam /usr/src
RUN set -ex; \ RUN set -ex; \
cd /usr/src; \ cd /usr/src; \
git clone https://github.com/Z3Prover/z3.git -b z3-4.8.8 --depth 1 ; \ git clone https://github.com/Z3Prover/z3.git -b z3-4.8.9 --depth 1 ; \
cd z3; \ cd z3; \
mkdir build; \ mkdir build; \
cd build; \ cd build; \

View File

@ -22,7 +22,7 @@
# (c) 2016-2019 solidity contributors. # (c) 2016-2019 solidity contributors.
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
FROM gcr.io/oss-fuzz-base/base-clang as base FROM gcr.io/oss-fuzz-base/base-clang as base
LABEL version="3" LABEL version="4"
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
@ -56,7 +56,7 @@ RUN git clone -b boost-1.69.0 https://github.com/boostorg/boost.git \
rm -rf /usr/src/boost rm -rf /usr/src/boost
# Z3 # Z3
RUN git clone --depth 1 -b z3-4.8.7 https://github.com/Z3Prover/z3.git \ RUN git clone --depth 1 -b z3-4.8.9 https://github.com/Z3Prover/z3.git \
/usr/src/z3; \ /usr/src/z3; \
cd /usr/src/z3; \ cd /usr/src/z3; \
mkdir build; \ mkdir build; \

View File

@ -22,7 +22,7 @@
# (c) 2016-2019 solidity contributors. # (c) 2016-2019 solidity contributors.
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
FROM buildpack-deps:bionic AS base FROM buildpack-deps:bionic AS base
LABEL version="2" LABEL version="3"
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive

View File

@ -22,7 +22,7 @@
# (c) 2016-2019 solidity contributors. # (c) 2016-2019 solidity contributors.
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
FROM buildpack-deps:focal AS base FROM buildpack-deps:focal AS base
LABEL version="2" LABEL version="3"
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive

View File

@ -22,7 +22,7 @@
# (c) 2016-2019 solidity contributors. # (c) 2016-2019 solidity contributors.
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
FROM buildpack-deps:focal AS base FROM buildpack-deps:focal AS base
LABEL version="2" LABEL version="3"
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive

View File

@ -57,7 +57,7 @@ packagename=solc
static_build_distribution=focal static_build_distribution=focal
DISTRIBUTIONS="bionic eoan focal" DISTRIBUTIONS="bionic focal groovy"
if is_release if is_release
then then

View File

@ -17,6 +17,7 @@
// SPDX-License-Identifier: GPL-3.0 // SPDX-License-Identifier: GPL-3.0
#include <stdexcept> #include <stdexcept>
#include <iostream>
#include <test/Common.h> #include <test/Common.h>
#include <libsolutil/Assertions.h> #include <libsolutil/Assertions.h>
@ -57,9 +58,9 @@ boost::filesystem::path testPath()
return {}; return {};
} }
std::string EVMOneEnvOrDefaultPath() std::string envOrDefaultPath(std::string const& env_name, std::string const& lib_name)
{ {
if (auto path = getenv("ETH_EVMONE")) if (auto path = getenv(env_name.c_str()))
return path; return path;
auto const searchPath = auto const searchPath =
@ -76,7 +77,7 @@ std::string EVMOneEnvOrDefaultPath()
}; };
for (auto const& basePath: searchPath) for (auto const& basePath: searchPath)
{ {
fs::path p = basePath / evmoneFilename; fs::path p = basePath / lib_name;
if (fs::exists(p)) if (fs::exists(p))
return p.string(); return p.string();
} }
@ -92,7 +93,8 @@ CommonOptions::CommonOptions(std::string _caption):
options.add_options() options.add_options()
("evm-version", po::value(&evmVersionString), "which evm version to use") ("evm-version", po::value(&evmVersionString), "which evm version to use")
("testpath", po::value<fs::path>(&this->testPath)->default_value(solidity::test::testPath()), "path to test files") ("testpath", po::value<fs::path>(&this->testPath)->default_value(solidity::test::testPath()), "path to test files")
("evmonepath", po::value<fs::path>(&evmonePath)->default_value(EVMOneEnvOrDefaultPath()), "path to evmone library") ("vm", po::value<std::vector<fs::path>>(&vmPaths), "path to evmc library, can be supplied multiple times.")
("ewasm", po::bool_switch(&ewasm), "tries to automatically find an ewasm vm and enable ewasm test-execution.")
("no-smt", po::bool_switch(&disableSMT), "disable SMT checker") ("no-smt", po::bool_switch(&disableSMT), "disable SMT checker")
("optimize", po::bool_switch(&optimize), "enables optimization") ("optimize", po::bool_switch(&optimize), "enables optimization")
("enforce-via-yul", po::bool_switch(&enforceViaYul), "Enforce compiling all tests via yul to see if additional tests can be activated.") ("enforce-via-yul", po::bool_switch(&enforceViaYul), "Enforce compiling all tests via yul to see if additional tests can be activated.")
@ -141,6 +143,33 @@ bool CommonOptions::parse(int argc, char const* const* argv)
throw std::runtime_error(errorMessage.str()); throw std::runtime_error(errorMessage.str());
} }
if (vmPaths.empty())
{
std::string evmone = envOrDefaultPath("ETH_EVMONE", evmoneFilename);
if (!evmone.empty())
vmPaths.emplace_back(evmone);
else
{
std::cout << "Unable to find " << solidity::test::evmoneFilename
<< ". Please provide the path using --vm <path>." << std::endl;
std::cout << "You can download it at" << std::endl;
std::cout << solidity::test::evmoneDownloadLink << std::endl;
}
}
if (ewasm) {
std::string hera = envOrDefaultPath("ETH_HERA", heraFilename);
if (!hera.empty())
vmPaths.emplace_back(hera);
else {
std::cout << "Unable to find " << solidity::test::heraFilename
<< ". Please provide the path using --vm <path>." << std::endl;
std::cout << "You can download it at" << std::endl;
std::cout << solidity::test::heraDownloadLink << std::endl;
std::cout << "Ewasm tests disabled." << std::endl;
}
}
return true; return true;
} }

View File

@ -21,6 +21,8 @@
#include <libsolutil/Exceptions.h> #include <libsolutil/Exceptions.h>
#include <liblangutil/EVMVersion.h> #include <liblangutil/EVMVersion.h>
#include <test/evmc/evmc.h>
#include <boost/filesystem/path.hpp> #include <boost/filesystem/path.hpp>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
@ -31,21 +33,27 @@ namespace solidity::test
#ifdef _WIN32 #ifdef _WIN32
static constexpr auto evmoneFilename = "evmone.dll"; static constexpr auto evmoneFilename = "evmone.dll";
static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.4.1/evmone-0.4.1-windows-amd64.zip"; static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.4.1/evmone-0.4.1-windows-amd64.zip";
static constexpr auto heraFilename = "hera.dll";
static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/archive/v0.3.0.tar.gz";
#elif defined(__APPLE__) #elif defined(__APPLE__)
static constexpr auto evmoneFilename = "libevmone.dylib"; static constexpr auto evmoneFilename = "libevmone.dylib";
static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.4.1/evmone-0.4.1-darwin-x86_64.tar.gz"; static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.4.1/evmone-0.4.1-darwin-x86_64.tar.gz";
static constexpr auto heraFilename = "libhera.dylib";
static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.3.0/hera-0.3.0-darwin-x86_64.tar.gz";
#else #else
static constexpr auto evmoneFilename = "libevmone.so"; static constexpr auto evmoneFilename = "libevmone.so";
static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.4.1/evmone-0.4.1-linux-x86_64.tar.gz"; static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.4.1/evmone-0.4.1-linux-x86_64.tar.gz";
static constexpr auto heraFilename = "libhera.so";
static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.3.0/hera-0.3.0-linux-x86_64.tar.gz";
#endif #endif
struct ConfigException : public util::Exception {}; struct ConfigException : public util::Exception {};
struct CommonOptions: boost::noncopyable struct CommonOptions: boost::noncopyable
{ {
boost::filesystem::path evmonePath; std::vector<boost::filesystem::path> vmPaths;
boost::filesystem::path testPath; boost::filesystem::path testPath;
bool ewasm = false;
bool optimize = false; bool optimize = false;
bool enforceViaYul = false; bool enforceViaYul = false;
bool disableSMT = false; bool disableSMT = false;
@ -64,8 +72,8 @@ struct CommonOptions: boost::noncopyable
CommonOptions(std::string caption = ""); CommonOptions(std::string caption = "");
virtual ~CommonOptions() {}; virtual ~CommonOptions() {};
protected:
protected:
boost::program_options::options_description options; boost::program_options::options_description options;
private: private:

View File

@ -39,17 +39,18 @@ using namespace evmc::literals;
evmc::VM& EVMHost::getVM(string const& _path) evmc::VM& EVMHost::getVM(string const& _path)
{ {
static evmc::VM theVM; static evmc::VM NullVM{nullptr};
if (!theVM && !_path.empty()) static map<string, unique_ptr<evmc::VM>> vms;
if (vms.count(_path) == 0)
{ {
evmc_loader_error_code errorCode = {}; evmc_loader_error_code errorCode = {};
auto vm = evmc::VM{evmc_load_and_configure(_path.c_str(), &errorCode)}; auto vm = evmc::VM{evmc_load_and_configure(_path.c_str(), &errorCode)};
if (vm && errorCode == EVMC_LOADER_SUCCESS) if (vm && errorCode == EVMC_LOADER_SUCCESS)
{ {
if (vm.get_capabilities() & EVMC_CAPABILITY_EVM1) if (vm.get_capabilities() & (EVMC_CAPABILITY_EVM1 | EVMC_CAPABILITY_EWASM))
theVM = std::move(vm); vms[_path] = make_unique<evmc::VM>(evmc::VM(move(vm)));
else else
cerr << "VM loaded does not support EVM1" << endl; cerr << "VM loaded neither supports EVM1 nor EWASM" << endl;
} }
else else
{ {
@ -59,7 +60,38 @@ evmc::VM& EVMHost::getVM(string const& _path)
cerr << endl; cerr << endl;
} }
} }
return theVM;
if (vms.count(_path) > 0)
return *vms[_path];
return NullVM;
}
bool EVMHost::checkVmPaths(vector<boost::filesystem::path> const& _vmPaths)
{
bool evmVmFound = false;
bool ewasmVmFound = false;
for (auto const& path: _vmPaths)
{
evmc::VM& vm = EVMHost::getVM(path.string());
if (!vm)
return false;
if (vm.has_capability(EVMC_CAPABILITY_EVM1))
{
if (evmVmFound)
throw runtime_error("Multiple evm1 evmc vms defined. Please only define one evm1 evmc vm.");
evmVmFound = true;
}
if (vm.has_capability(EVMC_CAPABILITY_EWASM))
{
if (ewasmVmFound)
throw runtime_error("Multiple ewasm evmc vms where defined. Please only define one ewasm evmc vm.");
ewasmVmFound = true;
}
}
return evmVmFound;
} }
EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm): EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm):

View File

@ -30,6 +30,8 @@
#include <libsolutil/FixedHash.h> #include <libsolutil/FixedHash.h>
#include <boost/filesystem.hpp>
namespace solidity::test namespace solidity::test
{ {
using Address = util::h160; using Address = util::h160;
@ -40,12 +42,17 @@ public:
using MockedHost::get_code_size; using MockedHost::get_code_size;
using MockedHost::get_balance; using MockedHost::get_balance;
/// Tries to dynamically load libevmone. @returns nullptr on failure. /// Tries to dynamically load an evmc vm supporting evm1 or ewasm and caches the loaded VM.
/// The path has to be provided for the first successful run and will be ignored /// @returns vmc::VM(nullptr) on failure.
/// afterwards.
static evmc::VM& getVM(std::string const& _path = {}); static evmc::VM& getVM(std::string const& _path = {});
explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm = getVM()); /// Tries to load all defined evmc vm shared libraries.
/// @param _vmPaths paths to multiple evmc shared libraries.
/// @throw Exception if multiple evm1 or multiple ewasm evmc vms where loaded.
/// @returns true, if an evmc vm was supporting evm1 loaded properly.
static bool checkVmPaths(std::vector<boost::filesystem::path> const& _vmPaths);
explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm);
void reset() { accounts.clear(); m_currentAddress = {}; } void reset() { accounts.clear(); m_currentAddress = {}; }
void newBlock() void newBlock()
@ -71,6 +78,12 @@ public:
static util::h256 convertFromEVMC(evmc::bytes32 const& _data); static util::h256 convertFromEVMC(evmc::bytes32 const& _data);
static evmc::bytes32 convertToEVMC(util::h256 const& _data); static evmc::bytes32 convertToEVMC(util::h256 const& _data);
/// @returns true, if the evmc VM has the given capability.
bool hasCapability(evmc_capabilities capability) const noexcept
{
return m_vm.has_capability(capability);
}
private: private:
evmc::address m_currentAddress = {}; evmc::address m_currentAddress = {};

View File

@ -29,6 +29,8 @@
#include <libsolutil/CommonIO.h> #include <libsolutil/CommonIO.h>
#include <liblangutil/Exceptions.h>
#include <boost/test/framework.hpp> #include <boost/test/framework.hpp>
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/replace.hpp>
@ -40,27 +42,47 @@ using namespace solidity::util;
using namespace solidity::test; using namespace solidity::test;
ExecutionFramework::ExecutionFramework(): ExecutionFramework::ExecutionFramework():
ExecutionFramework(solidity::test::CommonOptions::get().evmVersion()) ExecutionFramework(solidity::test::CommonOptions::get().evmVersion(), solidity::test::CommonOptions::get().vmPaths)
{ {
} }
ExecutionFramework::ExecutionFramework(langutil::EVMVersion _evmVersion): ExecutionFramework::ExecutionFramework(langutil::EVMVersion _evmVersion, vector<boost::filesystem::path> const& _vmPaths):
m_evmVersion(_evmVersion), m_evmVersion(_evmVersion),
m_optimiserSettings(solidity::frontend::OptimiserSettings::minimal()), m_optimiserSettings(solidity::frontend::OptimiserSettings::minimal()),
m_showMessages(solidity::test::CommonOptions::get().showMessages), m_showMessages(solidity::test::CommonOptions::get().showMessages),
m_evmHost(make_shared<EVMHost>(m_evmVersion)) m_vmPaths(_vmPaths)
{ {
if (solidity::test::CommonOptions::get().optimize) if (solidity::test::CommonOptions::get().optimize)
m_optimiserSettings = solidity::frontend::OptimiserSettings::standard(); m_optimiserSettings = solidity::frontend::OptimiserSettings::standard();
for (auto const& path: m_vmPaths)
if (EVMHost::getVM(path.string()).has_capability(EVMC_CAPABILITY_EWASM))
m_supportsEwasm = true;
selectVM(evmc_capabilities::EVMC_CAPABILITY_EVM1);
}
void ExecutionFramework::selectVM(evmc_capabilities _cap)
{
m_evmcHost.reset();
for (auto const& path: m_vmPaths)
{
evmc::VM& vm = EVMHost::getVM(path.string());
if (vm.has_capability(_cap))
{
m_evmcHost = make_unique<EVMHost>(m_evmVersion, vm);
break;
}
}
solAssert(m_evmcHost != nullptr, "");
reset(); reset();
} }
void ExecutionFramework::reset() void ExecutionFramework::reset()
{ {
m_evmHost->reset(); m_evmcHost->reset();
for (size_t i = 0; i < 10; i++) for (size_t i = 0; i < 10; i++)
m_evmHost->accounts[EVMHost::convertToEVMC(account(i))].balance = m_evmcHost->accounts[EVMHost::convertToEVMC(account(i))].balance =
EVMHost::convertToEVMC(u256(1) << 100); EVMHost::convertToEVMC(u256(1) << 100);
} }
@ -92,7 +114,7 @@ std::pair<bool, string> ExecutionFramework::compareAndCreateMessage(
u256 ExecutionFramework::gasLimit() const u256 ExecutionFramework::gasLimit() const
{ {
return {m_evmHost->tx_context.block_gas_limit}; return {m_evmcHost->tx_context.block_gas_limit};
} }
u256 ExecutionFramework::gasPrice() const u256 ExecutionFramework::gasPrice() const
@ -100,24 +122,24 @@ u256 ExecutionFramework::gasPrice() const
// here and below we use "return u256{....}" instead of just "return {....}" // here and below we use "return u256{....}" instead of just "return {....}"
// to please MSVC and avoid unexpected // to please MSVC and avoid unexpected
// warning C4927 : illegal conversion; more than one user - defined conversion has been implicitly applied // warning C4927 : illegal conversion; more than one user - defined conversion has been implicitly applied
return u256{EVMHost::convertFromEVMC(m_evmHost->tx_context.tx_gas_price)}; return u256{EVMHost::convertFromEVMC(m_evmcHost->tx_context.tx_gas_price)};
} }
u256 ExecutionFramework::blockHash(u256 const& _number) const u256 ExecutionFramework::blockHash(u256 const& _number) const
{ {
return u256{EVMHost::convertFromEVMC( return u256{EVMHost::convertFromEVMC(
m_evmHost->get_block_hash(static_cast<int64_t>(_number & numeric_limits<uint64_t>::max())) m_evmcHost->get_block_hash(static_cast<int64_t>(_number & numeric_limits<uint64_t>::max()))
)}; )};
} }
u256 ExecutionFramework::blockNumber() const u256 ExecutionFramework::blockNumber() const
{ {
return m_evmHost->tx_context.block_number; return m_evmcHost->tx_context.block_number;
} }
void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 const& _value) void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 const& _value)
{ {
m_evmHost->newBlock(); m_evmcHost->newBlock();
if (m_showMessages) if (m_showMessages)
{ {
@ -147,7 +169,7 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256
} }
message.gas = m_gas.convert_to<int64_t>(); message.gas = m_gas.convert_to<int64_t>();
evmc::result result = m_evmHost->call(message); evmc::result result = m_evmcHost->call(message);
m_output = bytes(result.output_data, result.output_data + result.output_size); m_output = bytes(result.output_data, result.output_data + result.output_size);
if (_isCreation) if (_isCreation)
@ -166,7 +188,7 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256
void ExecutionFramework::sendEther(Address const& _addr, u256 const& _amount) void ExecutionFramework::sendEther(Address const& _addr, u256 const& _amount)
{ {
m_evmHost->newBlock(); m_evmcHost->newBlock();
if (m_showMessages) if (m_showMessages)
{ {
@ -181,12 +203,12 @@ void ExecutionFramework::sendEther(Address const& _addr, u256 const& _amount)
message.destination = EVMHost::convertToEVMC(_addr); message.destination = EVMHost::convertToEVMC(_addr);
message.gas = m_gas.convert_to<int64_t>(); message.gas = m_gas.convert_to<int64_t>();
m_evmHost->call(message); m_evmcHost->call(message);
} }
size_t ExecutionFramework::currentTimestamp() size_t ExecutionFramework::currentTimestamp()
{ {
return static_cast<size_t>(m_evmHost->tx_context.block_timestamp); return static_cast<size_t>(m_evmcHost->tx_context.block_timestamp);
} }
size_t ExecutionFramework::blockTimestamp(u256 _block) size_t ExecutionFramework::blockTimestamp(u256 _block)
@ -204,32 +226,32 @@ Address ExecutionFramework::account(size_t _idx)
bool ExecutionFramework::addressHasCode(Address const& _addr) bool ExecutionFramework::addressHasCode(Address const& _addr)
{ {
return m_evmHost->get_code_size(EVMHost::convertToEVMC(_addr)) != 0; return m_evmcHost->get_code_size(EVMHost::convertToEVMC(_addr)) != 0;
} }
size_t ExecutionFramework::numLogs() const size_t ExecutionFramework::numLogs() const
{ {
return m_evmHost->recorded_logs.size(); return m_evmcHost->recorded_logs.size();
} }
size_t ExecutionFramework::numLogTopics(size_t _logIdx) const size_t ExecutionFramework::numLogTopics(size_t _logIdx) const
{ {
return m_evmHost->recorded_logs.at(_logIdx).topics.size(); return m_evmcHost->recorded_logs.at(_logIdx).topics.size();
} }
h256 ExecutionFramework::logTopic(size_t _logIdx, size_t _topicIdx) const h256 ExecutionFramework::logTopic(size_t _logIdx, size_t _topicIdx) const
{ {
return EVMHost::convertFromEVMC(m_evmHost->recorded_logs.at(_logIdx).topics.at(_topicIdx)); return EVMHost::convertFromEVMC(m_evmcHost->recorded_logs.at(_logIdx).topics.at(_topicIdx));
} }
Address ExecutionFramework::logAddress(size_t _logIdx) const Address ExecutionFramework::logAddress(size_t _logIdx) const
{ {
return EVMHost::convertFromEVMC(m_evmHost->recorded_logs.at(_logIdx).creator); return EVMHost::convertFromEVMC(m_evmcHost->recorded_logs.at(_logIdx).creator);
} }
bytes ExecutionFramework::logData(size_t _logIdx) const bytes ExecutionFramework::logData(size_t _logIdx) const
{ {
const auto& data = m_evmHost->recorded_logs.at(_logIdx).data; const auto& data = m_evmcHost->recorded_logs.at(_logIdx).data;
// TODO: Return a copy of log data, because this is expected from REQUIRE_LOG_DATA(), // TODO: Return a copy of log data, because this is expected from REQUIRE_LOG_DATA(),
// but reference type like string_view would be preferable. // but reference type like string_view would be preferable.
return {data.begin(), data.end()}; return {data.begin(), data.end()};
@ -237,13 +259,13 @@ bytes ExecutionFramework::logData(size_t _logIdx) const
u256 ExecutionFramework::balanceAt(Address const& _addr) u256 ExecutionFramework::balanceAt(Address const& _addr)
{ {
return u256(EVMHost::convertFromEVMC(m_evmHost->get_balance(EVMHost::convertToEVMC(_addr)))); return u256(EVMHost::convertFromEVMC(m_evmcHost->get_balance(EVMHost::convertToEVMC(_addr))));
} }
bool ExecutionFramework::storageEmpty(Address const& _addr) bool ExecutionFramework::storageEmpty(Address const& _addr)
{ {
const auto it = m_evmHost->accounts.find(EVMHost::convertToEVMC(_addr)); const auto it = m_evmcHost->accounts.find(EVMHost::convertToEVMC(_addr));
if (it != m_evmHost->accounts.end()) if (it != m_evmcHost->accounts.end())
{ {
for (auto const& entry: it->second.storage) for (auto const& entry: it->second.storage)
if (!(entry.second.value == evmc::bytes32{})) if (!(entry.second.value == evmc::bytes32{}))

View File

@ -24,6 +24,7 @@
#pragma once #pragma once
#include <test/Common.h> #include <test/Common.h>
#include <test/EVMHost.h>
#include <libsolidity/interface/OptimiserSettings.h> #include <libsolidity/interface/OptimiserSettings.h>
#include <libsolidity/interface/DebugSettings.h> #include <libsolidity/interface/DebugSettings.h>
@ -39,8 +40,6 @@
namespace solidity::test namespace solidity::test
{ {
class EVMHost;
using rational = boost::rational<bigint>; using rational = boost::rational<bigint>;
/// An Ethereum address: 20 bytes. /// An Ethereum address: 20 bytes.
/// @NOTE This is not endian-specific; it's just a bunch of bytes. /// @NOTE This is not endian-specific; it's just a bunch of bytes.
@ -55,7 +54,7 @@ class ExecutionFramework
public: public:
ExecutionFramework(); ExecutionFramework();
explicit ExecutionFramework(langutil::EVMVersion _evmVersion); ExecutionFramework(langutil::EVMVersion _evmVersion, std::vector<boost::filesystem::path> const& _vmPaths);
virtual ~ExecutionFramework() = default; virtual ~ExecutionFramework() = default;
virtual bytes const& compileAndRunWithoutCheck( virtual bytes const& compileAndRunWithoutCheck(
@ -255,6 +254,7 @@ private:
} }
protected: protected:
void selectVM(evmc_capabilities _cap = evmc_capabilities::EVMC_CAPABILITY_EVM1);
void reset(); void reset();
void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0); void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0);
@ -279,7 +279,10 @@ protected:
solidity::frontend::RevertStrings m_revertStrings = solidity::frontend::RevertStrings::Default; solidity::frontend::RevertStrings m_revertStrings = solidity::frontend::RevertStrings::Default;
solidity::frontend::OptimiserSettings m_optimiserSettings = solidity::frontend::OptimiserSettings::minimal(); solidity::frontend::OptimiserSettings m_optimiserSettings = solidity::frontend::OptimiserSettings::minimal();
bool m_showMessages = false; bool m_showMessages = false;
std::shared_ptr<EVMHost> m_evmHost; bool m_supportsEwasm = false;
std::unique_ptr<EVMHost> m_evmcHost;
std::vector<boost::filesystem::path> m_vmPaths;
bool m_transactionSuccessful = true; bool m_transactionSuccessful = true;
Address m_sender = account(0); Address m_sender = account(0);

View File

@ -39,6 +39,7 @@ public:
{ {
std::string filename; std::string filename;
langutil::EVMVersion evmVersion; langutil::EVMVersion evmVersion;
std::vector<boost::filesystem::path> vmPaths;
bool enforceCompileViaYul; bool enforceCompileViaYul;
}; };

View File

@ -39,7 +39,6 @@
#include <test/InteractiveTests.h> #include <test/InteractiveTests.h>
#include <test/Common.h> #include <test/Common.h>
#include <test/EVMHost.h> #include <test/EVMHost.h>
#include <test/Common.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
@ -72,7 +71,7 @@ int registerTests(
{ {
int numTestsAdded = 0; int numTestsAdded = 0;
fs::path fullpath = _basepath / _path; fs::path fullpath = _basepath / _path;
TestCase::Config config{fullpath.string(), solidity::test::CommonOptions::get().evmVersion(), _enforceViaYul}; TestCase::Config config{fullpath.string(), solidity::test::CommonOptions::get().evmVersion(), solidity::test::CommonOptions::get().vmPaths, _enforceViaYul};
if (fs::is_directory(fullpath)) if (fs::is_directory(fullpath))
{ {
test_suite* sub_suite = BOOST_TEST_SUITE(_path.filename().string()); test_suite* sub_suite = BOOST_TEST_SUITE(_path.filename().string());
@ -156,14 +155,19 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] )
initializeOptions(); initializeOptions();
bool disableSemantics = !solidity::test::EVMHost::getVM(solidity::test::CommonOptions::get().evmonePath.string()); bool disableSemantics = true;
if (disableSemantics) try
{ {
cout << "Unable to find " << solidity::test::evmoneFilename << ". Please provide the path using -- --evmonepath <path>." << endl; disableSemantics = !solidity::test::EVMHost::checkVmPaths(solidity::test::CommonOptions::get().vmPaths);
cout << "You can download it at" << endl;
cout << solidity::test::evmoneDownloadLink << endl;
cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl;
} }
catch (std::runtime_error const& _exception)
{
cerr << "Error: " << _exception.what() << endl;
exit(1);
}
if (disableSemantics)
cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl;
// Include the interactive tests in the automatic tests as well // Include the interactive tests in the automatic tests as well
for (auto const& ts: g_interactiveTestsuites) for (auto const& ts: g_interactiveTestsuites)
{ {

View File

@ -0,0 +1 @@
--optimize --ir-optimized --metadata-hash none

View File

@ -0,0 +1,23 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;
pragma experimental ABIEncoderV2;
// The point of this test is to check that the
// AST IDs are removed from the optimized IR
// so that they do not have a big effect on the
// optimizer if it has a bug that makes it
// depen on the actual identifiers.
struct S { uint x; }
struct T { uint[2] y; }
contract C {
S[2] values;
T t;
function sumArray(S[] memory _s) public returns (uint, string memory) {
values[0].x = _s[0].x;
t.y[0] = _s[1].x;
return (t.y[0], "longstringlongstringlongstringlongstringlongstringlongstringlongstringlongstringlongstringlongstring");
}
}

View File

@ -0,0 +1,129 @@
Optimized IR:
/*******************************************************
* WARNING *
* Solidity to Yul compilation is still EXPERIMENTAL *
* It can result in LOSS OF FUNDS or worse *
* !USE AT YOUR OWN RISK! *
*******************************************************/
object "C_56" {
code {
{
mstore(64, 128)
if callvalue() { revert(0, 0) }
let _1 := datasize("C_56_deployed")
codecopy(0, dataoffset("C_56_deployed"), _1)
return(0, _1)
}
}
object "C_56_deployed" {
code {
{
mstore(64, 128)
if iszero(lt(calldatasize(), 4))
{
let _1 := 0
if eq(0xf8eddcc6, shr(224, calldataload(_1)))
{
if callvalue() { revert(_1, _1) }
let _2 := 32
if slt(add(calldatasize(), not(3)), _2) { revert(_1, _1) }
let offset := calldataload(4)
let _3 := 0xffffffffffffffff
if gt(offset, _3) { revert(_1, _1) }
if iszero(slt(add(offset, 35), calldatasize())) { revert(_1, _1) }
let length := calldataload(add(4, offset))
if gt(length, _3) { revert(_1, _1) }
let _4 := mul(length, _2)
let dst := allocateMemory(add(_4, _2))
let dst_1 := dst
mstore(dst, length)
dst := add(dst, _2)
let src := add(offset, 36)
if gt(add(add(offset, _4), 36), calldatasize()) { revert(_1, _1) }
let i := _1
for { } lt(i, length) { i := add(i, 1) }
{
mstore(dst, abi_decode_t_struct$_S(src, calldatasize()))
dst := add(dst, _2)
src := add(src, _2)
}
let ret, ret_1 := fun_sumArray_55(dst_1)
let memPos := allocateMemory(_1)
return(memPos, sub(abi_encode_uint256_t_string(memPos, ret, ret_1), memPos))
}
}
revert(0, 0)
}
function abi_decode_t_struct$_S(headStart, end) -> value
{
if slt(sub(end, headStart), 0x20) { revert(value, value) }
value := allocateMemory(0x20)
mstore(value, calldataload(headStart))
}
function abi_encode_uint256_t_string(headStart, value0, value1) -> tail
{
mstore(headStart, value0)
let _1 := 32
mstore(add(headStart, _1), 64)
let length := mload(value1)
mstore(add(headStart, 64), length)
let i := tail
for { } lt(i, length) { i := add(i, _1) }
{
mstore(add(add(headStart, i), 96), mload(add(add(value1, i), _1)))
}
if gt(i, length)
{
mstore(add(add(headStart, length), 96), tail)
}
tail := add(add(headStart, and(add(length, 31), not(31))), 96)
}
function allocateMemory(size) -> memPtr
{
memPtr := mload(64)
let newFreePtr := add(memPtr, size)
if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) }
mstore(64, newFreePtr)
}
function convert_t_stringliteral_6490_to_t_string() -> converted
{
converted := allocateMemory(160)
mstore(converted, 100)
mstore(add(converted, 32), "longstringlongstringlongstringlo")
mstore(add(converted, 64), "ngstringlongstringlongstringlong")
mstore(add(converted, 96), "stringlongstringlongstringlongst")
mstore(add(converted, 128), "ring")
}
function extract_from_storage_value_dynamict_uint256(slot_value, offset) -> value
{
value := shr(mul(offset, 8), slot_value)
}
function fun_sumArray_55(vloc__s_19_mpos) -> vloc, vloc__24_mpos
{
let _1 := mload(vloc__s_19_mpos)
if iszero(lt(vloc, _1)) { invalid() }
let _2 := mload(mload(add(add(vloc__s_19_mpos, mul(vloc, 32)), 32)))
let _3, _4 := storage_array_index_access$_t_struct$_S_storage(vloc, vloc)
sstore(_3, _2)
if iszero(lt(0x01, _1)) { invalid() }
let _5 := mload(mload(add(vloc__s_19_mpos, 64)))
if iszero(lt(vloc, 0x02)) { invalid() }
let slot := add(0x02, vloc)
let _6 := sload(slot)
let shiftBits := mul(vloc, 8)
let mask := shl(shiftBits, not(0))
sstore(slot, or(and(_6, not(mask)), and(shl(shiftBits, _5), mask)))
let _7, _8 := storage_array_index_access$_t_struct$_S_storage(0x02, vloc)
vloc := extract_from_storage_value_dynamict_uint256(sload(_7), _8)
vloc__24_mpos := convert_t_stringliteral_6490_to_t_string()
}
function storage_array_index_access$_t_struct$_S_storage(array, index) -> slot, offset
{
if iszero(lt(index, 0x02)) { invalid() }
slot := add(array, index)
offset := offset
}
}
}
}

View File

@ -33,19 +33,22 @@ object "Arraysum_33" {
for { } for { }
lt(vloc_i, _2) lt(vloc_i, _2)
{ {
vloc_i := increment_t_uint256(vloc_i) if gt(vloc_i, not(1)) { revert(_1, _1) }
vloc_i := add(vloc_i, 1)
} }
{ {
let _3, _4 := storage_array_index_access_t_array$_t_uint256_$dyn_storage(_1, vloc_i) mstore(_1, _1)
vloc_sum := checked_add_t_uint256(vloc_sum, extract_from_storage_value_dynamict_uint256(sload(_3), _4)) let _3 := sload(add(keccak256(_1, 0x20), vloc_i))
if gt(vloc_sum, not(_3)) { revert(_1, _1) }
vloc_sum := add(vloc_sum, _3)
} }
let memPos := allocateMemory(_1) let memPos := allocateMemory(_1)
return(memPos, sub(abi_encode_tuple_t_uint256__to_t_uint256__fromStack(memPos, _1), memPos)) return(memPos, sub(abi_encode_uint(memPos, _1), memPos))
} }
} }
revert(0, 0) revert(0, 0)
} }
function abi_encode_tuple_t_uint256__to_t_uint256__fromStack(headStart, value0) -> tail function abi_encode_uint(headStart, value0) -> tail
{ {
tail := add(headStart, 32) tail := add(headStart, 32)
mstore(headStart, value0) mstore(headStart, value0)
@ -57,27 +60,6 @@ object "Arraysum_33" {
if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) }
mstore(64, newFreePtr) mstore(64, newFreePtr)
} }
function checked_add_t_uint256(x, y) -> sum
{
if gt(x, not(y)) { revert(sum, sum) }
sum := add(x, y)
}
function extract_from_storage_value_dynamict_uint256(slot_value, offset) -> value
{
value := shr(mul(offset, 8), slot_value)
}
function increment_t_uint256(value) -> ret
{
if gt(value, not(1)) { revert(ret, ret) }
ret := add(value, 1)
}
function storage_array_index_access_t_array$_t_uint256_$dyn_storage(array, index) -> slot, offset
{
if iszero(lt(index, sload(array))) { invalid() }
mstore(slot, array)
slot := add(keccak256(slot, 0x20), index)
offset := offset
}
} }
} }
} }

View File

@ -6,9 +6,6 @@
(import \"ethereum\" \"finish\" (func $eth.finish (param i32 i32))) (import \"ethereum\" \"finish\" (func $eth.finish (param i32 i32)))
(memory $memory (export \"memory\") 1) (memory $memory (export \"memory\") 1)
(export \"main\" (func $main)) (export \"main\" (func $main))
(global $global_ (mut i64) (i64.const 0))
(global $global__1 (mut i64) (i64.const 0))
(global $global__2 (mut i64) (i64.const 0))
(func $main (func $main
(local $_1 i64) (local $_1 i64)
@ -18,7 +15,6 @@
(local $z1 i64) (local $z1 i64)
(local $z2 i64) (local $z2 i64)
(local $z3 i64) (local $z3 i64)
(local $z4 i64)
(local $_3 i64) (local $_3 i64)
(block $label_ (block $label_
(local.set $_1 (i64.const 0)) (local.set $_1 (i64.const 0))
@ -32,18 +28,14 @@
(i64.store (i32.add (local.get $r) (i32.const 16)) (local.get $_2)) (i64.store (i32.add (local.get $r) (i32.const 16)) (local.get $_2))
(i64.store (i32.add (local.get $r) (i32.const 24)) (call $endian_swap (i64.const 128))) (i64.store (i32.add (local.get $r) (i32.const 24)) (call $endian_swap (i64.const 128)))
(call $eth.getCallValue (i32.const 0)) (call $eth.getCallValue (i32.const 0))
(block (local.set $z1 (call $endian_swap (i64.load (i32.const 0))))
(local.set $z1 (call $mload_internal (i32.const 0))) (local.set $z2 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 8)))))
(local.set $z2 (global.get $global_)) (local.set $z3 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 16)))))
(local.set $z3 (global.get $global__1)) (if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $z1) (local.get $z2)) (i64.or (local.get $z3) (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 24)))))))) (then
(local.set $z4 (global.get $global__2)) (call $revert (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1))))
)
(if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $z1) (local.get $z2)) (i64.or (local.get $z3) (local.get $z4))))) (then
(call $eth.revert (call $to_internal_i32ptr (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)))))
(local.set $_3 (datasize \"C_2_deployed\")) (local.set $_3 (datasize \"C_2_deployed\"))
(call $eth.codeCopy (call $to_internal_i32ptr (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (dataoffset \"C_2_deployed\")) (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_3))) (call $codecopy (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (dataoffset \"C_2_deployed\") (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_3))
(call $eth.finish (call $to_internal_i32ptr (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_3))) (call $return (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_3))
) )
) )
@ -54,7 +46,7 @@
(param $x4 i64) (param $x4 i64)
(result i32) (result i32)
(local $v i32) (local $v i32)
(block $label__3 (block $label__1
(if (i64.ne (i64.const 0) (i64.or (i64.or (local.get $x1) (local.get $x2)) (local.get $x3))) (then (if (i64.ne (i64.const 0) (i64.or (i64.or (local.get $x1) (local.get $x2)) (local.get $x3))) (then
(unreachable))) (unreachable)))
(if (i64.ne (i64.const 0) (i64.shr_u (local.get $x4) (i64.const 32))) (then (if (i64.ne (i64.const 0) (i64.shr_u (local.get $x4) (i64.const 32))) (then
@ -73,7 +65,7 @@
(result i32) (result i32)
(local $r i32) (local $r i32)
(local $p i32) (local $p i32)
(block $label__4 (block $label__2
(local.set $p (call $u256_to_i32 (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4))) (local.set $p (call $u256_to_i32 (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4)))
(local.set $r (i32.add (local.get $p) (i32.const 64))) (local.set $r (i32.add (local.get $p) (i32.const 64)))
(if (i32.lt_u (local.get $r) (local.get $p)) (then (if (i32.lt_u (local.get $r) (local.get $p)) (then
@ -83,11 +75,29 @@
(local.get $r) (local.get $r)
) )
(func $codecopy
(param $x1 i64)
(param $x2 i64)
(param $x3 i64)
(param $x4 i64)
(param $y1 i64)
(param $y2 i64)
(param $y3 i64)
(param $y4 i64)
(param $z1 i64)
(param $z2 i64)
(param $z3 i64)
(param $z4 i64)
(block $label__3
(call $eth.codeCopy (call $to_internal_i32ptr (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4)) (call $u256_to_i32 (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4)) (call $u256_to_i32 (local.get $z1) (local.get $z2) (local.get $z3) (local.get $z4)))
)
)
(func $endian_swap_16 (func $endian_swap_16
(param $x i64) (param $x i64)
(result i64) (result i64)
(local $y i64) (local $y i64)
(block $label__5 (block $label__4
(local.set $y (i64.or (i64.and (i64.shl (local.get $x) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (local.get $x) (i64.const 8)) (i64.const 255)))) (local.set $y (i64.or (i64.and (i64.shl (local.get $x) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (local.get $x) (i64.const 8)) (i64.const 255))))
) )
@ -99,7 +109,7 @@
(result i64) (result i64)
(local $y i64) (local $y i64)
(local $hi i64) (local $hi i64)
(block $label__6 (block $label__5
(local.set $hi (i64.shl (call $endian_swap_16 (local.get $x)) (i64.const 16))) (local.set $hi (i64.shl (call $endian_swap_16 (local.get $x)) (i64.const 16)))
(local.set $y (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $x) (i64.const 16))))) (local.set $y (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $x) (i64.const 16)))))
@ -112,7 +122,7 @@
(result i64) (result i64)
(local $y i64) (local $y i64)
(local $hi i64) (local $hi i64)
(block $label__7 (block $label__6
(local.set $hi (i64.shl (call $endian_swap_32 (local.get $x)) (i64.const 32))) (local.set $hi (i64.shl (call $endian_swap_32 (local.get $x)) (i64.const 32)))
(local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $x) (i64.const 32))))) (local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $x) (i64.const 32)))))
@ -120,24 +130,32 @@
(local.get $y) (local.get $y)
) )
(func $mload_internal (func $return
(param $pos i32) (param $x1 i64)
(result i64) (param $x2 i64)
(local $z1 i64) (param $x3 i64)
(local $z2 i64) (param $x4 i64)
(local $z3 i64) (param $y1 i64)
(local $z4 i64) (param $y2 i64)
(block $label__8 (param $y3 i64)
(local.set $z1 (call $endian_swap (i64.load (local.get $pos)))) (param $y4 i64)
(local.set $z2 (call $endian_swap (i64.load (i32.add (local.get $pos) (i32.const 8))))) (block $label__7
(local.set $z3 (call $endian_swap (i64.load (i32.add (local.get $pos) (i32.const 16))))) (call $eth.finish (call $to_internal_i32ptr (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4)) (call $u256_to_i32 (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4)))
(local.set $z4 (call $endian_swap (i64.load (i32.add (local.get $pos) (i32.const 24))))) )
)
(func $revert
(param $x1 i64)
(param $x2 i64)
(param $x3 i64)
(param $x4 i64)
(param $y1 i64)
(param $y2 i64)
(param $y3 i64)
(param $y4 i64)
(block $label__8
(call $eth.revert (call $to_internal_i32ptr (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4)) (call $u256_to_i32 (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4)))
) )
(global.set $global_ (local.get $z2))
(global.set $global__1 (local.get $z3))
(global.set $global__2 (local.get $z4))
(local.get $z1)
) )
) )

View File

@ -4,12 +4,6 @@
Pretty printed source: Pretty printed source:
object "object" { object "object" {
code { code {
{
let a3, b3, c3, d3, e3, f3, g3, h3, i3, j3, k3, l3, m3, o3, p3 := fun()
let a3_1, b3_1, c3_1, d3_1, e3_1, f3_1, g3_1, h3_1, i3_1, j3_1, k3_1, l3_1, m3_1, o3_1, p3_1 := fun()
sstore(a3, a3_1)
}
function fun() -> a3, b3, c3, d3, e3, f3, g3, h3, i3, j3, k3, l3, m3, o3, p3
{ {
let _1 := 1 let _1 := 1
sstore(_1, _1) sstore(_1, _1)
@ -25,93 +19,29 @@ object "object" {
sstore(11, _1) sstore(11, _1)
sstore(12, _1) sstore(12, _1)
sstore(13, _1) sstore(13, _1)
a3 := _1 sstore(_1, _1)
b3 := _1 sstore(2, _1)
c3 := _1 sstore(3, _1)
d3 := _1 sstore(4, _1)
e3 := _1 sstore(5, _1)
f3 := _1 sstore(6, _1)
g3 := _1 sstore(7, _1)
h3 := _1 sstore(8, _1)
i3 := _1 sstore(9, _1)
j3 := _1 sstore(10, _1)
k3 := _1 sstore(11, _1)
l3 := _1 sstore(12, _1)
m3 := _1 sstore(13, _1)
o3 := _1 sstore(_1, _1)
p3 := _1
} }
} }
} }
Binary representation: Binary representation:
60056030565b505050505050505050505050505060196030565b5050505050505050505050505050808255505060c3565b6000600060006000600060006000600060006000600060006000600060006001808155806002558060035580600455806005558060065580600755806008558060095580600a5580600b5580600c5580600d55809f50809e50809d50809c50809b50809a50809950809850809750809650809550809450809350809250809150505b909192939495969798999a9b9c9d9e565b 6001808155806002558060035580600455806005558060065580600755806008558060095580600a5580600b5580600c5580600d55808155806002558060035580600455806005558060065580600755806008558060095580600a5580600b5580600c5580600d5580815550
Text representation: Text representation:
/* "yul_stack_opt/input.sol":3:573 */
tag_1
tag_2
jump // in
tag_1:
pop
pop
pop
pop
pop
pop
pop
pop
pop
pop
pop
pop
pop
pop
tag_3
tag_2
jump // in
tag_3:
pop
pop
pop
pop
pop
pop
pop
pop
pop
pop
pop
pop
pop
pop
/* "yul_stack_opt/input.sol":740:742 */
dup1
/* "yul_stack_opt/input.sol":736:738 */
dup3
/* "yul_stack_opt/input.sol":729:743 */
sstore
pop
pop
/* "yul_stack_opt/input.sol":3:573 */
jump(tag_4)
tag_2:
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
0x00
/* "yul_stack_opt/input.sol":98:99 */ /* "yul_stack_opt/input.sol":98:99 */
0x01 0x01
dup1 dup1
@ -192,96 +122,84 @@ tag_2:
sstore sstore
/* "yul_stack_opt/input.sol":98:99 */ /* "yul_stack_opt/input.sol":98:99 */
dup1 dup1
/* "yul_stack_opt/input.sol":423:430 */ dup2
swap16 /* "yul_stack_opt/input.sol":129:141 */
pop sstore
/* "yul_stack_opt/input.sol":98:99 */ /* "yul_stack_opt/input.sol":98:99 */
dup1 dup1
/* "yul_stack_opt/input.sol":433:440 */ /* "yul_stack_opt/input.sol":151:160 */
swap15 0x02
pop /* "yul_stack_opt/input.sol":144:164 */
sstore
/* "yul_stack_opt/input.sol":98:99 */ /* "yul_stack_opt/input.sol":98:99 */
dup1 dup1
/* "yul_stack_opt/input.sol":443:450 */ /* "yul_stack_opt/input.sol":174:183 */
swap14 0x03
pop /* "yul_stack_opt/input.sol":167:187 */
sstore
/* "yul_stack_opt/input.sol":98:99 */ /* "yul_stack_opt/input.sol":98:99 */
dup1 dup1
/* "yul_stack_opt/input.sol":453:460 */ /* "yul_stack_opt/input.sol":197:206 */
swap13 0x04
pop /* "yul_stack_opt/input.sol":190:210 */
sstore
/* "yul_stack_opt/input.sol":98:99 */ /* "yul_stack_opt/input.sol":98:99 */
dup1 dup1
/* "yul_stack_opt/input.sol":463:470 */ /* "yul_stack_opt/input.sol":220:229 */
swap12 0x05
pop /* "yul_stack_opt/input.sol":213:233 */
sstore
/* "yul_stack_opt/input.sol":98:99 */ /* "yul_stack_opt/input.sol":98:99 */
dup1 dup1
/* "yul_stack_opt/input.sol":473:480 */ /* "yul_stack_opt/input.sol":243:252 */
swap11 0x06
pop /* "yul_stack_opt/input.sol":236:256 */
sstore
/* "yul_stack_opt/input.sol":98:99 */ /* "yul_stack_opt/input.sol":98:99 */
dup1 dup1
/* "yul_stack_opt/input.sol":483:490 */ /* "yul_stack_opt/input.sol":266:275 */
swap10 0x07
pop /* "yul_stack_opt/input.sol":259:279 */
sstore
/* "yul_stack_opt/input.sol":98:99 */ /* "yul_stack_opt/input.sol":98:99 */
dup1 dup1
/* "yul_stack_opt/input.sol":493:500 */ /* "yul_stack_opt/input.sol":289:298 */
swap9 0x08
pop /* "yul_stack_opt/input.sol":282:302 */
sstore
/* "yul_stack_opt/input.sol":98:99 */ /* "yul_stack_opt/input.sol":98:99 */
dup1 dup1
/* "yul_stack_opt/input.sol":503:510 */ /* "yul_stack_opt/input.sol":312:321 */
swap8 0x09
pop /* "yul_stack_opt/input.sol":305:325 */
sstore
/* "yul_stack_opt/input.sol":98:99 */ /* "yul_stack_opt/input.sol":98:99 */
dup1 dup1
/* "yul_stack_opt/input.sol":513:520 */ /* "yul_stack_opt/input.sol":335:344 */
swap7 0x0a
pop /* "yul_stack_opt/input.sol":328:348 */
sstore
/* "yul_stack_opt/input.sol":98:99 */ /* "yul_stack_opt/input.sol":98:99 */
dup1 dup1
/* "yul_stack_opt/input.sol":523:530 */ /* "yul_stack_opt/input.sol":358:368 */
swap6 0x0b
pop /* "yul_stack_opt/input.sol":351:372 */
sstore
/* "yul_stack_opt/input.sol":98:99 */ /* "yul_stack_opt/input.sol":98:99 */
dup1 dup1
/* "yul_stack_opt/input.sol":533:540 */ /* "yul_stack_opt/input.sol":382:392 */
swap5 0x0c
pop /* "yul_stack_opt/input.sol":375:396 */
sstore
/* "yul_stack_opt/input.sol":98:99 */ /* "yul_stack_opt/input.sol":98:99 */
dup1 dup1
/* "yul_stack_opt/input.sol":543:550 */ /* "yul_stack_opt/input.sol":406:416 */
swap4 0x0d
pop /* "yul_stack_opt/input.sol":399:420 */
sstore
/* "yul_stack_opt/input.sol":98:99 */ /* "yul_stack_opt/input.sol":98:99 */
dup1 dup1
/* "yul_stack_opt/input.sol":553:560 */ dup2
swap3 /* "yul_stack_opt/input.sol":729:743 */
sstore
pop pop
/* "yul_stack_opt/input.sol":98:99 */
dup1
/* "yul_stack_opt/input.sol":563:570 */
swap2
pop
pop
/* "yul_stack_opt/input.sol":85:573 */
tag_5:
swap1
swap2
swap3
swap4
swap5
swap6
swap7
swap8
swap9
swap10
swap11
swap12
swap13
swap14
swap15
jump // out
tag_4:

View File

@ -417,7 +417,7 @@ BOOST_AUTO_TEST_CASE(auction_simple)
BOOST_CHECK_EQUAL(registrar.owner(name), 0); BOOST_CHECK_EQUAL(registrar.owner(name), 0);
// "wait" until auction end // "wait" until auction end
m_evmHost->tx_context.block_timestamp += m_biddingTime + 10; m_evmcHost->tx_context.block_timestamp += m_biddingTime + 10;
// trigger auction again // trigger auction again
registrar.reserve(name); registrar.reserve(name);
BOOST_CHECK_EQUAL(registrar.owner(name), m_sender); BOOST_CHECK_EQUAL(registrar.owner(name), m_sender);
@ -429,7 +429,7 @@ BOOST_AUTO_TEST_CASE(auction_bidding)
string name = "x"; string name = "x";
unsigned startTime = 0x776347e2; unsigned startTime = 0x776347e2;
m_evmHost->tx_context.block_timestamp = startTime; m_evmcHost->tx_context.block_timestamp = startTime;
RegistrarInterface registrar(*this); RegistrarInterface registrar(*this);
// initiate auction // initiate auction
@ -437,19 +437,19 @@ BOOST_AUTO_TEST_CASE(auction_bidding)
registrar.reserve(name); registrar.reserve(name);
BOOST_CHECK_EQUAL(registrar.owner(name), 0); BOOST_CHECK_EQUAL(registrar.owner(name), 0);
// overbid self // overbid self
m_evmHost->tx_context.block_timestamp = startTime + m_biddingTime - 10; m_evmcHost->tx_context.block_timestamp = startTime + m_biddingTime - 10;
registrar.setNextValue(12); registrar.setNextValue(12);
registrar.reserve(name); registrar.reserve(name);
// another bid by someone else // another bid by someone else
sendEther(account(1), 10 * ether); sendEther(account(1), 10 * ether);
m_sender = account(1); m_sender = account(1);
m_evmHost->tx_context.block_timestamp = startTime + 2 * m_biddingTime - 50; m_evmcHost->tx_context.block_timestamp = startTime + 2 * m_biddingTime - 50;
registrar.setNextValue(13); registrar.setNextValue(13);
registrar.reserve(name); registrar.reserve(name);
BOOST_CHECK_EQUAL(registrar.owner(name), 0); BOOST_CHECK_EQUAL(registrar.owner(name), 0);
// end auction by first bidder (which is not highest) trying to overbid again (too late) // end auction by first bidder (which is not highest) trying to overbid again (too late)
m_sender = account(0); m_sender = account(0);
m_evmHost->tx_context.block_timestamp = startTime + 4 * m_biddingTime; m_evmcHost->tx_context.block_timestamp = startTime + 4 * m_biddingTime;
registrar.setNextValue(20); registrar.setNextValue(20);
registrar.reserve(name); registrar.reserve(name);
BOOST_CHECK_EQUAL(registrar.owner(name), account(1)); BOOST_CHECK_EQUAL(registrar.owner(name), account(1));

View File

@ -48,6 +48,7 @@ function solcjs_test
printLog "Copying SMTChecker tests..." printLog "Copying SMTChecker tests..."
cp -Rf "$TEST_DIR"/test/libsolidity/smtCheckerTests test/ cp -Rf "$TEST_DIR"/test/libsolidity/smtCheckerTests test/
rm -rf test/smtCheckerTests/imports
# Update version (needed for some tests) # Update version (needed for some tests)
echo "Updating package.json to version $VERSION" echo "Updating package.json to version $VERSION"

View File

@ -30,6 +30,7 @@
#include <libsolidity/parsing/Parser.h> #include <libsolidity/parsing/Parser.h>
#include <libsolidity/analysis/DeclarationTypeChecker.h> #include <libsolidity/analysis/DeclarationTypeChecker.h>
#include <libsolidity/analysis/NameAndTypeResolver.h> #include <libsolidity/analysis/NameAndTypeResolver.h>
#include <libsolidity/analysis/Scoper.h>
#include <libsolidity/codegen/Compiler.h> #include <libsolidity/codegen/Compiler.h>
#include <libsolidity/ast/AST.h> #include <libsolidity/ast/AST.h>
#include <libsolidity/analysis/TypeChecker.h> #include <libsolidity/analysis/TypeChecker.h>
@ -59,6 +60,7 @@ evmasm::AssemblyItems compileContract(std::shared_ptr<CharStream> _sourceCode)
BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared<Scanner>(_sourceCode))); BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared<Scanner>(_sourceCode)));
BOOST_CHECK(!!sourceUnit); BOOST_CHECK(!!sourceUnit);
Scoper::assignScopes(*sourceUnit);
GlobalContext globalContext; GlobalContext globalContext;
NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter); NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter);
DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion()); DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion());

View File

@ -39,8 +39,8 @@ using namespace boost::unit_test;
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVersion, bool enforceViaYul): SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVersion, vector<boost::filesystem::path> const& _vmPaths, bool enforceViaYul):
SolidityExecutionFramework(_evmVersion), SolidityExecutionFramework(_evmVersion, _vmPaths),
EVMVersionRestrictedTestCase(_filename), EVMVersionRestrictedTestCase(_filename),
m_sources(m_reader.sources()), m_sources(m_reader.sources()),
m_lineOffset(m_reader.lineNumber()), m_lineOffset(m_reader.lineNumber()),
@ -72,6 +72,21 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer
else else
BOOST_THROW_EXCEPTION(runtime_error("Invalid compileViaYul value: " + choice + ".")); BOOST_THROW_EXCEPTION(runtime_error("Invalid compileViaYul value: " + choice + "."));
string compileToEwasm = m_reader.stringSetting("compileToEwasm", "false");
if (compileToEwasm == "also")
m_runWithEwasm = true;
else if (compileToEwasm == "false")
m_runWithEwasm = false;
else
BOOST_THROW_EXCEPTION(runtime_error("Invalid compileToEwasm value: " + compileToEwasm + "."));
if (m_runWithEwasm && !m_runWithYul)
BOOST_THROW_EXCEPTION(runtime_error("Invalid compileToEwasm value: " + compileToEwasm + ", compileViaYul need to be enabled."));
// run ewasm tests only, if an ewasm evmc vm was defined
if (m_runWithEwasm && !m_supportsEwasm)
m_runWithEwasm = false;
m_runWithABIEncoderV1Only = m_reader.boolSetting("ABIEncoderV1Only", false); m_runWithABIEncoderV1Only = m_reader.boolSetting("ABIEncoderV1Only", false);
if (m_runWithABIEncoderV1Only && solidity::test::CommonOptions::get().useABIEncoderV2) if (m_runWithABIEncoderV1Only && solidity::test::CommonOptions::get().useABIEncoderV2)
m_shouldRun = false; m_shouldRun = false;
@ -88,18 +103,44 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer
TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePrefix, bool _formatted) TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePrefix, bool _formatted)
{ {
TestResult result = TestResult::Success;
bool compileViaYul = m_runWithYul || m_enforceViaYul;
for (bool compileViaYul: set<bool>{!m_runWithoutYul, m_runWithYul || m_enforceViaYul}) if (m_runWithoutYul)
{ result = runTest(_stream, _linePrefix, _formatted, false, false);
if (compileViaYul && result == TestResult::Success)
result = runTest(_stream, _linePrefix, _formatted, true, false);
if (m_runWithEwasm && result == TestResult::Success)
result = runTest(_stream, _linePrefix, _formatted, true, true);
return result;
}
TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _linePrefix, bool _formatted, bool _compileViaYul, bool _compileToEwasm)
{
try try
{ {
reset();
bool success = true; bool success = true;
m_compileViaYul = compileViaYul; if (_compileViaYul && _compileToEwasm)
selectVM(evmc_capabilities::EVMC_CAPABILITY_EWASM);
else
selectVM(evmc_capabilities::EVMC_CAPABILITY_EVM1);
reset();
m_compileViaYul = _compileViaYul;
if (_compileToEwasm)
{
soltestAssert(m_compileViaYul, "");
m_compileToEwasm = _compileToEwasm;
}
m_compileViaYulCanBeSet = false; m_compileViaYulCanBeSet = false;
if (compileViaYul) if (_compileViaYul)
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul:" << endl; AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul:" << endl;
for (auto& test: m_tests) for (auto& test: m_tests)
@ -114,14 +155,15 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
if (constructed) if (constructed)
{ {
soltestAssert(!test.call().isLibrary, "Libraries have to be deployed before any other call."); soltestAssert(!test.call().isLibrary, "Libraries have to be deployed before any other call.");
soltestAssert(!test.call().isConstructor, "Constructor has to be the first function call expect for library deployments."); soltestAssert(
!test.call().isConstructor,
"Constructor has to be the first function call expect for library deployments.");
} }
else if (test.call().isLibrary) else if (test.call().isLibrary)
{ {
soltestAssert( soltestAssert(
deploy(test.call().signature, 0, {}, libraries) && m_transactionSuccessful, deploy(test.call().signature, 0, {}, libraries) && m_transactionSuccessful,
"Failed to deploy library " + test.call().signature "Failed to deploy library " + test.call().signature);
);
libraries[test.call().signature] = m_contractAddress; libraries[test.call().signature] = m_contractAddress;
continue; continue;
} }
@ -150,18 +192,17 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
else else
{ {
soltestAssert( soltestAssert(
m_allowNonExistingFunctions || m_compiler.methodIdentifiers(m_compiler.lastContractName()).isMember(test.call().signature), m_allowNonExistingFunctions || m_compiler.methodIdentifiers(m_compiler.lastContractName())
"The function " + test.call().signature + " is not known to the compiler" .isMember(test.call().signature),
); "The function " + test.call().signature + " is not known to the compiler");
output = callContractFunctionWithValueNoEncoding( output = callContractFunctionWithValueNoEncoding(
test.call().signature, test.call().signature, test.call().value.value, test.call().arguments.rawBytes()
test.call().value.value,
test.call().arguments.rawBytes()
); );
} }
if ((m_transactionSuccessful == test.call().expectations.failure) || (output != test.call().expectations.rawBytes())) if ((m_transactionSuccessful == test.call().expectations.failure)
|| (output != test.call().expectations.rawBytes()))
success = false; success = false;
test.setFailure(!m_transactionSuccessful); test.setFailure(!m_transactionSuccessful);
@ -170,15 +211,16 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
} }
} }
if (success && !m_runWithYul && compileViaYul) if (success && !m_runWithYul && _compileViaYul)
{ {
m_compileViaYulCanBeSet = true; m_compileViaYulCanBeSet = true;
AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) << _linePrefix << endl << _linePrefix AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) <<
<< "Test can pass via Yul and marked with compileViaYul: false." << endl; _linePrefix << endl <<
_linePrefix << "Test can pass via Yul and marked with compileViaYul: false." << endl;
return TestResult::Failure; return TestResult::Failure;
} }
if (!success && (m_runWithYul || !compileViaYul)) if (!success && (m_runWithYul || !_compileViaYul))
{ {
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl; AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
for (auto const& test: m_tests) for (auto const& test: m_tests)
@ -195,18 +237,19 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
_stream << test.format(errorReporter, _linePrefix, true, _formatted) << endl; _stream << test.format(errorReporter, _linePrefix, true, _formatted) << endl;
_stream << errorReporter.format(_linePrefix, _formatted); _stream << errorReporter.format(_linePrefix, _formatted);
} }
AnsiColorized(_stream, _formatted, {BOLD, RED}) << _linePrefix << endl << _linePrefix AnsiColorized(_stream, _formatted, {BOLD, RED})
<< "Attention: Updates on the test will apply the detected format displayed." << endl; << _linePrefix << endl
if (compileViaYul && m_runWithoutYul) << _linePrefix << "Attention: Updates on the test will apply the detected format displayed." << endl;
if (_compileViaYul && m_runWithoutYul)
{ {
_stream << _linePrefix << endl << _linePrefix; _stream << _linePrefix << endl << _linePrefix;
AnsiColorized(_stream, _formatted, {RED_BACKGROUND}) AnsiColorized(_stream, _formatted, {RED_BACKGROUND}) << "Note that the test passed without Yul.";
<< "Note that the test passed without Yul.";
_stream << endl; _stream << endl;
} }
else if (!compileViaYul && m_runWithYul) else if (!_compileViaYul && m_runWithYul)
AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) << _linePrefix << endl << _linePrefix AnsiColorized(_stream, _formatted, {BOLD, YELLOW})
<< "Note that the test also has to pass via Yul." << endl; << _linePrefix << endl
<< _linePrefix << "Note that the test also has to pass via Yul." << endl;
return TestResult::Failure; return TestResult::Failure;
} }
} }
@ -222,23 +265,19 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
} }
catch (boost::exception const&) catch (boost::exception const&)
{ {
if (compileViaYul && !m_runWithYul) if (!_compileViaYul || m_runWithYul)
continue;
throw; throw;
} }
catch (std::exception const&) catch (std::exception const&)
{ {
if (compileViaYul && !m_runWithYul) if (!_compileViaYul || m_runWithYul)
continue;
throw; throw;
} }
catch (...) catch (...)
{ {
if (compileViaYul && !m_runWithYul) if (!_compileViaYul || m_runWithYul)
continue;
throw; throw;
} }
}
return TestResult::Success; return TestResult::Success;
} }

View File

@ -40,9 +40,9 @@ class SemanticTest: public SolidityExecutionFramework, public EVMVersionRestrict
{ {
public: public:
static std::unique_ptr<TestCase> create(Config const& _options) static std::unique_ptr<TestCase> create(Config const& _options)
{ return std::make_unique<SemanticTest>(_options.filename, _options.evmVersion, _options.enforceCompileViaYul); } { return std::make_unique<SemanticTest>(_options.filename, _options.evmVersion, _options.vmPaths, _options.enforceCompileViaYul); }
explicit SemanticTest(std::string const& _filename, langutil::EVMVersion _evmVersion, bool _enforceViaYul = false); explicit SemanticTest(std::string const& _filename, langutil::EVMVersion _evmVersion, std::vector<boost::filesystem::path> const& _vmPaths, bool _enforceViaYul = false);
TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override; TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override;
void printSource(std::ostream &_stream, std::string const& _linePrefix = "", bool _formatted = false) const override; void printSource(std::ostream &_stream, std::string const& _linePrefix = "", bool _formatted = false) const override;
@ -59,10 +59,12 @@ public:
/// Returns true if deployment was successful, false otherwise. /// Returns true if deployment was successful, false otherwise.
bool deploy(std::string const& _contractName, u256 const& _value, bytes const& _arguments, std::map<std::string, solidity::test::Address> const& _libraries = {}); bool deploy(std::string const& _contractName, u256 const& _value, bytes const& _arguments, std::map<std::string, solidity::test::Address> const& _libraries = {});
private: private:
TestResult runTest(std::ostream& _stream, std::string const& _linePrefix, bool _formatted, bool _compileViaYul, bool _compileToEwasm);
SourceMap m_sources; SourceMap m_sources;
std::size_t m_lineOffset; std::size_t m_lineOffset;
std::vector<TestFunctionCall> m_tests; std::vector<TestFunctionCall> m_tests;
bool m_runWithYul = false; bool m_runWithYul = false;
bool m_runWithEwasm = false;
bool m_runWithoutYul = true; bool m_runWithoutYul = true;
bool m_enforceViaYul = false; bool m_enforceViaYul = false;
bool m_runWithABIEncoderV1Only = false; bool m_runWithABIEncoderV1Only = false;

View File

@ -50,16 +50,36 @@ using namespace solidity::langutil;
#define ALSO_VIA_YUL(CODE) \ #define ALSO_VIA_YUL(CODE) \
{ \ { \
m_doEwasmTestrun = true; \
\
m_compileViaYul = false; \
m_compileToEwasm = false; \
{ CODE } \ { CODE } \
reset(); \ \
m_compileViaYul = true; \ m_compileViaYul = true; \
reset(); \
{ CODE } \ { CODE } \
\
if (m_doEwasmTestrun) \
{ \
m_compileToEwasm = true; \
reset(); \
{ CODE } \
} \
} }
#define DISABLE_EWASM_TESTRUN() \
{ m_doEwasmTestrun = false; }
namespace solidity::frontend::test namespace solidity::frontend::test
{ {
BOOST_FIXTURE_TEST_SUITE(SolidityEndToEndTest, SolidityExecutionFramework) struct SolidityEndToEndTestExecutionFramework: public SolidityExecutionFramework
{
bool m_doEwasmTestrun = false;
};
BOOST_FIXTURE_TEST_SUITE(SolidityEndToEndTest, SolidityEndToEndTestExecutionFramework)
int constexpr roundTo32(int _num) int constexpr roundTo32(int _num)
{ {
@ -115,6 +135,8 @@ BOOST_AUTO_TEST_CASE(recursive_calls)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
function<u256(u256)> recursive_calls_cpp = [&recursive_calls_cpp](u256 const& n) -> u256 function<u256(u256)> recursive_calls_cpp = [&recursive_calls_cpp](u256 const& n) -> u256
{ {
@ -140,6 +162,8 @@ BOOST_AUTO_TEST_CASE(while_loop)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
auto while_loop_cpp = [](u256 const& n) -> u256 auto while_loop_cpp = [](u256 const& n) -> u256
@ -168,6 +192,8 @@ BOOST_AUTO_TEST_CASE(do_while_loop)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
auto do_while_loop_cpp = [](u256 const& n) -> u256 auto do_while_loop_cpp = [](u256 const& n) -> u256
@ -213,6 +239,8 @@ BOOST_AUTO_TEST_CASE(do_while_loop_multiple_local_vars)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
auto do_while = [](u256 n) -> u256 auto do_while = [](u256 n) -> u256
@ -263,6 +291,8 @@ BOOST_AUTO_TEST_CASE(nested_loops)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
auto nested_loops_cpp = [](u256 n) -> u256 auto nested_loops_cpp = [](u256 n) -> u256
@ -329,6 +359,8 @@ BOOST_AUTO_TEST_CASE(nested_loops_multiple_local_vars)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
auto nested_loops_cpp = [](u256 n) -> u256 auto nested_loops_cpp = [](u256 n) -> u256
@ -383,6 +415,8 @@ BOOST_AUTO_TEST_CASE(for_loop_multiple_local_vars)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
auto for_loop = [](u256 n) -> u256 auto for_loop = [](u256 n) -> u256
@ -444,6 +478,8 @@ BOOST_AUTO_TEST_CASE(nested_for_loop_multiple_local_vars)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
auto for_loop = [](u256 n) -> u256 auto for_loop = [](u256 n) -> u256
@ -484,6 +520,8 @@ BOOST_AUTO_TEST_CASE(for_loop)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
auto for_loop_cpp = [](u256 const& n) -> u256 auto for_loop_cpp = [](u256 const& n) -> u256
@ -512,6 +550,8 @@ BOOST_AUTO_TEST_CASE(for_loop_empty)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
auto for_loop_empty_cpp = []() -> u256 auto for_loop_empty_cpp = []() -> u256
@ -542,6 +582,8 @@ BOOST_AUTO_TEST_CASE(for_loop_simple_init_expr)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
auto for_loop_simple_init_expr_cpp = [](u256 const& n) -> u256 auto for_loop_simple_init_expr_cpp = [](u256 const& n) -> u256
@ -665,6 +707,8 @@ BOOST_AUTO_TEST_CASE(many_local_variables)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
auto f = [](u256 const& x1, u256 const& x2, u256 const& x3) -> u256 auto f = [](u256 const& x1, u256 const& x2, u256 const& x3) -> u256
{ {
@ -689,6 +733,8 @@ BOOST_AUTO_TEST_CASE(short_circuiting)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
auto short_circuiting_cpp = [](u256 n) -> u256 auto short_circuiting_cpp = [](u256 n) -> u256
@ -801,6 +847,8 @@ BOOST_AUTO_TEST_CASE(compound_assign)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
u256 value1; u256 value1;
@ -863,6 +911,8 @@ BOOST_AUTO_TEST_CASE(mapping_state)
map<u160, bool> m_voted; map<u160, bool> m_voted;
}; };
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
Ballot ballot; Ballot ballot;
@ -936,6 +986,8 @@ BOOST_AUTO_TEST_CASE(mapping_state_inc_dec)
return --table[value++]; return --table[value++];
}; };
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
value = 0; value = 0;
table.clear(); table.clear();
@ -962,6 +1014,8 @@ BOOST_AUTO_TEST_CASE(multi_level_mapping)
else return table[_x][_y] = _z; else return table[_x][_y] = _z;
}; };
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
table.clear(); table.clear();
@ -998,6 +1052,8 @@ BOOST_AUTO_TEST_CASE(constructor)
}; };
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
testContractAgainstCpp("get(uint256)", get, u256(6)); testContractAgainstCpp("get(uint256)", get, u256(6));
testContractAgainstCpp("get(uint256)", get, u256(7)); testContractAgainstCpp("get(uint256)", get, u256(7));
@ -1016,12 +1072,12 @@ BOOST_AUTO_TEST_CASE(blockchain)
} }
} }
)"; )";
m_evmHost->tx_context.block_coinbase = EVMHost::convertToEVMC(Address("0x1212121212121212121212121212121212121212")); m_evmcHost->tx_context.block_coinbase = EVMHost::convertToEVMC(Address("0x1212121212121212121212121212121212121212"));
m_evmHost->newBlock(); m_evmcHost->newBlock();
m_evmHost->newBlock(); m_evmcHost->newBlock();
m_evmHost->newBlock(); m_evmcHost->newBlock();
m_evmHost->newBlock(); m_evmcHost->newBlock();
m_evmHost->newBlock(); m_evmcHost->newBlock();
compileAndRun(sourceCode, 27); compileAndRun(sourceCode, 27);
ABI_CHECK(callContractFunctionWithValue("someInfo()", 28), encodeArgs(28, u256("0x1212121212121212121212121212121212121212"), 7)); ABI_CHECK(callContractFunctionWithValue("someInfo()", 28), encodeArgs(28, u256("0x1212121212121212121212121212121212121212"), 7));
} }
@ -1038,6 +1094,8 @@ BOOST_AUTO_TEST_CASE(send_ether)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
u256 amount(250); u256 amount(250);
compileAndRun(sourceCode, amount + 1); compileAndRun(sourceCode, amount + 1);
u160 address(23); u160 address(23);
@ -1070,6 +1128,8 @@ BOOST_AUTO_TEST_CASE(transfer_ether)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode, 0, "B"); compileAndRun(sourceCode, 0, "B");
u160 const nonPayableRecipient = m_contractAddress; u160 const nonPayableRecipient = m_contractAddress;
compileAndRun(sourceCode, 0, "C"); compileAndRun(sourceCode, 0, "C");
@ -1110,6 +1170,8 @@ BOOST_AUTO_TEST_CASE(log0)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
callContractFunction("a()"); callContractFunction("a()");
BOOST_REQUIRE_EQUAL(numLogs(), 1); BOOST_REQUIRE_EQUAL(numLogs(), 1);
@ -1129,6 +1191,8 @@ BOOST_AUTO_TEST_CASE(log1)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
callContractFunction("a()"); callContractFunction("a()");
BOOST_REQUIRE_EQUAL(numLogs(), 1); BOOST_REQUIRE_EQUAL(numLogs(), 1);
@ -1149,6 +1213,8 @@ BOOST_AUTO_TEST_CASE(log2)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
callContractFunction("a()"); callContractFunction("a()");
BOOST_REQUIRE_EQUAL(numLogs(), 1); BOOST_REQUIRE_EQUAL(numLogs(), 1);
@ -1170,6 +1236,8 @@ BOOST_AUTO_TEST_CASE(log3)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
callContractFunction("a()"); callContractFunction("a()");
BOOST_REQUIRE_EQUAL(numLogs(), 1); BOOST_REQUIRE_EQUAL(numLogs(), 1);
@ -1191,6 +1259,8 @@ BOOST_AUTO_TEST_CASE(log4)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
callContractFunction("a()"); callContractFunction("a()");
BOOST_REQUIRE_EQUAL(numLogs(), 1); BOOST_REQUIRE_EQUAL(numLogs(), 1);
@ -1212,6 +1282,8 @@ BOOST_AUTO_TEST_CASE(log_in_constructor)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
BOOST_REQUIRE_EQUAL(numLogs(), 1); BOOST_REQUIRE_EQUAL(numLogs(), 1);
BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress); BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress);
@ -1235,6 +1307,8 @@ BOOST_AUTO_TEST_CASE(selfdestruct)
u256 amount(130); u256 amount(130);
u160 address(23); u160 address(23);
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode, amount); compileAndRun(sourceCode, amount);
ABI_CHECK(callContractFunction("a(address)", address), bytes()); ABI_CHECK(callContractFunction("a(address)", address), bytes());
BOOST_CHECK(!addressHasCode(m_contractAddress)); BOOST_CHECK(!addressHasCode(m_contractAddress));
@ -1665,6 +1739,8 @@ BOOST_AUTO_TEST_CASE(gaslimit)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
auto result = callContractFunction("f()"); auto result = callContractFunction("f()");
ABI_CHECK(result, encodeArgs(gasLimit())); ABI_CHECK(result, encodeArgs(gasLimit()));
@ -1681,6 +1757,8 @@ BOOST_AUTO_TEST_CASE(gasprice)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("f()"), encodeArgs(gasPrice())); ABI_CHECK(callContractFunction("f()"), encodeArgs(gasPrice()));
) )
@ -1772,6 +1850,8 @@ BOOST_AUTO_TEST_CASE(event)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
u256 value(18); u256 value(18);
u256 id(0x1234); u256 id(0x1234);
@ -1800,6 +1880,8 @@ BOOST_AUTO_TEST_CASE(event_emit)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
u256 value(18); u256 value(18);
u256 id(0x1234); u256 id(0x1234);
@ -1826,6 +1908,8 @@ BOOST_AUTO_TEST_CASE(event_no_arguments)
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
callContractFunction("deposit()"); callContractFunction("deposit()");
BOOST_REQUIRE_EQUAL(numLogs(), 1); BOOST_REQUIRE_EQUAL(numLogs(), 1);
@ -1850,6 +1934,8 @@ BOOST_AUTO_TEST_CASE(event_access_through_base_name_emit)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
callContractFunction("f()"); callContractFunction("f()");
BOOST_REQUIRE_EQUAL(numLogs(), 1); BOOST_REQUIRE_EQUAL(numLogs(), 1);
@ -1889,6 +1975,8 @@ BOOST_AUTO_TEST_CASE(events_with_same_name)
u160 const c_loggedAddress = m_contractAddress; u160 const c_loggedAddress = m_contractAddress;
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("deposit()"), encodeArgs(u256(1))); ABI_CHECK(callContractFunction("deposit()"), encodeArgs(u256(1)));
BOOST_REQUIRE_EQUAL(numLogs(), 1); BOOST_REQUIRE_EQUAL(numLogs(), 1);
@ -1950,6 +2038,8 @@ BOOST_AUTO_TEST_CASE(events_with_same_name_inherited_emit)
u160 const c_loggedAddress = m_contractAddress; u160 const c_loggedAddress = m_contractAddress;
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("deposit()"), encodeArgs(u256(1))); ABI_CHECK(callContractFunction("deposit()"), encodeArgs(u256(1)));
BOOST_REQUIRE_EQUAL(numLogs(), 1); BOOST_REQUIRE_EQUAL(numLogs(), 1);
@ -1985,6 +2075,8 @@ BOOST_AUTO_TEST_CASE(event_anonymous)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
callContractFunction("deposit()"); callContractFunction("deposit()");
BOOST_REQUIRE_EQUAL(numLogTopics(0), 0); BOOST_REQUIRE_EQUAL(numLogTopics(0), 0);
@ -2002,6 +2094,8 @@ BOOST_AUTO_TEST_CASE(event_anonymous_with_topics)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
u256 value(18); u256 value(18);
u256 id(0x1234); u256 id(0x1234);
@ -2028,6 +2122,8 @@ BOOST_AUTO_TEST_CASE(event_lots_of_data)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
u256 value(18); u256 value(18);
u256 id(0x1234); u256 id(0x1234);
@ -2247,6 +2343,8 @@ BOOST_AUTO_TEST_CASE(event_dynamic_array_storage)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
u256 x(42); u256 x(42);
callContractFunction("createEvent(uint256)", x); callContractFunction("createEvent(uint256)", x);
@ -2276,6 +2374,8 @@ BOOST_AUTO_TEST_CASE(event_dynamic_array_storage_v2)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
u256 x(42); u256 x(42);
callContractFunction("createEvent(uint256)", x); callContractFunction("createEvent(uint256)", x);
@ -2365,6 +2465,8 @@ BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("f(uint256,uint256)", 5, 9) != encodeArgs(5, 8)); BOOST_CHECK(callContractFunction("f(uint256,uint256)", 5, 9) != encodeArgs(5, 8));
ABI_CHECK(callContractFunction("f(uint256,uint256)", 5, 9), encodeArgs(9, 8)); ABI_CHECK(callContractFunction("f(uint256,uint256)", 5, 9), encodeArgs(9, 8));
@ -2987,6 +3089,8 @@ BOOST_AUTO_TEST_CASE(fixed_array_cleanup)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
BOOST_CHECK(storageEmpty(m_contractAddress)); BOOST_CHECK(storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("fill()"), bytes()); ABI_CHECK(callContractFunction("fill()"), bytes());
@ -3010,6 +3114,8 @@ BOOST_AUTO_TEST_CASE(short_fixed_array_cleanup)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
BOOST_CHECK(storageEmpty(m_contractAddress)); BOOST_CHECK(storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("fill()"), bytes()); ABI_CHECK(callContractFunction("fill()"), bytes());
@ -3037,6 +3143,8 @@ BOOST_AUTO_TEST_CASE(dynamic_array_cleanup)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
BOOST_CHECK(storageEmpty(m_contractAddress)); BOOST_CHECK(storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("fill()"), bytes()); ABI_CHECK(callContractFunction("fill()"), bytes());
@ -4305,6 +4413,8 @@ BOOST_AUTO_TEST_CASE(string_as_mapping_key)
}; };
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode, 0, "Test"); compileAndRun(sourceCode, 0, "Test");
for (unsigned i = 0; i < strings.size(); i++) for (unsigned i = 0; i < strings.size(); i++)
ABI_CHECK(callContractFunction( ABI_CHECK(callContractFunction(
@ -5415,6 +5525,8 @@ BOOST_AUTO_TEST_CASE(no_nonpayable_circumvention_by_modifier)
} }
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode); compileAndRun(sourceCode);
ABI_CHECK(callContractFunctionWithValue("f()", 27), encodeArgs()); ABI_CHECK(callContractFunctionWithValue("f()", 27), encodeArgs());
BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0); BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0);
@ -5527,6 +5639,8 @@ BOOST_AUTO_TEST_CASE(contracts_separated_with_comment)
contract C2 {} contract C2 {}
)"; )";
ALSO_VIA_YUL( ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode, 0, "C1"); compileAndRun(sourceCode, 0, "C1");
compileAndRun(sourceCode, 0, "C2"); compileAndRun(sourceCode, 0, "C2");
) )

View File

@ -43,6 +43,7 @@ bytes SolidityExecutionFramework::multiSourceCompileContract(
entry.second = addPreamble(entry.second); entry.second = addPreamble(entry.second);
m_compiler.reset(); m_compiler.reset();
m_compiler.enableEwasmGeneration(m_compileToEwasm);
m_compiler.setSources(sourcesWithPreamble); m_compiler.setSources(sourcesWithPreamble);
m_compiler.setLibraries(_libraryAddresses); m_compiler.setLibraries(_libraryAddresses);
m_compiler.setRevertStringBehaviour(m_revertStrings); m_compiler.setRevertStringBehaviour(m_revertStrings);
@ -62,6 +63,10 @@ bytes SolidityExecutionFramework::multiSourceCompileContract(
std::string contractName(_contractName.empty() ? m_compiler.lastContractName() : _contractName); std::string contractName(_contractName.empty() ? m_compiler.lastContractName() : _contractName);
evmasm::LinkerObject obj; evmasm::LinkerObject obj;
if (m_compileViaYul) if (m_compileViaYul)
{
if (m_compileToEwasm)
obj = m_compiler.ewasmObject(contractName);
else
{ {
// Try compiling twice: If the first run fails due to stack errors, forcefully enable // Try compiling twice: If the first run fails due to stack errors, forcefully enable
// the optimizer. // the optimizer.
@ -77,11 +82,8 @@ bytes SolidityExecutionFramework::multiSourceCompileContract(
else if (forceEnableOptimizer) else if (forceEnableOptimizer)
optimiserSettings = OptimiserSettings::full(); optimiserSettings = OptimiserSettings::full();
yul::AssemblyStack asmStack( yul::AssemblyStack
m_evmVersion, asmStack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, optimiserSettings);
yul::AssemblyStack::Language::StrictAssembly,
optimiserSettings
);
bool analysisSuccessful = asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(contractName)); bool analysisSuccessful = asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(contractName));
solAssert(analysisSuccessful, "Code that passed analysis in CompilerStack can't have errors"); solAssert(analysisSuccessful, "Code that passed analysis in CompilerStack can't have errors");
@ -98,6 +100,7 @@ bytes SolidityExecutionFramework::multiSourceCompileContract(
} }
} }
} }
}
else else
obj = m_compiler.object(contractName); obj = m_compiler.object(contractName);
BOOST_REQUIRE(obj.linkReferences.empty()); BOOST_REQUIRE(obj.linkReferences.empty());

View File

@ -43,8 +43,8 @@ class SolidityExecutionFramework: public solidity::test::ExecutionFramework
public: public:
SolidityExecutionFramework(): m_showMetadata(solidity::test::CommonOptions::get().showMetadata) {} SolidityExecutionFramework(): m_showMetadata(solidity::test::CommonOptions::get().showMetadata) {}
explicit SolidityExecutionFramework(langutil::EVMVersion _evmVersion): explicit SolidityExecutionFramework(langutil::EVMVersion _evmVersion, std::vector<boost::filesystem::path> const& _vmPaths):
ExecutionFramework(_evmVersion), m_showMetadata(solidity::test::CommonOptions::get().showMetadata) ExecutionFramework(_evmVersion, _vmPaths), m_showMetadata(solidity::test::CommonOptions::get().showMetadata)
{} {}
bytes const& compileAndRunWithoutCheck( bytes const& compileAndRunWithoutCheck(
@ -76,8 +76,10 @@ public:
/// the latter only if it is required. /// the latter only if it is required.
static std::string addPreamble(std::string const& _sourceCode); static std::string addPreamble(std::string const& _sourceCode);
protected: protected:
solidity::frontend::CompilerStack m_compiler; solidity::frontend::CompilerStack m_compiler;
bool m_compileViaYul = false; bool m_compileViaYul = false;
bool m_compileToEwasm = false;
bool m_showMetadata = false; bool m_showMetadata = false;
RevertStrings m_revertStrings = RevertStrings::Default; RevertStrings m_revertStrings = RevertStrings::Default;
}; };

View File

@ -26,6 +26,7 @@
#include <liblangutil/Scanner.h> #include <liblangutil/Scanner.h>
#include <libsolidity/parsing/Parser.h> #include <libsolidity/parsing/Parser.h>
#include <libsolidity/analysis/NameAndTypeResolver.h> #include <libsolidity/analysis/NameAndTypeResolver.h>
#include <libsolidity/analysis/Scoper.h>
#include <libsolidity/analysis/DeclarationTypeChecker.h> #include <libsolidity/analysis/DeclarationTypeChecker.h>
#include <libsolidity/codegen/CompilerContext.h> #include <libsolidity/codegen/CompilerContext.h>
#include <libsolidity/codegen/ExpressionCompiler.h> #include <libsolidity/codegen/ExpressionCompiler.h>
@ -117,6 +118,7 @@ bytes compileFirstExpression(
ErrorList errors; ErrorList errors;
ErrorReporter errorReporter(errors); ErrorReporter errorReporter(errors);
GlobalContext globalContext; GlobalContext globalContext;
Scoper::assignScopes(*sourceUnit);
NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter); NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter);
resolver.registerDeclarations(*sourceUnit); resolver.registerDeclarations(*sourceUnit);
BOOST_REQUIRE_MESSAGE(resolver.resolveNamesAndTypes(*sourceUnit), "Resolving names failed"); BOOST_REQUIRE_MESSAGE(resolver.resolveNamesAndTypes(*sourceUnit), "Resolving names failed");

View File

@ -17,9 +17,9 @@ contract C {
// optimize-yul: true // optimize-yul: true
// ---- // ----
// creation: // creation:
// codeDepositCost: 614600 // codeDepositCost: 597000
// executionCost: 651 // executionCost: 632
// totalCost: 615251 // totalCost: 597632
// external: // external:
// a(): 1029 // a(): 1029
// b(uint256): 2084 // b(uint256): 2084

View File

@ -12,5 +12,7 @@ contract c {
} }
} }
// ====
// compileViaYul: also
// ---- // ----
// test() -> 1, 2, 3, 4 // test() -> 1, 2, 3, 4

View File

@ -0,0 +1,16 @@
contract C {
uint8[33] a;
uint32[9] b;
uint120[3] c;
function f() public returns (uint8, uint32, uint120) {
a[32] = 1; a[31] = 2; a[30] = 3;
b[0] = 1; b[1] = 2; b[2] = 3;
c[2] = 3; c[1] = 1;
return (a[32], b[1], c[2]);
}
}
// ====
// compileViaYul: also
// ----
// f() -> 1, 2, 3

View File

@ -8,4 +8,4 @@ contract C {
} }
} }
// ---- // ----
// Warning 2529: (82-89): Empty array "pop" detected here // Warning 2529: (82-89): Empty array "pop" detected here.

View File

@ -8,4 +8,4 @@ contract C {
} }
} }
// ---- // ----
// Warning 2529: (82-89): Empty array "pop" detected here // Warning 2529: (82-89): Empty array "pop" detected here.

View File

@ -8,5 +8,5 @@ contract C {
} }
} }
// ---- // ----
// Warning 2529: (82-89): Empty array "pop" detected here // Warning 2529: (82-89): Empty array "pop" detected here.
// Warning 2529: (93-100): Empty array "pop" detected here // Warning 2529: (93-100): Empty array "pop" detected here.

View File

@ -8,4 +8,4 @@ contract C {
} }
} }
// ---- // ----
// Warning 2529: (94-101): Empty array "pop" detected here // Warning 2529: (94-101): Empty array "pop" detected here.

View File

@ -11,4 +11,4 @@ contract C {
} }
} }
// ---- // ----
// Warning 2529: (122-129): Empty array "pop" detected here // Warning 2529: (122-129): Empty array "pop" detected here.

View File

@ -11,4 +11,4 @@ contract C {
} }
} }
// ---- // ----
// Warning 2529: (127-134): Empty array "pop" detected here // Warning 2529: (127-134): Empty array "pop" detected here.

View File

@ -13,4 +13,4 @@ contract C {
} }
} }
// ---- // ----
// Warning 2529: (82-89): Empty array "pop" detected here // Warning 2529: (82-89): Empty array "pop" detected here.

View File

@ -10,4 +10,4 @@ contract C {
} }
} }
// ---- // ----
// Warning 6328: (153-176): Assertion violation happens here // Warning 6328: (153-176): Assertion violation happens here.

View File

@ -14,6 +14,6 @@ contract C {
} }
} }
// ---- // ----
// Warning 6328: (198-224): Assertion violation happens here // Warning 6328: (198-224): Assertion violation happens here.
// Warning 6328: (228-254): Assertion violation happens here // Warning 6328: (228-254): Assertion violation happens here.
// Warning 6328: (258-281): Assertion violation happens here // Warning 6328: (258-281): Assertion violation happens here.

View File

@ -16,7 +16,7 @@ contract C {
} }
} }
// ---- // ----
// Warning 6328: (222-248): Assertion violation happens here // Warning 6328: (222-248): Assertion violation happens here.
// Warning 6328: (252-278): Assertion violation happens here // Warning 6328: (252-278): Assertion violation happens here.
// Warning 6328: (282-305): Assertion violation happens here // Warning 6328: (282-305): Assertion violation happens here.
// Warning 6328: (309-335): Assertion violation happens here // Warning 6328: (309-335): Assertion violation happens here.

View File

@ -7,4 +7,4 @@ contract C {
} }
} }
// ---- // ----
// Warning 2529: (82-89): Empty array "pop" detected here // Warning 2529: (82-89): Empty array "pop" detected here.

View File

@ -9,4 +9,4 @@ contract C {
} }
} }
// ---- // ----
// Warning 2529: (111-121): Empty array "pop" detected here // Warning 2529: (111-121): Empty array "pop" detected here.

View File

@ -7,4 +7,4 @@ contract C {
} }
} }
// ---- // ----
// Warning 2529: (76-83): Empty array "pop" detected here // Warning 2529: (76-83): Empty array "pop" detected here.

View File

@ -11,4 +11,4 @@ contract C {
} }
} }
// ---- // ----
// Warning 2529: (150-157): Empty array "pop" detected here // Warning 2529: (150-157): Empty array "pop" detected here.

View File

@ -10,5 +10,5 @@ contract C {
} }
} }
// ---- // ----
// Warning 3944: (162-177): Underflow (resulting value less than 0) happens here // Warning 3944: (162-177): Underflow (resulting value less than 0) happens here.
// Warning 6328: (150-184): Assertion violation happens here // Warning 6328: (150-184): Assertion violation happens here.

View File

@ -8,5 +8,5 @@ contract C {
} }
} }
// ---- // ----
// Warning 6328: (113-139): Assertion violation happens here // Warning 6328: (113-139): Assertion violation happens here.
// Warning 6328: (143-189): Assertion violation happens here // Warning 6328: (143-189): Assertion violation happens here.

View File

@ -10,6 +10,6 @@ contract C {
} }
} }
// ---- // ----
// Warning 6328: (122-148): Assertion violation happens here // Warning 6328: (122-148): Assertion violation happens here.
// Warning 6328: (202-218): Assertion violation happens here // Warning 6328: (202-218): Assertion violation happens here.
// Warning 6328: (222-278): Assertion violation happens here // Warning 6328: (222-278): Assertion violation happens here.

View File

@ -12,5 +12,5 @@ contract C {
} }
} }
// ---- // ----
// Warning 3944: (217-232): Underflow (resulting value less than 0) happens here // Warning 3944: (217-232): Underflow (resulting value less than 0) happens here.
// Warning 6328: (205-239): Assertion violation happens here // Warning 6328: (205-239): Assertion violation happens here.

View File

@ -12,4 +12,4 @@ contract C {
} }
} }
// ---- // ----
// Warning 6328: (167-188): Assertion violation happens here // Warning 6328: (167-188): Assertion violation happens here.

View File

@ -18,6 +18,6 @@ contract C {
} }
} }
// ---- // ----
// Warning 6328: (193-217): Assertion violation happens here // Warning 6328: (193-217): Assertion violation happens here.
// Warning 6328: (309-333): Assertion violation happens here // Warning 6328: (309-333): Assertion violation happens here.
// Warning 6328: (419-436): Assertion violation happens here // Warning 6328: (419-436): Assertion violation happens here.

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