mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #9805 from ethereum/develop
Merge develop into breaking.
This commit is contained in:
commit
efe829b4b2
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -50,6 +50,7 @@ void CHCSmtLib2Interface::reset()
|
||||
{
|
||||
m_accumulatedOutput.clear();
|
||||
m_variables.clear();
|
||||
m_unhandledQueries.clear();
|
||||
}
|
||||
|
||||
void CHCSmtLib2Interface::registerRelation(Expression const& _expr)
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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>());
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -35,4 +35,6 @@ shared_ptr<IntSort> SortProvider::intSort(bool _signed)
|
||||
return uintSort;
|
||||
}
|
||||
|
||||
shared_ptr<BitVectorSort> const SortProvider::bitVectorSort{make_shared<BitVectorSort>(256)};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
63
libsolidity/analysis/Scoper.cpp
Normal file
63
libsolidity/analysis/Scoper.cpp
Normal 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();
|
||||
}
|
45
libsolidity/analysis/Scoper.h
Normal file
45
libsolidity/analysis/Scoper.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -38,6 +38,7 @@ namespace solidity::frontend
|
||||
{
|
||||
|
||||
class ASTNode;
|
||||
class ScopeOpener;
|
||||
class SourceUnit;
|
||||
class PragmaDirective;
|
||||
class ImportDirective;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
122
libyul/optimiser/NameSimplifier.cpp
Normal file
122
libyul/optimiser/NameSimplifier.cpp
Normal 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;
|
||||
}
|
76
libyul/optimiser/NameSimplifier.h
Normal file
76
libyul/optimiser/NameSimplifier.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
@ -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;
|
||||
|
@ -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'},
|
||||
|
@ -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
|
||||
|
@ -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; \
|
||||
|
@ -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; \
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -57,7 +57,7 @@ packagename=solc
|
||||
|
||||
static_build_distribution=focal
|
||||
|
||||
DISTRIBUTIONS="bionic eoan focal"
|
||||
DISTRIBUTIONS="bionic focal groovy"
|
||||
|
||||
if is_release
|
||||
then
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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):
|
||||
|
@ -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 = {};
|
||||
|
||||
|
@ -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{}))
|
||||
|
@ -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);
|
||||
|
@ -39,6 +39,7 @@ public:
|
||||
{
|
||||
std::string filename;
|
||||
langutil::EVMVersion evmVersion;
|
||||
std::vector<boost::filesystem::path> vmPaths;
|
||||
bool enforceCompileViaYul;
|
||||
};
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
1
test/cmdlineTests/name_simplifier/args
Normal file
1
test/cmdlineTests/name_simplifier/args
Normal file
@ -0,0 +1 @@
|
||||
--optimize --ir-optimized --metadata-hash none
|
23
test/cmdlineTests/name_simplifier/input.sol
Normal file
23
test/cmdlineTests/name_simplifier/input.sol
Normal 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");
|
||||
}
|
||||
}
|
129
test/cmdlineTests/name_simplifier/output
Normal file
129
test/cmdlineTests/name_simplifier/output
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
)
|
||||
|
||||
)
|
||||
|
@ -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:
|
||||
|
@ -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));
|
||||
|
@ -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"
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
)
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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");
|
||||
|
@ -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
|
||||
|
@ -12,5 +12,7 @@ contract c {
|
||||
}
|
||||
}
|
||||
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// test() -> 1, 2, 3, 4
|
||||
|
@ -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
|
@ -8,4 +8,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 2529: (82-89): Empty array "pop" detected here
|
||||
// Warning 2529: (82-89): Empty array "pop" detected here.
|
||||
|
@ -8,4 +8,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 2529: (82-89): Empty array "pop" detected here
|
||||
// Warning 2529: (82-89): Empty array "pop" detected here.
|
||||
|
@ -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.
|
||||
|
@ -8,4 +8,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 2529: (94-101): Empty array "pop" detected here
|
||||
// Warning 2529: (94-101): Empty array "pop" detected here.
|
||||
|
@ -11,4 +11,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 2529: (122-129): Empty array "pop" detected here
|
||||
// Warning 2529: (122-129): Empty array "pop" detected here.
|
||||
|
@ -11,4 +11,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 2529: (127-134): Empty array "pop" detected here
|
||||
// Warning 2529: (127-134): Empty array "pop" detected here.
|
||||
|
@ -13,4 +13,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 2529: (82-89): Empty array "pop" detected here
|
||||
// Warning 2529: (82-89): Empty array "pop" detected here.
|
||||
|
@ -10,4 +10,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 6328: (153-176): Assertion violation happens here
|
||||
// Warning 6328: (153-176): Assertion violation happens here.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -7,4 +7,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 2529: (82-89): Empty array "pop" detected here
|
||||
// Warning 2529: (82-89): Empty array "pop" detected here.
|
||||
|
@ -9,4 +9,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 2529: (111-121): Empty array "pop" detected here
|
||||
// Warning 2529: (111-121): Empty array "pop" detected here.
|
||||
|
@ -7,4 +7,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 2529: (76-83): Empty array "pop" detected here
|
||||
// Warning 2529: (76-83): Empty array "pop" detected here.
|
||||
|
@ -11,4 +11,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 2529: (150-157): Empty array "pop" detected here
|
||||
// Warning 2529: (150-157): Empty array "pop" detected here.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -12,4 +12,4 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 6328: (167-188): Assertion violation happens here
|
||||
// Warning 6328: (167-188): Assertion violation happens here.
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user