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:
ubuntu-1804-docker-image:
type: string
# solbuildpackpusher/solidity-buildpack-deps:ubuntu1804-2
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:9ab317e583c395e50884ba82e9f99811c374344cea4c550725be8ec836e07acc"
# solbuildpackpusher/solidity-buildpack-deps:ubuntu1804-3
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:19f613d2ac47fedff654dacef984d8a64726c4d67ae8f2667a85ee7d97ac4c1c"
ubuntu-2004-docker-image:
type: string
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-2
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:cbfa42d8ecbe94391ba8837e218869242666de7a0da6ccac065a856c85b6a6a0"
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004-3
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:aeedbe7390a7383815f0cf0f8a1b8bf84dc5e334a3b0043ebcdf8b1bdbe80a81"
ubuntu-2004-clang-docker-image:
type: string
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-2
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:7a4d5271b5552139d9f2caefc50d42f401bf74132cf8f253e199e11c80ab42de"
# solbuildpackpusher/solidity-buildpack-deps:ubuntu2004.clang-3
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:2593c15689dee5b5bdfff96a36c8c68a468cd3b147c41f75b820b8fabc257be9"
ubuntu-1604-clang-ossfuzz-docker-image:
type: string
# solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-3
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:6fa6914bd81abcac4b162c738e6ff05d87cefe7655e3859c7a827e5a8ec20dc7"
# solbuildpackpusher/solidity-buildpack-deps:ubuntu1604.clang.ossfuzz-4
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:842126b164b3542f05bff2611459e21edc7e3e2c81ca9d1f43396c8cf066f7ca"
emscripten-docker-image:
type: string
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:d557d015918c3cf68b0d22839bab41013f0757b651a7fef21595f89721dbebcc"
default: "solbuildpackpusher/solidity-buildpack-deps@sha256:23dad3b34deae8107c8551804ef299f6a89c23ed506e8118fac151e2bdc9018c"
defaults:
@ -208,6 +208,11 @@ defaults:
requires:
- b_ubu_release
- workflow_archlinux: &workflow_archlinux
<<: *workflow_trigger_on_tags
requires:
- b_archlinux
- workflow_ubuntu2004_codecov: &workflow_ubuntu2004_codecov
<<: *workflow_trigger_on_tags
requires:
@ -657,6 +662,25 @@ jobs:
t_ubu_soltest: &t_ubu_soltest
<<: *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
docker:
- image: << pipeline.parameters.ubuntu-2004-docker-image >>
@ -870,7 +894,6 @@ workflows:
# build-only
- b_docs: *workflow_trigger_on_tags
- b_archlinux: *workflow_trigger_on_tags
- b_ubu_cxx20: *workflow_trigger_on_tags
- b_ubu_ossfuzz: *workflow_trigger_on_tags
@ -879,6 +902,10 @@ workflows:
- t_osx_cli: *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
- b_ubu: *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
# z3
wget https://github.com/Z3Prover/z3/releases/download/z3-4.8.8/z3-4.8.8-x64-osx-10.14.6.zip
unzip z3-4.8.8-x64-osx-10.14.6.zip
rm -f z3-4.8.8-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.8-x64-osx-10.14.6/bin/z3 /usr/local/bin
cp z3-4.8.8-x64-osx-10.14.6/include/* /usr/local/include
rm -rf z3-4.8.8-x64-osx-10.14.6
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.9-x64-osx-10.14.6.zip
rm -f z3-4.8.9-x64-osx-10.14.6.zip
cp z3-4.8.9-x64-osx-10.14.6/bin/libz3.a /usr/local/lib
cp z3-4.8.9-x64-osx-10.14.6/bin/z3 /usr/local/bin
cp z3-4.8.9-x64-osx-10.14.6/include/* /usr/local/include
rm -rf z3-4.8.9-x64-osx-10.14.6
# evmone
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:
- nvm install 10
- nvm use 10
- docker pull solbuildpackpusher/solidity-buildpack-deps@sha256:d557d015918c3cf68b0d22839bab41013f0757b651a7fef21595f89721dbebcc
- docker pull solbuildpackpusher/solidity-buildpack-deps@sha256:23dad3b34deae8107c8551804ef299f6a89c23ed506e8118fac151e2bdc9018c
env:
- SOLC_EMSCRIPTEN=On
- SOLC_INSTALL_DEPS_TRAVIS=Off

View File

@ -8,12 +8,19 @@ Language Features:
Compiler Features:
* SMTChecker: Support shifts.
* SMTChecker: Support structs.
* SMTChecker: Support ``type(T).min`` and ``type(T).max``.
* 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:
* Type Checker: Disallow ``virtual`` for modifiers in libraries.
* 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)
@ -43,6 +50,7 @@ Bugfixes:
* SMTChecker: Fix internal error on lvalue unary operators with tuples.
* SMTChecker: Fix internal error on tuple assignment.
* 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``.
* Type Checker: Disallow ``using for`` directive inside interfaces.
* 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
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.
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.
3. Protection against replay attacks.
A replay attack is when a signed message is reused to claim authorization for
a second action.
To avoid replay attacks we use the same as in Ethereum transactions
themselves, a so-called nonce, which is the number of transactions sent by an
account.
The smart contract checks if a nonce is used multiple times.
A replay attack is when a signed message is reused to claim
authorization for a second action. To avoid replay attacks
we use the same technique as in Ethereum transactions themselves,
a so-called nonce, which is the number of transactions sent by
an account. The smart contract checks if a nonce is used multiple times.
Another type of replay attack can occur when the owner
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
to memory or storage locations in the built-in functions
``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.
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 Builtins = typename Pattern::Builtins;
return std::vector<SimplificationRule<Pattern>> {
return std::vector<SimplificationRule<Pattern>>{
// arithmetic on constants
{Builtins::ADD(A, B), [=]{ return A.d() + B.d(); }, false},
{Builtins::MUL(A, B), [=]{ return A.d() * B.d(); }, false},
{Builtins::SUB(A, B), [=]{ return A.d() - B.d(); }, false},
{Builtins::DIV(A, B), [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }, false},
{Builtins::SDIV(A, B), [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }, false},
{Builtins::MOD(A, B), [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }, false},
{Builtins::SMOD(A, B), [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }, false},
{Builtins::EXP(A, B), [=]{ return Word(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << Pattern::WordSize)); }, false},
{Builtins::NOT(A), [=]{ return ~A.d(); }, false},
{Builtins::LT(A, B), [=]() -> Word { return A.d() < B.d() ? 1 : 0; }, false},
{Builtins::GT(A, B), [=]() -> Word { return A.d() > B.d() ? 1 : 0; }, false},
{Builtins::SLT(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; }, false},
{Builtins::EQ(A, B), [=]() -> Word { return A.d() == B.d() ? 1 : 0; }, false},
{Builtins::ISZERO(A), [=]() -> Word { return A.d() == 0 ? 1 : 0; }, false},
{Builtins::AND(A, B), [=]{ return A.d() & B.d(); }, false},
{Builtins::OR(A, B), [=]{ return A.d() | B.d(); }, false},
{Builtins::XOR(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(); }},
{Builtins::SUB(A, B), [=]{ return A.d() - B.d(); }},
{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()))); }},
{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()))); }},
{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(); }},
{Builtins::LT(A, B), [=]() -> Word { return A.d() < B.d() ? 1 : 0; }},
{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; }},
{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; }},
{Builtins::ISZERO(A), [=]() -> Word { return A.d() == 0 ? 1 : 0; }},
{Builtins::AND(A, B), [=]{ return A.d() & B.d(); }},
{Builtins::OR(A, B), [=]{ return A.d() | B.d(); }},
{Builtins::XOR(A, B), [=]{ return A.d() ^ B.d(); }},
{Builtins::BYTE(A, B), [=]{
return
A.d() >= Pattern::WordSize / 8 ?
0 :
(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::MULMOD(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()); }},
{Builtins::SIGNEXTEND(A, B), [=]() -> Word {
if (A.d() >= Pattern::WordSize / 8 - 1)
return B.d();
unsigned testBit = unsigned(A.d()) * 8 + 7;
Word mask = (Word(1) << testBit) - 1;
return boost::multiprecision::bit_test(B.d(), testBit) ? B.d() | ~mask : B.d() & mask;
}, false},
}},
{Builtins::SHL(A, B), [=]{
if (A.d() >= Pattern::WordSize)
return Word(0);
return shlWorkaround(B.d(), unsigned(A.d()));
}, false},
}},
{Builtins::SHR(A, B), [=]{
if (A.d() >= Pattern::WordSize)
return Word(0);
return B.d() >> unsigned(A.d());
}, false}
}}
};
}
@ -133,48 +133,48 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2(
using Builtins = typename Pattern::Builtins;
return std::vector<SimplificationRule<Pattern>> {
// invariants involving known constants
{Builtins::ADD(X, 0), [=]{ return X; }, false},
{Builtins::ADD(0, X), [=]{ return X; }, false},
{Builtins::SUB(X, 0), [=]{ return X; }, false},
{Builtins::SUB(~Word(0), X), [=]() -> Pattern { return Builtins::NOT(X); }, false},
{Builtins::MUL(X, 0), [=]{ return Word(0); }, true},
{Builtins::MUL(0, X), [=]{ return Word(0); }, true},
{Builtins::MUL(X, 1), [=]{ return X; }, false},
{Builtins::MUL(1, X), [=]{ return X; }, false},
{Builtins::MUL(X, Word(-1)), [=]() -> Pattern { return Builtins::SUB(0, X); }, false},
{Builtins::MUL(Word(-1), X), [=]() -> Pattern { return Builtins::SUB(0, X); }, false},
{Builtins::DIV(X, 0), [=]{ return Word(0); }, true},
{Builtins::DIV(0, X), [=]{ return Word(0); }, true},
{Builtins::DIV(X, 1), [=]{ return X; }, false},
{Builtins::SDIV(X, 0), [=]{ return Word(0); }, true},
{Builtins::SDIV(0, X), [=]{ return Word(0); }, true},
{Builtins::SDIV(X, 1), [=]{ return X; }, false},
{Builtins::AND(X, ~Word(0)), [=]{ return X; }, false},
{Builtins::AND(~Word(0), X), [=]{ return X; }, false},
{Builtins::AND(X, 0), [=]{ return Word(0); }, true},
{Builtins::AND(0, X), [=]{ return Word(0); }, true},
{Builtins::OR(X, 0), [=]{ return X; }, false},
{Builtins::OR(0, X), [=]{ return X; }, false},
{Builtins::OR(X, ~Word(0)), [=]{ return ~Word(0); }, true},
{Builtins::OR(~Word(0), X), [=]{ return ~Word(0); }, true},
{Builtins::XOR(X, 0), [=]{ return X; }, false},
{Builtins::XOR(0, X), [=]{ return X; }, false},
{Builtins::MOD(X, 0), [=]{ return Word(0); }, true},
{Builtins::MOD(0, X), [=]{ return Word(0); }, true},
{Builtins::EQ(X, 0), [=]() -> Pattern { return Builtins::ISZERO(X); }, false },
{Builtins::EQ(0, X), [=]() -> Pattern { return Builtins::ISZERO(X); }, false },
{Builtins::SHL(0, X), [=]{ return X; }, false},
{Builtins::SHR(0, X), [=]{ return X; }, false},
{Builtins::SHL(X, 0), [=]{ return Word(0); }, true},
{Builtins::SHR(X, 0), [=]{ return Word(0); }, true},
{Builtins::GT(X, 0), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }, false},
{Builtins::LT(0, X), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }, false},
{Builtins::GT(X, ~Word(0)), [=]{ return Word(0); }, true},
{Builtins::LT(~Word(0), X), [=]{ return Word(0); }, true},
{Builtins::GT(0, X), [=]{ return Word(0); }, true},
{Builtins::LT(X, 0), [=]{ return Word(0); }, true},
{Builtins::AND(Builtins::BYTE(X, Y), Word(0xff)), [=]() -> Pattern { return Builtins::BYTE(X, Y); }, false},
{Builtins::BYTE(Word(Pattern::WordSize / 8 - 1), X), [=]() -> Pattern { return Builtins::AND(X, Word(0xff)); }, false}
{Builtins::ADD(X, 0), [=]{ return X; }},
{Builtins::ADD(0, X), [=]{ return X; }},
{Builtins::SUB(X, 0), [=]{ return X; }},
{Builtins::SUB(~Word(0), X), [=]() -> Pattern { return Builtins::NOT(X); }},
{Builtins::MUL(X, 0), [=]{ return Word(0); }},
{Builtins::MUL(0, X), [=]{ return Word(0); }},
{Builtins::MUL(X, 1), [=]{ return X; }},
{Builtins::MUL(1, X), [=]{ return X; }},
{Builtins::MUL(X, Word(-1)), [=]() -> Pattern { return Builtins::SUB(0, X); }},
{Builtins::MUL(Word(-1), X), [=]() -> Pattern { return Builtins::SUB(0, X); }},
{Builtins::DIV(X, 0), [=]{ return Word(0); }},
{Builtins::DIV(0, X), [=]{ return Word(0); }},
{Builtins::DIV(X, 1), [=]{ return X; }},
{Builtins::SDIV(X, 0), [=]{ return Word(0); }},
{Builtins::SDIV(0, X), [=]{ return Word(0); }},
{Builtins::SDIV(X, 1), [=]{ return X; }},
{Builtins::AND(X, ~Word(0)), [=]{ return X; }},
{Builtins::AND(~Word(0), X), [=]{ return X; }},
{Builtins::AND(X, 0), [=]{ return Word(0); }},
{Builtins::AND(0, X), [=]{ return Word(0); }},
{Builtins::OR(X, 0), [=]{ return X; }},
{Builtins::OR(0, X), [=]{ return X; }},
{Builtins::OR(X, ~Word(0)), [=]{ return ~Word(0); }},
{Builtins::OR(~Word(0), X), [=]{ return ~Word(0); }},
{Builtins::XOR(X, 0), [=]{ return X; }},
{Builtins::XOR(0, X), [=]{ return X; }},
{Builtins::MOD(X, 0), [=]{ return Word(0); }},
{Builtins::MOD(0, X), [=]{ return Word(0); }},
{Builtins::EQ(X, 0), [=]() -> Pattern { return Builtins::ISZERO(X); },},
{Builtins::EQ(0, X), [=]() -> Pattern { return Builtins::ISZERO(X); },},
{Builtins::SHL(0, X), [=]{ return X; }},
{Builtins::SHR(0, X), [=]{ return X; }},
{Builtins::SHL(X, 0), [=]{ return Word(0); }},
{Builtins::SHR(X, 0), [=]{ return Word(0); }},
{Builtins::GT(X, 0), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }},
{Builtins::LT(0, X), [=]() -> Pattern { return Builtins::ISZERO(Builtins::ISZERO(X)); }},
{Builtins::GT(X, ~Word(0)), [=]{ return Word(0); }},
{Builtins::LT(~Word(0), X), [=]{ return Word(0); }},
{Builtins::GT(0, X), [=]{ return Word(0); }},
{Builtins::LT(X, 0), [=]{ return Word(0); }},
{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)); }},
};
}
@ -191,16 +191,16 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart3(
using Builtins = typename Pattern::Builtins;
return std::vector<SimplificationRule<Pattern>> {
// operations involving an expression and itself
{Builtins::AND(X, X), [=]{ return X; }, true},
{Builtins::OR(X, X), [=]{ return X; }, true},
{Builtins::XOR(X, X), [=]{ return Word(0); }, true},
{Builtins::SUB(X, X), [=]{ return Word(0); }, true},
{Builtins::EQ(X, X), [=]{ return Word(1); }, true},
{Builtins::LT(X, X), [=]{ return Word(0); }, true},
{Builtins::SLT(X, X), [=]{ return Word(0); }, true},
{Builtins::GT(X, X), [=]{ return Word(0); }, true},
{Builtins::SGT(X, X), [=]{ return Word(0); }, true},
{Builtins::MOD(X, X), [=]{ return Word(0); }, true}
{Builtins::AND(X, X), [=]{ return X; }},
{Builtins::OR(X, X), [=]{ return X; }},
{Builtins::XOR(X, X), [=]{ return Word(0); }},
{Builtins::SUB(X, X), [=]{ return Word(0); }},
{Builtins::EQ(X, X), [=]{ return Word(1); }},
{Builtins::LT(X, X), [=]{ return Word(0); }},
{Builtins::SLT(X, X), [=]{ return Word(0); }},
{Builtins::GT(X, X), [=]{ return Word(0); }},
{Builtins::SGT(X, X), [=]{ return Word(0); }},
{Builtins::MOD(X, X), [=]{ return Word(0); }}
};
}
@ -217,23 +217,23 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart4(
using Builtins = typename Pattern::Builtins;
return std::vector<SimplificationRule<Pattern>> {
// logical instruction combinations
{Builtins::NOT(Builtins::NOT(X)), [=]{ return X; }, false},
{Builtins::XOR(X, Builtins::XOR(X, Y)), [=]{ return Y; }, true},
{Builtins::XOR(X, Builtins::XOR(Y, X)), [=]{ return Y; }, true},
{Builtins::XOR(Builtins::XOR(X, Y), X), [=]{ return Y; }, true},
{Builtins::XOR(Builtins::XOR(Y, X), X), [=]{ return Y; }, true},
{Builtins::OR(X, Builtins::AND(X, Y)), [=]{ return X; }, true},
{Builtins::OR(X, Builtins::AND(Y, X)), [=]{ return X; }, true},
{Builtins::OR(Builtins::AND(X, Y), X), [=]{ return X; }, true},
{Builtins::OR(Builtins::AND(Y, X), X), [=]{ return X; }, true},
{Builtins::AND(X, Builtins::OR(X, Y)), [=]{ return X; }, true},
{Builtins::AND(X, Builtins::OR(Y, X)), [=]{ return X; }, true},
{Builtins::AND(Builtins::OR(X, Y), X), [=]{ return X; }, true},
{Builtins::AND(Builtins::OR(Y, X), X), [=]{ return X; }, true},
{Builtins::AND(X, Builtins::NOT(X)), [=]{ return Word(0); }, true},
{Builtins::AND(Builtins::NOT(X), X), [=]{ return Word(0); }, true},
{Builtins::OR(X, Builtins::NOT(X)), [=]{ return ~Word(0); }, true},
{Builtins::OR(Builtins::NOT(X), X), [=]{ return ~Word(0); }, true},
{Builtins::NOT(Builtins::NOT(X)), [=]{ return X; }},
{Builtins::XOR(X, Builtins::XOR(X, Y)), [=]{ return Y; }},
{Builtins::XOR(X, Builtins::XOR(Y, X)), [=]{ return Y; }},
{Builtins::XOR(Builtins::XOR(X, Y), X), [=]{ return Y; }},
{Builtins::XOR(Builtins::XOR(Y, X), X), [=]{ return Y; }},
{Builtins::OR(X, Builtins::AND(X, Y)), [=]{ return X; }},
{Builtins::OR(X, Builtins::AND(Y, X)), [=]{ return X; }},
{Builtins::OR(Builtins::AND(X, Y), X), [=]{ return X; }},
{Builtins::OR(Builtins::AND(Y, X), X), [=]{ return X; }},
{Builtins::AND(X, Builtins::OR(X, Y)), [=]{ return X; }},
{Builtins::AND(X, Builtins::OR(Y, X)), [=]{ return X; }},
{Builtins::AND(Builtins::OR(X, Y), X), [=]{ return X; }},
{Builtins::AND(Builtins::OR(Y, X), X), [=]{ return X; }},
{Builtins::AND(X, Builtins::NOT(X)), [=]{ return Word(0); }},
{Builtins::AND(Builtins::NOT(X), X), [=]{ return Word(0); }},
{Builtins::OR(X, Builtins::NOT(X)), [=]{ return ~Word(0); }},
{Builtins::OR(Builtins::NOT(X), X), [=]{ return ~Word(0); }},
};
}
@ -249,14 +249,14 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart4_5(
using Builtins = typename Pattern::Builtins;
return std::vector<SimplificationRule<Pattern>>{
// idempotent operations
{Builtins::AND(Builtins::AND(X, Y), Y), [=]{ return Builtins::AND(X, Y); }, true},
{Builtins::AND(Y, Builtins::AND(X, Y)), [=]{ return Builtins::AND(X, Y); }, true},
{Builtins::AND(Builtins::AND(Y, X), Y), [=]{ return Builtins::AND(Y, X); }, true},
{Builtins::AND(Y, Builtins::AND(Y, X)), [=]{ return Builtins::AND(Y, X); }, true},
{Builtins::OR(Builtins::OR(X, Y), Y), [=]{ return Builtins::OR(X, Y); }, true},
{Builtins::OR(Y, Builtins::OR(X, Y)), [=]{ return Builtins::OR(X, Y); }, true},
{Builtins::OR(Builtins::OR(Y, X), Y), [=]{ return Builtins::OR(Y, X); }, true},
{Builtins::OR(Y, Builtins::OR(Y, X)), [=]{ return Builtins::OR(Y, X); }, 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); }},
{Builtins::AND(Builtins::AND(Y, X), Y), [=]{ return Builtins::AND(Y, X); }},
{Builtins::AND(Y, Builtins::AND(Y, X)), [=]{ return Builtins::AND(Y, X); }},
{Builtins::OR(Builtins::OR(X, Y), Y), [=]{ return Builtins::OR(X, Y); }},
{Builtins::OR(Y, Builtins::OR(X, Y)), [=]{ return Builtins::OR(X, Y); }},
{Builtins::OR(Builtins::OR(Y, X), Y), [=]{ return Builtins::OR(Y, X); }},
{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;
rules.push_back({
Builtins::MOD(X, value),
[=]() -> Pattern { return Builtins::AND(X, value - 1); },
false
[=]() -> Pattern { return Builtins::AND(X, value - 1); }
});
}
@ -289,7 +288,6 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5(
rules.push_back({
Builtins::SHL(A, X),
[=]() -> Pattern { return Word(0); },
true,
[=]() { return A.d() >= Pattern::WordSize; }
});
@ -297,7 +295,6 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5(
rules.push_back({
Builtins::SHR(A, X),
[=]() -> Pattern { return Word(0); },
true,
[=]() { return A.d() >= Pattern::WordSize; }
});
@ -305,7 +302,6 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5(
rules.push_back({
Builtins::BYTE(A, X),
[=]() -> Pattern { return Word(0); },
true,
[=]() { return A.d() >= Pattern::WordSize / 8; }
});
@ -320,13 +316,11 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5(
Word const mask = (Word(1) << 160) - 1;
rules.push_back({
Builtins::AND(Pattern{instr}, mask),
[=]() -> Pattern { return {instr}; },
false
[=]() -> Pattern { return {instr}; }
});
rules.push_back({
Builtins::AND(mask, Pattern{instr}),
[=]() -> Pattern { return {instr}; },
false
[=]() -> Pattern { return {instr}; }
});
}
@ -357,21 +351,18 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart6(
typename Builtins::PatternGeneratorInstance op{instr};
rules.push_back({
Builtins::ISZERO(Builtins::ISZERO(op(X, Y))),
[=]() -> Pattern { return op(X, Y); },
false
[=]() -> Pattern { return op(X, Y); }
});
}
rules.push_back({
Builtins::ISZERO(Builtins::ISZERO(Builtins::ISZERO(X))),
[=]() -> Pattern { return Builtins::ISZERO(X); },
false
[=]() -> Pattern { return Builtins::ISZERO(X); }
});
rules.push_back({
Builtins::ISZERO(Builtins::XOR(X, Y)),
[=]() -> Pattern { return Builtins::EQ(X, Y); },
false
[=]() -> Pattern { return Builtins::EQ(X, Y); }
});
return rules;
@ -409,23 +400,19 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
rules += std::vector<SimplificationRule<Pattern>>{{
// (X+A)+B -> X+(A+B)
op(opXA, B),
[=]() -> Pattern { return op(X, fun(A.d(), B.d())); },
false
[=]() -> Pattern { return op(X, fun(A.d(), B.d())); }
}, {
// (X+A)+Y -> (X+Y)+A
op(opXA, Y),
[=]() -> Pattern { return op(op(X, Y), A); },
false
[=]() -> Pattern { return op(op(X, Y), A); }
}, {
// B+(X+A) -> X+(A+B)
op(B, opXA),
[=]() -> Pattern { return op(X, fun(A.d(), B.d())); },
false
[=]() -> Pattern { return op(X, fun(A.d(), B.d())); }
}, {
// Y+(X+A) -> (Y+X)+A
op(Y, opXA),
[=]() -> Pattern { return op(op(Y, X), A); },
false
[=]() -> Pattern { return op(op(Y, X), A); }
}};
}
}
@ -440,8 +427,7 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
return Builtins::AND(X, Word(0));
else
return Builtins::SHL(Word(sum), X);
},
false
}
});
// Combine two SHR by constant
@ -454,8 +440,7 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
return Builtins::AND(X, Word(0));
else
return Builtins::SHR(Word(sum), X);
},
false
}
});
// Combine SHL-SHR by constant
@ -472,7 +457,6 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
else
return Builtins::AND(X, mask);
},
false,
[=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; }
});
@ -490,7 +474,6 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
else
return Builtins::AND(X, mask);
},
false,
[=] { 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 ])
shiftOp(B, Builtins::AND(X, A)),
replacement,
false,
[=] { return B.d() < Pattern::WordSize; }
});
rules.push_back({
// SH[L/R](B, AND(A, X)) -> AND(SH[L/R](B, X), [ A << B / A >> B ])
shiftOp(B, Builtins::AND(A, X)),
replacement,
false,
[=] { return B.d() < Pattern::WordSize; }
});
}
@ -526,17 +507,14 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
Builtins::MUL(X, Builtins::SHL(Y, Word(1))),
[=]() -> Pattern {
return Builtins::SHL(Y, X);
},
// Actually only changes the order, does not remove.
true
}
});
rules.push_back({
// MUL(SHL(X, 1), Y) -> SHL(X, Y)
Builtins::MUL(Builtins::SHL(X, Word(1)), Y),
[=]() -> Pattern {
return Builtins::SHL(X, Y);
},
false
}
});
rules.push_back({
@ -544,9 +522,7 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
Builtins::DIV(X, Builtins::SHL(Y, Word(1))),
[=]() -> Pattern {
return Builtins::SHR(Y, X);
},
// Actually only changes the order, does not remove.
true
}
});
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)
Builtins::AND(A, Builtins::SHR(B, X)),
[=]() -> Pattern { return Builtins::SHR(B, X); },
false,
feasibilityFunction
});
@ -568,28 +543,24 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
// AND(SHR(B, X), A) -> ((2^256-1) >> B) & A == ((2^256-1) >> B)
Builtins::AND(Builtins::SHR(B, X), A),
[=]() -> Pattern { return Builtins::SHR(B, X); },
false,
feasibilityFunction
});
rules.push_back({
Builtins::BYTE(A, Builtins::SHL(B, X)),
[=]() -> Pattern { return Builtins::BYTE(A.d() + B.d() / 8, X); },
false,
[=] { return B.d() % 8 == 0 && A.d() <= 32 && B.d() <= 256; }
});
rules.push_back({
Builtins::BYTE(A, Builtins::SHR(B, X)),
[=]() -> Pattern { return Word(0); },
true,
[=] { return A.d() < B.d() / 8; }
});
rules.push_back({
Builtins::BYTE(A, Builtins::SHR(B, 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;
}
@ -615,76 +586,28 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart8(
{
// X - A -> X + (-A)
Builtins::SUB(X, A),
[=]() -> Pattern { return Builtins::ADD(X, 0 - A.d()); },
false
[=]() -> Pattern { return Builtins::ADD(X, 0 - A.d()); }
}, {
// (X + A) - Y -> (X - Y) + A
Builtins::SUB(Builtins::ADD(X, A), Y),
[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); },
false
[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); }
}, {
// (A + X) - Y -> (X - Y) + A
Builtins::SUB(Builtins::ADD(A, X), Y),
[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); },
false
[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); }
}, {
// X - (Y + A) -> (X - Y) + (-A)
Builtins::SUB(X, Builtins::ADD(Y, A)),
[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); },
false
[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); }
}, {
// X - (A + Y) -> (X - Y) + (-A)
Builtins::SUB(X, Builtins::ADD(A, Y)),
[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); },
false
[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); }
}
};
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>
std::vector<SimplificationRule<Pattern>> evmRuleList(
langutil::EVMVersion _evmVersion,
@ -703,7 +626,7 @@ std::vector<SimplificationRule<Pattern>> evmRuleList(
if (_evmVersion.hasSelfBalance())
rules.push_back({
Builtins::BALANCE(Instruction::ADDRESS),
[]() -> Pattern { return Instruction::SELFBALANCE; }, false
[]() -> Pattern { return Instruction::SELFBALANCE; }
});
return rules;
@ -743,7 +666,6 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleList(
rules += simplificationRuleListPart6(A, B, C, W, X);
rules += simplificationRuleListPart7(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())
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
* after the pattern has matched and a bool that indicates
* whether the action would remove something from the expression
* than is not a constant literal.
* after the pattern has matched and optional condition to check if the
* action should be applied.
*/
template <class Pattern>
struct SimplificationRule
@ -40,18 +39,15 @@ struct SimplificationRule
SimplificationRule(
Pattern _pattern,
std::function<Pattern()> _action,
bool _removesNonConstants,
std::function<bool()> _feasible = {}
):
pattern(std::move(_pattern)),
action(std::move(_action)),
removesNonConstants(_removesNonConstants),
feasible(std::move(_feasible))
{}
Pattern pattern;
std::function<Pattern()> action;
bool removesNonConstants;
std::function<bool()> feasible;
};

View File

@ -50,6 +50,7 @@ void CHCSmtLib2Interface::reset()
{
m_accumulatedOutput.clear();
m_variables.clear();
m_unhandledQueries.clear();
}
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]);
else if (n == "bvxor")
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")
{
size_t size = std::stoul(_expr.arguments[1].name);
@ -289,6 +295,8 @@ CVC4::Type CVC4Interface::cvc4Sort(Sort const& _sort)
return m_context.booleanType();
case Kind::Int:
return m_context.integerType();
case Kind::BitVector:
return m_context.mkBitVectorType(dynamic_cast<BitVectorSort const&>(_sort).size);
case Kind::Function:
{
FunctionSort const& fSort = dynamic_cast<FunctionSort const&>(_sort);

View File

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

View File

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

View File

@ -33,12 +33,12 @@ using namespace solidity::frontend;
using namespace solidity::smtutil;
SMTPortfolio::SMTPortfolio(
map<h256, string> const& _smtlib2Responses,
frontend::ReadCallback::Callback const& _smtCallback,
map<h256, string> _smtlib2Responses,
frontend::ReadCallback::Callback _smtCallback,
[[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
if (_enabledSolvers.z3)
m_solvers.emplace_back(make_unique<Z3Interface>());

View File

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

View File

@ -99,6 +99,9 @@ public:
{"bvand", 2},
{"bvor", 2},
{"bvxor", 2},
{"bvshl", 2},
{"bvlshr", 2},
{"bvashr", 2},
{"int2bv", 2},
{"bv2int", 1},
{"select", 2},
@ -299,15 +302,30 @@ public:
auto bvSort = _a.sort;
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)
{
auto bvSort = _a.sort;
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;
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
{

View File

@ -35,4 +35,6 @@ shared_ptr<IntSort> SortProvider::intSort(bool _signed)
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 sintSort;
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];
else if (n == "bvxor")
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")
{
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();
case Kind::Int:
return m_context.int_sort();
case Kind::BitVector:
return m_context.bv_sort(dynamic_cast<BitVectorSort const&>(_sort).size);
case Kind::Array:
{
auto const& arraySort = dynamic_cast<ArraySort const&>(_sort);

View File

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

View File

@ -519,14 +519,13 @@ bool DeclarationRegistrationHelper::visit(SourceUnit& _sourceUnit)
if (!m_scopes[&_sourceUnit])
// By importing, it is possible that the container already exists.
m_scopes[&_sourceUnit] = make_shared<DeclarationContainer>(m_currentScope, m_scopes[m_currentScope].get());
m_currentScope = &_sourceUnit;
return true;
return ASTVisitor::visit(_sourceUnit);
}
void DeclarationRegistrationHelper::endVisit(SourceUnit& _sourceUnit)
{
_sourceUnit.annotation().exportedSymbols = m_scopes[&_sourceUnit]->declarations();
closeCurrentScope();
ASTVisitor::endVisit(_sourceUnit);
}
bool DeclarationRegistrationHelper::visit(ImportDirective& _import)
@ -536,8 +535,7 @@ bool DeclarationRegistrationHelper::visit(ImportDirective& _import)
if (!m_scopes[importee])
m_scopes[importee] = make_shared<DeclarationContainer>(nullptr, m_scopes[nullptr].get());
m_scopes[&_import] = m_scopes[importee];
registerDeclaration(_import, false);
return true;
return ASTVisitor::visit(_import);
}
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_currentContract = &_contract;
registerDeclaration(_contract, true);
_contract.annotation().canonicalName = currentCanonicalName();
return true;
return ASTVisitor::visit(_contract);
}
void DeclarationRegistrationHelper::endVisit(ContractDefinition&)
void DeclarationRegistrationHelper::endVisit(ContractDefinition& _contract)
{
// make "this" and "super" invisible.
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), nullptr, true, true);
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), nullptr, true, true);
m_globalContext.resetCurrentContract();
m_currentContract = nullptr;
closeCurrentScope();
}
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();
ASTVisitor::endVisit(_contract);
}
void DeclarationRegistrationHelper::endVisit(VariableDeclarationStatement& _variableDeclarationStatement)
@ -673,32 +566,42 @@ void DeclarationRegistrationHelper::endVisit(VariableDeclarationStatement& _vari
for (ASTPointer<VariableDeclaration> const& var: _variableDeclarationStatement.declarations())
if (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;
}
bool DeclarationRegistrationHelper::visit(EventDefinition& _event)
void DeclarationRegistrationHelper::endVisitNode(ASTNode& _node)
{
registerDeclaration(_event, true);
return true;
}
void DeclarationRegistrationHelper::endVisit(EventDefinition&)
{
closeCurrentScope();
if (dynamic_cast<ScopeOpener const*>(&_node))
closeCurrentScope();
if (dynamic_cast<VariableScope*>(&_node))
m_currentFunction = nullptr;
}
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())};
tie(iter, newlyAdded) = m_scopes.emplace(&_subScope, move(container));
solAssert(newlyAdded, "Unable to add new scope.");
bool newlyAdded = m_scopes.emplace(&_subScope, move(container)).second;
// 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;
}
@ -708,7 +611,7 @@ void DeclarationRegistrationHelper::closeCurrentScope()
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.");
@ -729,10 +632,8 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio
registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, inactive, m_errorReporter);
_declaration.annotation().scope = m_currentScope;
_declaration.annotation().contract = m_currentContract;
if (_opensScope)
enterNewSubScope(_declaration);
solAssert(_declaration.annotation().scope == m_currentScope, "");
solAssert(_declaration.annotation().contract == m_currentContract, "");
}
string DeclarationRegistrationHelper::currentCanonicalName() const

View File

@ -163,31 +163,15 @@ private:
bool visit(ImportDirective& _import) override;
bool visit(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;
bool visit(VariableDeclaration& _declaration) override;
bool visit(EventDefinition& _event) override;
void endVisit(EventDefinition& _event) override;
bool visitNode(ASTNode& _node) override;
void endVisitNode(ASTNode& _node) override;
void enterNewSubScope(ASTNode& _subScope);
void closeCurrentScope();
void registerDeclaration(Declaration& _declaration, bool _opensScope);
void registerDeclaration(Declaration& _declaration);
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.
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;
}

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

View File

@ -152,10 +152,19 @@ std::vector<T const*> ASTNode::filteredNodes(std::vector<ASTPointer<ASTNode>> co
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.
*/
class SourceUnit: public ASTNode
class SourceUnit: public ASTNode, public ScopeOpener
{
public:
SourceUnit(
@ -455,7 +464,7 @@ protected:
* document order. It first visits all struct declarations, then all variable declarations and
* finally all function declarations.
*/
class ContractDefinition: public Declaration, public StructurallyDocumented
class ContractDefinition: public Declaration, public StructurallyDocumented, public ScopeOpener
{
public:
ContractDefinition(
@ -593,7 +602,7 @@ private:
ASTPointer<TypeName> m_typeName;
};
class StructDefinition: public Declaration
class StructDefinition: public Declaration, public ScopeOpener
{
public:
StructDefinition(
@ -620,7 +629,7 @@ private:
std::vector<ASTPointer<VariableDeclaration>> m_members;
};
class EnumDefinition: public Declaration
class EnumDefinition: public Declaration, public ScopeOpener
{
public:
EnumDefinition(
@ -765,7 +774,7 @@ protected:
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:
FunctionDefinition(
@ -989,7 +998,7 @@ private:
/**
* Definition of a function modifier.
*/
class ModifierDefinition: public CallableDeclaration, public StructurallyDocumented, public ImplementationOptional
class ModifierDefinition: public CallableDeclaration, public StructurallyDocumented, public ImplementationOptional, public ScopeOpener
{
public:
ModifierDefinition(
@ -1061,7 +1070,7 @@ private:
/**
* Definition of a (loggable) event.
*/
class EventDefinition: public CallableDeclaration, public StructurallyDocumented
class EventDefinition: public CallableDeclaration, public StructurallyDocumented, public ScopeOpener
{
public:
EventDefinition(
@ -1199,7 +1208,7 @@ private:
/**
* 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:
FunctionTypeName(
@ -1334,7 +1343,7 @@ private:
/**
* Brace-enclosed block containing zero or more statements.
*/
class Block: public Statement, public Scopable
class Block: public Statement, public Scopable, public ScopeOpener
{
public:
Block(
@ -1411,7 +1420,7 @@ private:
* 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:
TryCatchClause(
@ -1526,7 +1535,7 @@ private:
/**
* For loop statement
*/
class ForStatement: public BreakableStatement, public Scopable
class ForStatement: public BreakableStatement, public Scopable, public ScopeOpener
{
public:
ForStatement(

View File

@ -108,10 +108,10 @@ struct ScopableAnnotation
virtual ~ScopableAnnotation() = default;
/// 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;
/// 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;
};

View File

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

View File

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

View File

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

View File

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

View File

@ -43,19 +43,19 @@ using namespace solidity::frontend;
CHC::CHC(
smt::EncodingContext& _context,
ErrorReporter& _errorReporter,
map<util::h256, string> const& _smtlib2Responses,
ReadCallback::Callback const& _smtCallback,
[[maybe_unused]] smtutil::SMTSolverChoice _enabledSolvers
[[maybe_unused]] map<util::h256, string> const& _smtlib2Responses,
[[maybe_unused]] ReadCallback::Callback const& _smtCallback,
smtutil::SMTSolverChoice _enabledSolvers
):
SMTEncoder(_context),
m_outerErrorReporter(_errorReporter),
m_enabledSolvers(_enabledSolvers)
{
#ifdef HAVE_Z3
if (_enabledSolvers.z3)
m_interface = make_unique<smtutil::Z3CHCInterface>();
bool usesZ3 = _enabledSolvers.z3;
#ifndef HAVE_Z3
usesZ3 = false;
#endif
if (!m_interface)
if (!usesZ3)
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), "");
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();
m_genesisPredicate = createSymbolicBlock(arity0FunctionSort(), "genesis");
addRule(genesis(), "genesis");
set<SourceUnit const*, IdCompare> sources;
sources.insert(&_source);
for (auto const& source: _source.referencedSourceUnits(true))
@ -131,7 +109,7 @@ bool CHC::visit(ContractDefinition const& _contract)
void CHC::endVisit(ContractDefinition const& _contract)
{
auto implicitConstructor = (*m_implicitConstructorPredicate)({});
connectBlocks(genesis(), implicitConstructor);
addRule(implicitConstructor, implicitConstructor.name);
m_currentBlock = implicitConstructor;
m_context.addAssertion(m_error.currentValue() == 0);
@ -156,7 +134,7 @@ bool CHC::visit(FunctionDefinition const& _function)
{
if (!_function.isImplemented())
{
connectBlocks(genesis(), summary(_function));
addRule(summary(_function), "summary_function_" + to_string(_function.id()));
return false;
}
@ -184,7 +162,7 @@ bool CHC::visit(FunctionDefinition const& _function)
if (_function.isConstructor())
connectBlocks(m_currentBlock, functionPred);
else
connectBlocks(genesis(), functionPred);
addRule(functionPred, functionPred.name);
m_context.addAssertion(m_error.currentValue() == 0);
for (auto const* var: m_stateVariables)
@ -676,6 +654,30 @@ void CHC::resetSourceAnalysis()
m_interfaces.clear();
m_nondetInterfaces.clear();
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()
@ -861,27 +863,23 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source)
{
for (auto const& node: _source.nodes())
if (auto const* contract = dynamic_cast<ContractDefinition const*>(node.get()))
{
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))
createVariable(*var);
/// Base nondeterministic interface that allows
/// 0 steps to be taken, used as base for the inductive
/// rule for each function.
auto const& iface = *m_nondetInterfaces.at(contract);
auto state0 = stateVariablesAtIndex(0, *contract);
addRule(iface(state0 + state0), "base_nondet");
for (auto const* base: contract->annotation().linearizedBaseContracts)
{
for (auto const* var: SMTEncoder::stateVariablesIncludingInheritedAndPrivate(*base))
if (!m_context.knownVariable(*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
/// 0 steps to be taken, used as base for the inductive
/// rule for each function.
auto const* iface = m_nondetInterfaces.at(base);
auto state0 = stateVariablesAtIndex(0, *base);
addRule((*iface)(state0 + state0), "base_nondet");
}
for (auto const* function: base->definedFunctions())
{
for (auto var: function->parameters())
@ -900,13 +898,11 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source)
!base->isInterface()
)
{
auto state1 = stateVariablesAtIndex(1, *base);
auto state2 = stateVariablesAtIndex(2, *base);
auto state1 = stateVariablesAtIndex(1, *contract);
auto state2 = stateVariablesAtIndex(2, *contract);
auto const* iface = m_nondetInterfaces.at(base);
auto state0 = stateVariablesAtIndex(0, *base);
auto nondetPre = (*iface)(state0 + state1);
auto nondetPost = (*iface)(state0 + state2);
auto nondetPre = iface(state0 + state1);
auto nondetPost = iface(state0 + state2);
vector<smtutil::Expression> args{m_error.currentValue()};
args += state1 +
@ -915,10 +911,10 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source)
applyMap(function->parameters(), [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));
}
}
}
}
}
smtutil::Expression CHC::interface()
@ -1210,6 +1206,16 @@ void CHC::addVerificationTarget(
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});
}
@ -1251,7 +1257,7 @@ void CHC::checkVerificationTargets()
if (target.type == VerificationTarget::Type::PopEmptyArray)
{
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.";
errorReporterId = 2529_error;
}
@ -1267,8 +1273,8 @@ void CHC::checkVerificationTargets()
if (!intType)
intType = TypeProvider::uint256();
satMsgUnderflow = "Underflow (resulting value less than " + formatNumberReadable(intType->minValue()) + ") happens here";
satMsgOverflow = "Overflow (resulting value larger than " + formatNumberReadable(intType->maxValue()) + ") happens here";
satMsgUnderflow = "Underflow (resulting value less than " + formatNumberReadable(intType->minValue()) + ") happens here.";
satMsgOverflow = "Overflow (resulting value larger than " + formatNumberReadable(intType->maxValue()) + ") happens here.";
if (target.type == VerificationTarget::Type::Underflow)
{
satMsg = satMsgUnderflow;
@ -1314,7 +1320,7 @@ void CHC::checkAssertTarget(ASTNode const* _scope, CHCVerificationTarget const&
solAssert(it != m_errorIds.end(), "");
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,
_scope->location(),
_satMsg,
SecondarySourceLocation().append(" for:\n" + *cex, SourceLocation{})
SecondarySourceLocation().append("\nCounterexample:\n" + *cex, SourceLocation{})
);
else
m_outerErrorReporter.warning(
_errorReporterId,
_scope->location(),
_satMsg + "."
_satMsg
);
}
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
/// or stop.
if (interfaceId)
{
Predicate const* interfacePredicate = Predicate::predicate(_graph.nodes.at(*interfaceId).first);
solAssert(interfacePredicate && interfacePredicate->isInterface(), "");
node = *interfaceId;
}
else
break;
}

View File

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

View File

@ -133,10 +133,7 @@ bool SMTEncoder::visit(FunctionDefinition const& _function)
if (_function.isConstructor())
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);
if (_function.returnParameterList())
@ -571,7 +568,7 @@ void SMTEncoder::endVisit(BinaryOperation const& _op)
arithmeticOperation(_op);
else if (TokenTraits::isCompareOp(_op.getOperator()))
compareOperation(_op);
else if (TokenTraits::isBitOp(_op.getOperator()))
else if (TokenTraits::isBitOp(_op.getOperator()) || TokenTraits::isShiftOp(_op.getOperator()))
bitwiseOperation(_op);
else
m_errorReporter.warning(
@ -881,16 +878,31 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess)
auto identifier = dynamic_cast<Identifier const*>(&_memberAccess.expression());
if (exprType->category() == Type::Category::Magic)
{
string accessedName;
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
m_errorReporter.warning(
9551_error,
_memberAccess.location(),
"Assertion checker does not yet support this expression."
);
defineGlobalVariable(accessedName + "." + _memberAccess.memberName(), _memberAccess);
return false;
}
else if (smt::isNonRecursiveStruct(*exprType))
@ -1422,7 +1434,8 @@ void SMTEncoder::booleanOperation(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;
solAssert(commonType, "");
@ -1432,16 +1445,33 @@ void SMTEncoder::bitwiseOperation(BinaryOperation const& _op)
auto bvRight = smtutil::Expression::int2bv(expr(_op.rightExpression(), commonType), bvSize);
optional<smtutil::Expression> result;
if (_op.getOperator() == Token::BitAnd)
switch (op)
{
case Token::BitAnd:
result = bvLeft & bvRight;
else if (_op.getOperator() == Token::BitOr)
break;
case Token::BitOr:
result = bvLeft | bvRight;
else if (_op.getOperator() == Token::BitXor)
break;
case Token::BitXor:
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, "");
if (result)
defineExpr(_op, smtutil::Expression::bv2int(*result, isSigned));
defineExpr(_op, smtutil::Expression::bv2int(*result, isSigned));
}
void SMTEncoder::bitwiseNotOperation(UnaryOperation const& _op)
@ -2022,6 +2052,14 @@ vector<VariableDeclaration const*> SMTEncoder::stateVariablesIncludingInheritedA
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)
{
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(FunctionDefinition const& _function);
/// @returns the SourceUnit that contains _scopable.
static SourceUnit const* sourceUnitContaining(Scopable const& _scopable);
protected:
// TODO: Check that we do not have concurrent reads and writes to a variable,
// because the order of expression evaluation is undefined

View File

@ -36,6 +36,7 @@
#include <libsolidity/analysis/PostTypeChecker.h>
#include <libsolidity/analysis/StaticAnalyzer.h>
#include <libsolidity/analysis/SyntaxChecker.h>
#include <libsolidity/analysis/Scoper.h>
#include <libsolidity/analysis/TypeChecker.h>
#include <libsolidity/analysis/ViewPureChecker.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."));
resolveImports();
for (Source const* source: m_sourceOrder)
if (source->ast)
Scoper::assignScopes(*source->ast);
bool noErrors = true;
try

View File

@ -32,7 +32,7 @@ namespace solidity::frontend
struct OptimiserSettings
{
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
"cCTUtTOntnfDIul" // Perform structural simplification
@ -47,7 +47,7 @@ struct OptimiserSettings
"gvif" // Run full inliner
"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.
static OptimiserSettings none()

View File

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

View File

@ -22,12 +22,9 @@
#include <libyul/optimiser/ExpressionSimplifier.h>
#include <libyul/optimiser/SimplificationRules.h>
#include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/AsmData.h>
#include <libsolutil/CommonData.h>
using namespace std;
using namespace solidity;
using namespace solidity::yul;
@ -40,17 +37,7 @@ void ExpressionSimplifier::run(OptimiserStepContext& _context, Block& _ast)
void ExpressionSimplifier::visit(Expression& _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())
return;
while (auto const* match = SimplificationRules::findFirstMatch(_expression, m_dialect, m_value))
_expression = match->action().toExpression(locationOf(_expression));
}
}

View File

@ -33,7 +33,8 @@ struct OptimiserStepContext;
/**
* Applies simplification rules to all expressions.
* 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
* and takes them into account for replacements.

View File

@ -28,6 +28,7 @@
#include <libyul/optimiser/Metrics.h>
#include <libyul/optimiser/SSAValueTracker.h>
#include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/CallGraphGenerator.h>
#include <libyul/Exceptions.h>
#include <libyul/AsmData.h>
#include <libyul/Dialect.h>
@ -41,7 +42,9 @@ using namespace solidity::yul;
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):
@ -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)
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.
m_pass = _pass;
// 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
@ -87,14 +85,76 @@ void FullInliner::run()
// should have as little an impact as possible. This is the case
// if we handle inlining in source (and thus, for the IR generator,
// 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)
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))
continue;
FunctionDefinition& fun = std::get<FunctionDefinition>(statement);
handleBlock(fun.name, fun.body);
updateCodeSize(fun);
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)
@ -115,6 +175,10 @@ bool FullInliner::shallInline(FunctionCall const& _funCall, YulString _callSite)
if (size <= 1)
return true;
// In the first pass, only inline tiny functions.
if (m_pass == Pass::InlineTiny)
return false;
// Do not inline into already big functions.
if (m_functionSizes.at(_callSite) > 45)
return false;

View File

@ -91,13 +91,20 @@ public:
void tentativelyUpdateCodeSize(YulString _function, YulString _callSite);
private:
enum Pass { InlineTiny, InlineRest };
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 handleBlock(YulString _currentFunctionName, Block& _block);
bool recursive(FunctionDefinition const& _fun) const;
Pass m_pass;
/// The AST to be modified. The root block itself will not be modified, because
/// we store pointers to functions.
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;
assertThrow(m_arguments.size() == instrAndArgs->second->size(), OptimizerException, "");
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;
}
}
else
{
assertThrow(m_arguments.empty(), OptimizerException, "\"Any\" should not have arguments.");
assertThrow(!holds_alternative<FunctionCall>(*expr), OptimizerException, "\"Any\" at top-level.");
}
if (m_matchGroup)
@ -197,9 +207,14 @@ bool Pattern::matches(
assertThrow(m_kind == PatternKind::Any, OptimizerException, "Match group repetition for non-any.");
Expression const* firstMatch = (*m_matchGroups)[m_matchGroup];
assertThrow(firstMatch, OptimizerException, "Match set but to null.");
return
SyntacticallyEqual{}(*firstMatch, _expr) &&
SideEffectsCollector(_dialect, _expr).movable();
assertThrow(
!holds_alternative<FunctionCall>(_expr) &&
!holds_alternative<FunctionCall>(*firstMatch),
OptimizerException,
"Group matches have to be literals or variables."
);
return SyntacticallyEqual{}(*firstMatch, _expr);
}
else if (m_kind == PatternKind::Any)
(*m_matchGroups)[m_matchGroup] = &_expr;

View File

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

View File

@ -35,5 +35,5 @@ else
fi
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

View File

@ -29,12 +29,12 @@
# make version=1.39.15 build
#
FROM emscripten/emsdk:1.39.15 AS base
LABEL version="1"
LABEL version="2"
ADD emscripten.jam /usr/src
RUN set -ex; \
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; \
mkdir build; \
cd build; \

View File

@ -22,7 +22,7 @@
# (c) 2016-2019 solidity contributors.
#------------------------------------------------------------------------------
FROM gcr.io/oss-fuzz-base/base-clang as base
LABEL version="3"
LABEL version="4"
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
# 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; \
cd /usr/src/z3; \
mkdir build; \

View File

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

View File

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

View File

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

View File

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

View File

@ -17,6 +17,7 @@
// SPDX-License-Identifier: GPL-3.0
#include <stdexcept>
#include <iostream>
#include <test/Common.h>
#include <libsolutil/Assertions.h>
@ -57,9 +58,9 @@ boost::filesystem::path testPath()
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;
auto const searchPath =
@ -76,7 +77,7 @@ std::string EVMOneEnvOrDefaultPath()
};
for (auto const& basePath: searchPath)
{
fs::path p = basePath / evmoneFilename;
fs::path p = basePath / lib_name;
if (fs::exists(p))
return p.string();
}
@ -92,7 +93,8 @@ CommonOptions::CommonOptions(std::string _caption):
options.add_options()
("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")
("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")
("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.")
@ -141,6 +143,33 @@ bool CommonOptions::parse(int argc, char const* const* argv)
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;
}

View File

@ -21,6 +21,8 @@
#include <libsolutil/Exceptions.h>
#include <liblangutil/EVMVersion.h>
#include <test/evmc/evmc.h>
#include <boost/filesystem/path.hpp>
#include <boost/noncopyable.hpp>
#include <boost/program_options.hpp>
@ -31,21 +33,27 @@ namespace solidity::test
#ifdef _WIN32
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 heraFilename = "hera.dll";
static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/archive/v0.3.0.tar.gz";
#elif defined(__APPLE__)
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 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
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 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
struct ConfigException : public util::Exception {};
struct CommonOptions: boost::noncopyable
{
boost::filesystem::path evmonePath;
std::vector<boost::filesystem::path> vmPaths;
boost::filesystem::path testPath;
bool ewasm = false;
bool optimize = false;
bool enforceViaYul = false;
bool disableSMT = false;
@ -64,8 +72,8 @@ struct CommonOptions: boost::noncopyable
CommonOptions(std::string caption = "");
virtual ~CommonOptions() {};
protected:
protected:
boost::program_options::options_description options;
private:

View File

@ -39,17 +39,18 @@ using namespace evmc::literals;
evmc::VM& EVMHost::getVM(string const& _path)
{
static evmc::VM theVM;
if (!theVM && !_path.empty())
static evmc::VM NullVM{nullptr};
static map<string, unique_ptr<evmc::VM>> vms;
if (vms.count(_path) == 0)
{
evmc_loader_error_code errorCode = {};
auto vm = evmc::VM{evmc_load_and_configure(_path.c_str(), &errorCode)};
if (vm && errorCode == EVMC_LOADER_SUCCESS)
{
if (vm.get_capabilities() & EVMC_CAPABILITY_EVM1)
theVM = std::move(vm);
if (vm.get_capabilities() & (EVMC_CAPABILITY_EVM1 | EVMC_CAPABILITY_EWASM))
vms[_path] = make_unique<evmc::VM>(evmc::VM(move(vm)));
else
cerr << "VM loaded does not support EVM1" << endl;
cerr << "VM loaded neither supports EVM1 nor EWASM" << endl;
}
else
{
@ -59,7 +60,38 @@ evmc::VM& EVMHost::getVM(string const& _path)
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):

View File

@ -30,6 +30,8 @@
#include <libsolutil/FixedHash.h>
#include <boost/filesystem.hpp>
namespace solidity::test
{
using Address = util::h160;
@ -40,12 +42,17 @@ public:
using MockedHost::get_code_size;
using MockedHost::get_balance;
/// Tries to dynamically load libevmone. @returns nullptr on failure.
/// The path has to be provided for the first successful run and will be ignored
/// afterwards.
/// Tries to dynamically load an evmc vm supporting evm1 or ewasm and caches the loaded VM.
/// @returns vmc::VM(nullptr) on failure.
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 newBlock()
@ -71,6 +78,12 @@ public:
static util::h256 convertFromEVMC(evmc::bytes32 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:
evmc::address m_currentAddress = {};

View File

@ -29,6 +29,8 @@
#include <libsolutil/CommonIO.h>
#include <liblangutil/Exceptions.h>
#include <boost/test/framework.hpp>
#include <boost/algorithm/string/replace.hpp>
@ -40,27 +42,47 @@ using namespace solidity::util;
using namespace solidity::test;
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_optimiserSettings(solidity::frontend::OptimiserSettings::minimal()),
m_showMessages(solidity::test::CommonOptions::get().showMessages),
m_evmHost(make_shared<EVMHost>(m_evmVersion))
m_vmPaths(_vmPaths)
{
if (solidity::test::CommonOptions::get().optimize)
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();
}
void ExecutionFramework::reset()
{
m_evmHost->reset();
m_evmcHost->reset();
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);
}
@ -92,7 +114,7 @@ std::pair<bool, string> ExecutionFramework::compareAndCreateMessage(
u256 ExecutionFramework::gasLimit() const
{
return {m_evmHost->tx_context.block_gas_limit};
return {m_evmcHost->tx_context.block_gas_limit};
}
u256 ExecutionFramework::gasPrice() const
@ -100,24 +122,24 @@ u256 ExecutionFramework::gasPrice() const
// here and below we use "return u256{....}" instead of just "return {....}"
// to please MSVC and avoid unexpected
// 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
{
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
{
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)
{
m_evmHost->newBlock();
m_evmcHost->newBlock();
if (m_showMessages)
{
@ -147,7 +169,7 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256
}
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);
if (_isCreation)
@ -166,7 +188,7 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256
void ExecutionFramework::sendEther(Address const& _addr, u256 const& _amount)
{
m_evmHost->newBlock();
m_evmcHost->newBlock();
if (m_showMessages)
{
@ -181,12 +203,12 @@ void ExecutionFramework::sendEther(Address const& _addr, u256 const& _amount)
message.destination = EVMHost::convertToEVMC(_addr);
message.gas = m_gas.convert_to<int64_t>();
m_evmHost->call(message);
m_evmcHost->call(message);
}
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)
@ -204,32 +226,32 @@ Address ExecutionFramework::account(size_t _idx)
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
{
return m_evmHost->recorded_logs.size();
return m_evmcHost->recorded_logs.size();
}
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
{
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
{
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
{
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(),
// but reference type like string_view would be preferable.
return {data.begin(), data.end()};
@ -237,13 +259,13 @@ bytes ExecutionFramework::logData(size_t _logIdx) const
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)
{
const auto it = m_evmHost->accounts.find(EVMHost::convertToEVMC(_addr));
if (it != m_evmHost->accounts.end())
const auto it = m_evmcHost->accounts.find(EVMHost::convertToEVMC(_addr));
if (it != m_evmcHost->accounts.end())
{
for (auto const& entry: it->second.storage)
if (!(entry.second.value == evmc::bytes32{}))

View File

@ -24,6 +24,7 @@
#pragma once
#include <test/Common.h>
#include <test/EVMHost.h>
#include <libsolidity/interface/OptimiserSettings.h>
#include <libsolidity/interface/DebugSettings.h>
@ -39,8 +40,6 @@
namespace solidity::test
{
class EVMHost;
using rational = boost::rational<bigint>;
/// An Ethereum address: 20 bytes.
/// @NOTE This is not endian-specific; it's just a bunch of bytes.
@ -55,7 +54,7 @@ class ExecutionFramework
public:
ExecutionFramework();
explicit ExecutionFramework(langutil::EVMVersion _evmVersion);
ExecutionFramework(langutil::EVMVersion _evmVersion, std::vector<boost::filesystem::path> const& _vmPaths);
virtual ~ExecutionFramework() = default;
virtual bytes const& compileAndRunWithoutCheck(
@ -255,6 +254,7 @@ private:
}
protected:
void selectVM(evmc_capabilities _cap = evmc_capabilities::EVMC_CAPABILITY_EVM1);
void reset();
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::OptimiserSettings m_optimiserSettings = solidity::frontend::OptimiserSettings::minimal();
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;
Address m_sender = account(0);

View File

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

View File

@ -39,7 +39,6 @@
#include <test/InteractiveTests.h>
#include <test/Common.h>
#include <test/EVMHost.h>
#include <test/Common.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/predicate.hpp>
@ -72,7 +71,7 @@ int registerTests(
{
int numTestsAdded = 0;
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))
{
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();
bool disableSemantics = !solidity::test::EVMHost::getVM(solidity::test::CommonOptions::get().evmonePath.string());
if (disableSemantics)
bool disableSemantics = true;
try
{
cout << "Unable to find " << solidity::test::evmoneFilename << ". Please provide the path using -- --evmonepath <path>." << endl;
cout << "You can download it at" << endl;
cout << solidity::test::evmoneDownloadLink << endl;
cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl;
disableSemantics = !solidity::test::EVMHost::checkVmPaths(solidity::test::CommonOptions::get().vmPaths);
}
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
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 { }
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)
vloc_sum := checked_add_t_uint256(vloc_sum, extract_from_storage_value_dynamict_uint256(sload(_3), _4))
mstore(_1, _1)
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)
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)
}
function abi_encode_tuple_t_uint256__to_t_uint256__fromStack(headStart, value0) -> tail
function abi_encode_uint(headStart, value0) -> tail
{
tail := add(headStart, 32)
mstore(headStart, value0)
@ -57,27 +60,6 @@ object "Arraysum_33" {
if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) }
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)))
(memory $memory (export \"memory\") 1)
(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
(local $_1 i64)
@ -18,7 +15,6 @@
(local $z1 i64)
(local $z2 i64)
(local $z3 i64)
(local $z4 i64)
(local $_3 i64)
(block $label_
(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 24)) (call $endian_swap (i64.const 128)))
(call $eth.getCallValue (i32.const 0))
(block
(local.set $z1 (call $mload_internal (i32.const 0)))
(local.set $z2 (global.get $global_))
(local.set $z3 (global.get $global__1))
(local.set $z4 (global.get $global__2))
)
(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 $z1 (call $endian_swap (i64.load (i32.const 0))))
(local.set $z2 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 8)))))
(local.set $z3 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 16)))))
(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
(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))))
(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 $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 $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 $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)
(result 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
(unreachable)))
(if (i64.ne (i64.const 0) (i64.shr_u (local.get $x4) (i64.const 32))) (then
@ -73,7 +65,7 @@
(result i32)
(local $r 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 $r (i32.add (local.get $p) (i32.const 64)))
(if (i32.lt_u (local.get $r) (local.get $p)) (then
@ -83,11 +75,29 @@
(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
(param $x i64)
(result 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))))
)
@ -99,7 +109,7 @@
(result i64)
(local $y 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 $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)
(local $y 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 $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)
)
(func $mload_internal
(param $pos i32)
(result i64)
(local $z1 i64)
(local $z2 i64)
(local $z3 i64)
(local $z4 i64)
(block $label__8
(local.set $z1 (call $endian_swap (i64.load (local.get $pos))))
(local.set $z2 (call $endian_swap (i64.load (i32.add (local.get $pos) (i32.const 8)))))
(local.set $z3 (call $endian_swap (i64.load (i32.add (local.get $pos) (i32.const 16)))))
(local.set $z4 (call $endian_swap (i64.load (i32.add (local.get $pos) (i32.const 24)))))
(func $return
(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__7
(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)))
)
)
(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:
object "object" {
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
sstore(_1, _1)
@ -25,93 +19,29 @@ object "object" {
sstore(11, _1)
sstore(12, _1)
sstore(13, _1)
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
sstore(_1, _1)
sstore(2, _1)
sstore(3, _1)
sstore(4, _1)
sstore(5, _1)
sstore(6, _1)
sstore(7, _1)
sstore(8, _1)
sstore(9, _1)
sstore(10, _1)
sstore(11, _1)
sstore(12, _1)
sstore(13, _1)
sstore(_1, _1)
}
}
}
Binary representation:
60056030565b505050505050505050505050505060196030565b5050505050505050505050505050808255505060c3565b6000600060006000600060006000600060006000600060006000600060006001808155806002558060035580600455806005558060065580600755806008558060095580600a5580600b5580600c5580600d55809f50809e50809d50809c50809b50809a50809950809850809750809650809550809450809350809250809150505b909192939495969798999a9b9c9d9e565b
6001808155806002558060035580600455806005558060065580600755806008558060095580600a5580600b5580600c5580600d55808155806002558060035580600455806005558060065580600755806008558060095580600a5580600b5580600c5580600d5580815550
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 */
0x01
dup1
@ -192,96 +122,84 @@ tag_2:
sstore
/* "yul_stack_opt/input.sol":98:99 */
dup1
/* "yul_stack_opt/input.sol":423:430 */
swap16
pop
dup2
/* "yul_stack_opt/input.sol":129:141 */
sstore
/* "yul_stack_opt/input.sol":98:99 */
dup1
/* "yul_stack_opt/input.sol":433:440 */
swap15
pop
/* "yul_stack_opt/input.sol":151:160 */
0x02
/* "yul_stack_opt/input.sol":144:164 */
sstore
/* "yul_stack_opt/input.sol":98:99 */
dup1
/* "yul_stack_opt/input.sol":443:450 */
swap14
pop
/* "yul_stack_opt/input.sol":174:183 */
0x03
/* "yul_stack_opt/input.sol":167:187 */
sstore
/* "yul_stack_opt/input.sol":98:99 */
dup1
/* "yul_stack_opt/input.sol":453:460 */
swap13
pop
/* "yul_stack_opt/input.sol":197:206 */
0x04
/* "yul_stack_opt/input.sol":190:210 */
sstore
/* "yul_stack_opt/input.sol":98:99 */
dup1
/* "yul_stack_opt/input.sol":463:470 */
swap12
pop
/* "yul_stack_opt/input.sol":220:229 */
0x05
/* "yul_stack_opt/input.sol":213:233 */
sstore
/* "yul_stack_opt/input.sol":98:99 */
dup1
/* "yul_stack_opt/input.sol":473:480 */
swap11
pop
/* "yul_stack_opt/input.sol":243:252 */
0x06
/* "yul_stack_opt/input.sol":236:256 */
sstore
/* "yul_stack_opt/input.sol":98:99 */
dup1
/* "yul_stack_opt/input.sol":483:490 */
swap10
pop
/* "yul_stack_opt/input.sol":266:275 */
0x07
/* "yul_stack_opt/input.sol":259:279 */
sstore
/* "yul_stack_opt/input.sol":98:99 */
dup1
/* "yul_stack_opt/input.sol":493:500 */
swap9
pop
/* "yul_stack_opt/input.sol":289:298 */
0x08
/* "yul_stack_opt/input.sol":282:302 */
sstore
/* "yul_stack_opt/input.sol":98:99 */
dup1
/* "yul_stack_opt/input.sol":503:510 */
swap8
pop
/* "yul_stack_opt/input.sol":312:321 */
0x09
/* "yul_stack_opt/input.sol":305:325 */
sstore
/* "yul_stack_opt/input.sol":98:99 */
dup1
/* "yul_stack_opt/input.sol":513:520 */
swap7
pop
/* "yul_stack_opt/input.sol":335:344 */
0x0a
/* "yul_stack_opt/input.sol":328:348 */
sstore
/* "yul_stack_opt/input.sol":98:99 */
dup1
/* "yul_stack_opt/input.sol":523:530 */
swap6
pop
/* "yul_stack_opt/input.sol":358:368 */
0x0b
/* "yul_stack_opt/input.sol":351:372 */
sstore
/* "yul_stack_opt/input.sol":98:99 */
dup1
/* "yul_stack_opt/input.sol":533:540 */
swap5
pop
/* "yul_stack_opt/input.sol":382:392 */
0x0c
/* "yul_stack_opt/input.sol":375:396 */
sstore
/* "yul_stack_opt/input.sol":98:99 */
dup1
/* "yul_stack_opt/input.sol":543:550 */
swap4
pop
/* "yul_stack_opt/input.sol":406:416 */
0x0d
/* "yul_stack_opt/input.sol":399:420 */
sstore
/* "yul_stack_opt/input.sol":98:99 */
dup1
/* "yul_stack_opt/input.sol":553:560 */
swap3
dup2
/* "yul_stack_opt/input.sol":729:743 */
sstore
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);
// "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
registrar.reserve(name);
BOOST_CHECK_EQUAL(registrar.owner(name), m_sender);
@ -429,7 +429,7 @@ BOOST_AUTO_TEST_CASE(auction_bidding)
string name = "x";
unsigned startTime = 0x776347e2;
m_evmHost->tx_context.block_timestamp = startTime;
m_evmcHost->tx_context.block_timestamp = startTime;
RegistrarInterface registrar(*this);
// initiate auction
@ -437,19 +437,19 @@ BOOST_AUTO_TEST_CASE(auction_bidding)
registrar.reserve(name);
BOOST_CHECK_EQUAL(registrar.owner(name), 0);
// 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.reserve(name);
// another bid by someone else
sendEther(account(1), 10 * ether);
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.reserve(name);
BOOST_CHECK_EQUAL(registrar.owner(name), 0);
// end auction by first bidder (which is not highest) trying to overbid again (too late)
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.reserve(name);
BOOST_CHECK_EQUAL(registrar.owner(name), account(1));

View File

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

View File

@ -30,6 +30,7 @@
#include <libsolidity/parsing/Parser.h>
#include <libsolidity/analysis/DeclarationTypeChecker.h>
#include <libsolidity/analysis/NameAndTypeResolver.h>
#include <libsolidity/analysis/Scoper.h>
#include <libsolidity/codegen/Compiler.h>
#include <libsolidity/ast/AST.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_CHECK(!!sourceUnit);
Scoper::assignScopes(*sourceUnit);
GlobalContext globalContext;
NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter);
DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion());

View File

@ -39,8 +39,8 @@ using namespace boost::unit_test;
namespace fs = boost::filesystem;
SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVersion, bool enforceViaYul):
SolidityExecutionFramework(_evmVersion),
SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVersion, vector<boost::filesystem::path> const& _vmPaths, bool enforceViaYul):
SolidityExecutionFramework(_evmVersion, _vmPaths),
EVMVersionRestrictedTestCase(_filename),
m_sources(m_reader.sources()),
m_lineOffset(m_reader.lineNumber()),
@ -72,6 +72,21 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer
else
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);
if (m_runWithABIEncoderV1Only && solidity::test::CommonOptions::get().useABIEncoderV2)
m_shouldRun = false;
@ -88,156 +103,180 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer
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
bool success = true;
if (_compileViaYul && _compileToEwasm)
selectVM(evmc_capabilities::EVMC_CAPABILITY_EWASM);
else
selectVM(evmc_capabilities::EVMC_CAPABILITY_EVM1);
reset();
m_compileViaYul = _compileViaYul;
if (_compileToEwasm)
{
reset();
bool success = true;
soltestAssert(m_compileViaYul, "");
m_compileToEwasm = _compileToEwasm;
}
m_compileViaYul = compileViaYul;
m_compileViaYulCanBeSet = false;
m_compileViaYulCanBeSet = false;
if (compileViaYul)
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul:" << endl;
if (_compileViaYul)
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul:" << endl;
for (auto& test: m_tests)
test.reset();
for (auto& test: m_tests)
test.reset();
map<string, solidity::test::Address> libraries;
map<string, solidity::test::Address> libraries;
bool constructed = false;
bool constructed = false;
for (auto& test: m_tests)
for (auto& test: m_tests)
{
if (constructed)
{
if (constructed)
{
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.");
}
else if (test.call().isLibrary)
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.");
}
else if (test.call().isLibrary)
{
soltestAssert(
deploy(test.call().signature, 0, {}, libraries) && m_transactionSuccessful,
"Failed to deploy library " + test.call().signature);
libraries[test.call().signature] = m_contractAddress;
continue;
}
else
{
if (test.call().isConstructor)
deploy("", test.call().value.value, test.call().arguments.rawBytes(), libraries);
else
soltestAssert(deploy("", 0, bytes(), libraries), "Failed to deploy contract.");
constructed = true;
}
if (test.call().isConstructor)
{
if (m_transactionSuccessful == test.call().expectations.failure)
success = false;
test.setFailure(!m_transactionSuccessful);
test.setRawBytes(bytes());
}
else
{
bytes output;
if (test.call().useCallWithoutSignature)
output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value);
else
{
soltestAssert(
deploy(test.call().signature, 0, {}, libraries) && m_transactionSuccessful,
"Failed to deploy library " + test.call().signature
m_allowNonExistingFunctions || m_compiler.methodIdentifiers(m_compiler.lastContractName())
.isMember(test.call().signature),
"The function " + test.call().signature + " is not known to the compiler");
output = callContractFunctionWithValueNoEncoding(
test.call().signature, test.call().value.value, test.call().arguments.rawBytes()
);
libraries[test.call().signature] = m_contractAddress;
continue;
}
else
{
if (test.call().isConstructor)
deploy("", test.call().value.value, test.call().arguments.rawBytes(), libraries);
else
soltestAssert(deploy("", 0, bytes(), libraries), "Failed to deploy contract.");
constructed = true;
}
if (test.call().isConstructor)
{
if (m_transactionSuccessful == test.call().expectations.failure)
success = false;
if ((m_transactionSuccessful == test.call().expectations.failure)
|| (output != test.call().expectations.rawBytes()))
success = false;
test.setFailure(!m_transactionSuccessful);
test.setRawBytes(bytes());
}
else
{
bytes output;
if (test.call().useCallWithoutSignature)
output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value);
else
{
soltestAssert(
m_allowNonExistingFunctions || m_compiler.methodIdentifiers(m_compiler.lastContractName()).isMember(test.call().signature),
"The function " + test.call().signature + " is not known to the compiler"
);
output = callContractFunctionWithValueNoEncoding(
test.call().signature,
test.call().value.value,
test.call().arguments.rawBytes()
);
}
if ((m_transactionSuccessful == test.call().expectations.failure) || (output != test.call().expectations.rawBytes()))
success = false;
test.setFailure(!m_transactionSuccessful);
test.setRawBytes(std::move(output));
test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName()));
}
test.setFailure(!m_transactionSuccessful);
test.setRawBytes(std::move(output));
test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName()));
}
}
if (success && !m_runWithYul && compileViaYul)
if (success && !m_runWithYul && _compileViaYul)
{
m_compileViaYulCanBeSet = true;
AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) <<
_linePrefix << endl <<
_linePrefix << "Test can pass via Yul and marked with compileViaYul: false." << endl;
return TestResult::Failure;
}
if (!success && (m_runWithYul || !_compileViaYul))
{
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
for (auto const& test: m_tests)
{
m_compileViaYulCanBeSet = true;
AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) << _linePrefix << endl << _linePrefix
<< "Test can pass via Yul and marked with compileViaYul: false." << endl;
return TestResult::Failure;
ErrorReporter errorReporter;
_stream << test.format(errorReporter, _linePrefix, false, _formatted) << endl;
_stream << errorReporter.format(_linePrefix, _formatted);
}
if (!success && (m_runWithYul || !compileViaYul))
_stream << endl;
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
for (auto const& test: m_tests)
{
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
for (auto const& test: m_tests)
{
ErrorReporter errorReporter;
_stream << test.format(errorReporter, _linePrefix, false, _formatted) << endl;
_stream << errorReporter.format(_linePrefix, _formatted);
}
ErrorReporter errorReporter;
_stream << test.format(errorReporter, _linePrefix, true, _formatted) << endl;
_stream << errorReporter.format(_linePrefix, _formatted);
}
AnsiColorized(_stream, _formatted, {BOLD, RED})
<< _linePrefix << endl
<< _linePrefix << "Attention: Updates on the test will apply the detected format displayed." << endl;
if (_compileViaYul && m_runWithoutYul)
{
_stream << _linePrefix << endl << _linePrefix;
AnsiColorized(_stream, _formatted, {RED_BACKGROUND}) << "Note that the test passed without Yul.";
_stream << endl;
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
for (auto const& test: m_tests)
{
ErrorReporter errorReporter;
_stream << test.format(errorReporter, _linePrefix, true, _formatted) << endl;
_stream << errorReporter.format(_linePrefix, _formatted);
}
AnsiColorized(_stream, _formatted, {BOLD, RED}) << _linePrefix << endl << _linePrefix
<< "Attention: Updates on the test will apply the detected format displayed." << endl;
if (compileViaYul && m_runWithoutYul)
{
_stream << _linePrefix << endl << _linePrefix;
AnsiColorized(_stream, _formatted, {RED_BACKGROUND})
<< "Note that the test passed without Yul.";
_stream << endl;
}
else if (!compileViaYul && m_runWithYul)
AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) << _linePrefix << endl << _linePrefix
<< "Note that the test also has to pass via Yul." << endl;
return TestResult::Failure;
}
else if (!_compileViaYul && m_runWithYul)
AnsiColorized(_stream, _formatted, {BOLD, YELLOW})
<< _linePrefix << endl
<< _linePrefix << "Note that the test also has to pass via Yul." << endl;
return TestResult::Failure;
}
catch (WhiskersError const&)
{
// this is an error in Whiskers template, so should be thrown anyway
}
catch (WhiskersError const&)
{
// this is an error in Whiskers template, so should be thrown anyway
throw;
}
catch (YulException const&)
{
// this should be an error in yul compilation or translation
throw;
}
catch (boost::exception const&)
{
if (!_compileViaYul || m_runWithYul)
throw;
}
catch (YulException const&)
{
// this should be an error in yul compilation or translation
}
catch (std::exception const&)
{
if (!_compileViaYul || m_runWithYul)
throw;
}
catch (boost::exception const&)
{
if (compileViaYul && !m_runWithYul)
continue;
}
catch (...)
{
if (!_compileViaYul || m_runWithYul)
throw;
}
catch (std::exception const&)
{
if (compileViaYul && !m_runWithYul)
continue;
throw;
}
catch (...)
{
if (compileViaYul && !m_runWithYul)
continue;
throw;
}
}
return TestResult::Success;

View File

@ -40,9 +40,9 @@ class SemanticTest: public SolidityExecutionFramework, public EVMVersionRestrict
{
public:
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;
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.
bool deploy(std::string const& _contractName, u256 const& _value, bytes const& _arguments, std::map<std::string, solidity::test::Address> const& _libraries = {});
private:
TestResult runTest(std::ostream& _stream, std::string const& _linePrefix, bool _formatted, bool _compileViaYul, bool _compileToEwasm);
SourceMap m_sources;
std::size_t m_lineOffset;
std::vector<TestFunctionCall> m_tests;
bool m_runWithYul = false;
bool m_runWithEwasm = false;
bool m_runWithoutYul = true;
bool m_enforceViaYul = false;
bool m_runWithABIEncoderV1Only = false;

View File

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

View File

@ -43,6 +43,7 @@ bytes SolidityExecutionFramework::multiSourceCompileContract(
entry.second = addPreamble(entry.second);
m_compiler.reset();
m_compiler.enableEwasmGeneration(m_compileToEwasm);
m_compiler.setSources(sourcesWithPreamble);
m_compiler.setLibraries(_libraryAddresses);
m_compiler.setRevertStringBehaviour(m_revertStrings);
@ -63,38 +64,40 @@ bytes SolidityExecutionFramework::multiSourceCompileContract(
evmasm::LinkerObject obj;
if (m_compileViaYul)
{
// Try compiling twice: If the first run fails due to stack errors, forcefully enable
// the optimizer.
for (bool forceEnableOptimizer: {false, true})
if (m_compileToEwasm)
obj = m_compiler.ewasmObject(contractName);
else
{
OptimiserSettings optimiserSettings = m_optimiserSettings;
if (!forceEnableOptimizer && !optimiserSettings.runYulOptimiser)
// Try compiling twice: If the first run fails due to stack errors, forcefully enable
// the optimizer.
for (bool forceEnableOptimizer: {false, true})
{
// Enable some optimizations on the first run
optimiserSettings.runYulOptimiser = true;
optimiserSettings.yulOptimiserSteps = "uljmul jmul";
}
else if (forceEnableOptimizer)
optimiserSettings = OptimiserSettings::full();
OptimiserSettings optimiserSettings = m_optimiserSettings;
if (!forceEnableOptimizer && !optimiserSettings.runYulOptimiser)
{
// Enable some optimizations on the first run
optimiserSettings.runYulOptimiser = true;
optimiserSettings.yulOptimiserSteps = "uljmul jmul";
}
else if (forceEnableOptimizer)
optimiserSettings = OptimiserSettings::full();
yul::AssemblyStack asmStack(
m_evmVersion,
yul::AssemblyStack::Language::StrictAssembly,
optimiserSettings
);
bool analysisSuccessful = asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(contractName));
solAssert(analysisSuccessful, "Code that passed analysis in CompilerStack can't have errors");
yul::AssemblyStack
asmStack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, optimiserSettings);
bool analysisSuccessful = asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(contractName));
solAssert(analysisSuccessful, "Code that passed analysis in CompilerStack can't have errors");
try
{
asmStack.optimize();
obj = std::move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode);
break;
}
catch (...)
{
if (forceEnableOptimizer || optimiserSettings == OptimiserSettings::full())
throw;
try
{
asmStack.optimize();
obj = std::move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode);
break;
}
catch (...)
{
if (forceEnableOptimizer || optimiserSettings == OptimiserSettings::full())
throw;
}
}
}
}

View File

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

View File

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

View File

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

View File

@ -12,5 +12,7 @@ contract c {
}
}
// ====
// compileViaYul: also
// ----
// 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: (93-100): Empty array "pop" detected here
// Warning 2529: (82-89): 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: (228-254): Assertion violation happens here
// Warning 6328: (258-281): Assertion violation happens here
// Warning 6328: (198-224): Assertion violation happens here.
// Warning 6328: (228-254): 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: (252-278): Assertion violation happens here
// Warning 6328: (282-305): Assertion violation happens here
// Warning 6328: (309-335): Assertion violation happens here
// Warning 6328: (222-248): Assertion violation happens here.
// Warning 6328: (252-278): Assertion violation happens here.
// Warning 6328: (282-305): 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 6328: (150-184): Assertion violation happens here
// Warning 3944: (162-177): Underflow (resulting value less than 0) 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: (143-189): Assertion violation happens here
// Warning 6328: (113-139): 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: (202-218): Assertion violation happens here
// Warning 6328: (222-278): Assertion violation happens here
// Warning 6328: (122-148): Assertion violation happens here.
// Warning 6328: (202-218): 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 6328: (205-239): Assertion violation happens here
// Warning 3944: (217-232): Underflow (resulting value less than 0) 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: (309-333): Assertion violation happens here
// Warning 6328: (419-436): Assertion violation happens here
// Warning 6328: (193-217): Assertion violation happens here.
// Warning 6328: (309-333): 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