mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #7745 from ethereum/develop
Merge develop into develop_060
This commit is contained in:
commit
94272d44aa
@ -7,22 +7,24 @@ The docker images are build locally on the developer machine:
|
||||
```!sh
|
||||
cd .circleci/docker/
|
||||
|
||||
docker build -t ethereum/solidity-buildpack-deps:ubuntu1904 -f Dockerfile.ubuntu1904 .
|
||||
docker push ethereum/solidity-buildpack-deps:ubuntu1904
|
||||
docker build -t ethereum/solidity-buildpack-deps:ubuntu1904-<revision> -f Dockerfile.ubuntu1904 .
|
||||
docker push ethereum/solidity-buildpack-deps:ubuntu1904-<revision>
|
||||
```
|
||||
|
||||
which you can find on Dockerhub after the push at:
|
||||
The current revision is `2`.
|
||||
|
||||
https://hub.docker.com/r/ethereum/solidity-buildpack-deps
|
||||
Once the docker image has been built and pushed to Dockerhub, you can find it at:
|
||||
|
||||
where the image tag reflects the target OS to build Solidity and run its test on.
|
||||
https://hub.docker.com/r/ethereum/solidity-buildpack-deps:ubuntu1904-<revision>
|
||||
|
||||
where the image tag reflects the target OS and revision to build Solidity and run its tests on.
|
||||
|
||||
### Testing docker images locally
|
||||
|
||||
```!sh
|
||||
cd solidity
|
||||
# Mounts your local solidity directory in docker container for testing
|
||||
docker run -v `pwd`:/src/solidity -ti ethereum/solidity-buildpack-deps:ubuntu1904 /bin/bash
|
||||
docker run -v `pwd`:/src/solidity -ti ethereum/solidity-buildpack-deps:ubuntu1904-<revision> /bin/bash
|
||||
cd /src/solidity
|
||||
<commands_to_test_build_with_new_docker_image>
|
||||
```
|
||||
```
|
||||
|
@ -106,7 +106,7 @@ defaults:
|
||||
|
||||
- test_ubuntu1904_clang: &test_ubuntu1904_clang
|
||||
docker:
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-clang
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-2
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
@ -117,7 +117,7 @@ defaults:
|
||||
|
||||
- test_ubuntu1904: &test_ubuntu1904
|
||||
docker:
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-2
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
@ -287,7 +287,7 @@ jobs:
|
||||
|
||||
b_ubu_clang: &build_ubuntu1904_clang
|
||||
docker:
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-clang
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-2
|
||||
environment:
|
||||
CC: clang
|
||||
CXX: clang++
|
||||
@ -299,7 +299,7 @@ jobs:
|
||||
|
||||
b_ubu: &build_ubuntu1904
|
||||
docker:
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-2
|
||||
steps:
|
||||
- checkout
|
||||
- run: *run_build
|
||||
@ -313,7 +313,7 @@ jobs:
|
||||
|
||||
b_ubu18: &build_ubuntu1804
|
||||
docker:
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu1804
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu1804-2
|
||||
environment:
|
||||
CMAKE_OPTIONS: -DCMAKE_CXX_FLAGS=-O2
|
||||
CMAKE_BUILD_TYPE: RelWithDebugInfo
|
||||
@ -519,7 +519,7 @@ jobs:
|
||||
|
||||
b_docs:
|
||||
docker:
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-2
|
||||
steps:
|
||||
- checkout
|
||||
- run: *setup_prerelease_commit_hash
|
||||
@ -544,7 +544,7 @@ jobs:
|
||||
|
||||
t_ubu_cli: &t_ubu_cli
|
||||
docker:
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904
|
||||
- image: ethereum/solidity-buildpack-deps:ubuntu1904-2
|
||||
environment:
|
||||
TERM: xterm
|
||||
steps:
|
||||
|
@ -89,43 +89,24 @@ RUN set -ex; \
|
||||
ninja install/strip; \
|
||||
rm -rf /usr/src/libprotobuf-mutator
|
||||
|
||||
# ETHASH
|
||||
RUN set -ex; \
|
||||
cd /usr/src; \
|
||||
git clone --branch="v0.4.4" https://github.com/chfast/ethash.git; \
|
||||
cd ethash; \
|
||||
mkdir build; \
|
||||
cd build; \
|
||||
cmake .. -G Ninja -DBUILD_SHARED_LIBS=ON -DETHASH_BUILD_TESTS=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \
|
||||
ninja; \
|
||||
ninja install/strip; \
|
||||
rm -rf /usr/src/ethash
|
||||
|
||||
# INTX
|
||||
RUN set -ex; \
|
||||
cd /usr/src; \
|
||||
git clone --branch="v0.2.0" https://github.com/chfast/intx.git; \
|
||||
cd intx; \
|
||||
mkdir build; \
|
||||
cd build; \
|
||||
cmake .. -G Ninja -DBUILD_SHARED_LIBS=ON -DINTX_TESTING=OFF -DINTX_BENCHMARKING=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \
|
||||
ninja; \
|
||||
ninja install/strip; \
|
||||
rm -rf /usr/src/intx;
|
||||
|
||||
# EVMONE
|
||||
RUN set -ex; \
|
||||
cd /usr/src; \
|
||||
git clone --branch="v0.1.0" --recurse-submodules https://github.com/ethereum/evmone.git; \
|
||||
git clone --branch="v0.3.0" --recurse-submodules https://github.com/ethereum/evmone.git; \
|
||||
cd evmone; \
|
||||
mkdir build; \
|
||||
cd build; \
|
||||
# isoltest links against the evmone shared library
|
||||
cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \
|
||||
ninja; \
|
||||
ninja install/strip; \
|
||||
# abiv2_proto_ossfuzz links against the evmone standalone static library
|
||||
cmake -G Ninja -DBUILD_SHARED_LIBS=OFF -DCMAKE_INSTALL_PREFIX="/usr" ..; \
|
||||
ninja; \
|
||||
ninja install/strip; \
|
||||
rm -rf /usr/src/evmone
|
||||
|
||||
FROM base
|
||||
COPY --from=libraries /usr/lib /usr/lib
|
||||
COPY --from=libraries /usr/bin /usr/bin
|
||||
COPY --from=libraries /usr/include /usr/include
|
||||
COPY --from=libraries /usr/include /usr/include
|
||||
|
@ -74,34 +74,10 @@ RUN set -ex; \
|
||||
ar r /usr/lib/libFuzzingEngine.a *.o; \
|
||||
rm -rf /var/lib/libfuzzer
|
||||
|
||||
# ETHASH
|
||||
RUN set -ex; \
|
||||
cd /usr/src; \
|
||||
git clone --branch="v0.4.4" https://github.com/chfast/ethash.git; \
|
||||
cd ethash; \
|
||||
mkdir build; \
|
||||
cd build; \
|
||||
cmake .. -G Ninja -DBUILD_SHARED_LIBS=OFF -DETHASH_BUILD_TESTS=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \
|
||||
ninja; \
|
||||
ninja install/strip; \
|
||||
rm -rf /usr/src/ethash
|
||||
|
||||
# INTX
|
||||
RUN set -ex; \
|
||||
cd /usr/src; \
|
||||
git clone --branch="v0.2.0" https://github.com/chfast/intx.git; \
|
||||
cd intx; \
|
||||
mkdir build; \
|
||||
cd build; \
|
||||
cmake .. -G Ninja -DBUILD_SHARED_LIBS=OFF -DINTX_TESTING=OFF -DINTX_BENCHMARKING=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \
|
||||
ninja; \
|
||||
ninja install/strip; \
|
||||
rm -rf /usr/src/intx;
|
||||
|
||||
# EVMONE
|
||||
ARG EVMONE_HASH="f10d12c190f55a9d373e78b2dc0074d35d752c02cb536bb6fe754fb3719dd69e"
|
||||
ARG EVMONE_HASH="fa4f40daf7cf9ccbcca6c78345977e084ea2136a8eae661e4d19471be852b15b"
|
||||
ARG EVMONE_MAJOR="0"
|
||||
ARG EVMONE_MINOR="1"
|
||||
ARG EVMONE_MINOR="3"
|
||||
ARG EVMONE_MICRO="0"
|
||||
RUN set -ex; \
|
||||
EVMONE_VERSION="$EVMONE_MAJOR.$EVMONE_MINOR.$EVMONE_MICRO"; \
|
||||
|
@ -74,37 +74,14 @@ RUN set -ex; \
|
||||
ar r /usr/lib/libFuzzingEngine.a *.o; \
|
||||
rm -rf /var/lib/libfuzzer
|
||||
|
||||
# ETHASH
|
||||
RUN set -ex; \
|
||||
cd /usr/src; \
|
||||
git clone --branch="v0.4.4" https://github.com/chfast/ethash.git; \
|
||||
cd ethash; \
|
||||
mkdir build; \
|
||||
cd build; \
|
||||
cmake .. -G Ninja -DBUILD_SHARED_LIBS=OFF -DETHASH_BUILD_TESTS=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \
|
||||
ninja; \
|
||||
ninja install/strip; \
|
||||
rm -rf /usr/src/ethash
|
||||
|
||||
# INTX
|
||||
RUN set -ex; \
|
||||
cd /usr/src; \
|
||||
git clone --branch="v0.2.0" https://github.com/chfast/intx.git; \
|
||||
cd intx; \
|
||||
mkdir build; \
|
||||
cd build; \
|
||||
cmake .. -G Ninja -DBUILD_SHARED_LIBS=OFF -DINTX_TESTING=OFF -DINTX_BENCHMARKING=OFF -DCMAKE_INSTALL_PREFIX="/usr"; \
|
||||
ninja; \
|
||||
ninja install/strip; \
|
||||
rm -rf /usr/src/intx;
|
||||
|
||||
# EVMONE
|
||||
RUN set -ex; \
|
||||
cd /usr/src; \
|
||||
git clone --branch="v0.1.0" --recurse-submodules https://github.com/ethereum/evmone.git; \
|
||||
git clone --branch="v0.3.0" --recurse-submodules https://github.com/ethereum/evmone.git; \
|
||||
cd evmone; \
|
||||
mkdir build; \
|
||||
cd build; \
|
||||
# isoltest links against the evmone shared library
|
||||
cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX="/usr" ..; \
|
||||
ninja; \
|
||||
ninja install/strip; \
|
||||
|
17
.github/workflows/stale.yml
vendored
Normal file
17
.github/workflows/stale.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
name: Mark stale pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-pr-message: "A reminder that this pull request has had no activity for 14 days"
|
||||
stale-pr-label: "no-pr-activity"
|
||||
days-before-stale: 14
|
@ -42,6 +42,7 @@ Compiler Features:
|
||||
|
||||
|
||||
Bugfixes:
|
||||
* SMTChecker: Fix internal error when using ``abi.decode``.
|
||||
|
||||
|
||||
|
||||
|
@ -67,47 +67,48 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1(
|
||||
)
|
||||
{
|
||||
using Word = typename Pattern::Word;
|
||||
using Builtins = typename Pattern::Builtins;
|
||||
return std::vector<SimplificationRule<Pattern>> {
|
||||
// arithmetic on constants
|
||||
{{Pattern::Builtins::ADD, {A, B}}, [=]{ return A.d() + B.d(); }, false},
|
||||
{{Pattern::Builtins::MUL, {A, B}}, [=]{ return A.d() * B.d(); }, false},
|
||||
{{Pattern::Builtins::SUB, {A, B}}, [=]{ return A.d() - B.d(); }, false},
|
||||
{{Pattern::Builtins::DIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }, false},
|
||||
{{Pattern::Builtins::SDIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }, false},
|
||||
{{Pattern::Builtins::MOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }, false},
|
||||
{{Pattern::Builtins::SMOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }, false},
|
||||
{{Pattern::Builtins::EXP, {A, B}}, [=]{ return Word(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << Pattern::WordSize)); }, false},
|
||||
{{Pattern::Builtins::NOT, {A}}, [=]{ return ~A.d(); }, false},
|
||||
{{Pattern::Builtins::LT, {A, B}}, [=]() -> Word { return A.d() < B.d() ? 1 : 0; }, false},
|
||||
{{Pattern::Builtins::GT, {A, B}}, [=]() -> Word { return A.d() > B.d() ? 1 : 0; }, false},
|
||||
{{Pattern::Builtins::SLT, {A, B}}, [=]() -> Word { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }, false},
|
||||
{{Pattern::Builtins::SGT, {A, B}}, [=]() -> Word { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }, false},
|
||||
{{Pattern::Builtins::EQ, {A, B}}, [=]() -> Word { return A.d() == B.d() ? 1 : 0; }, false},
|
||||
{{Pattern::Builtins::ISZERO, {A}}, [=]() -> Word { return A.d() == 0 ? 1 : 0; }, false},
|
||||
{{Pattern::Builtins::AND, {A, B}}, [=]{ return A.d() & B.d(); }, false},
|
||||
{{Pattern::Builtins::OR, {A, B}}, [=]{ return A.d() | B.d(); }, false},
|
||||
{{Pattern::Builtins::XOR, {A, B}}, [=]{ return A.d() ^ B.d(); }, false},
|
||||
{{Pattern::Builtins::BYTE, {A, B}}, [=]{
|
||||
{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::BYTE(A, B), [=]{
|
||||
return
|
||||
A.d() >= Pattern::WordSize / 8 ?
|
||||
0 :
|
||||
(B.d() >> unsigned(8 * (Pattern::WordSize / 8 - 1 - A.d()))) & 0xff;
|
||||
}, false},
|
||||
{{Pattern::Builtins::ADDMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) + bigint(B.d())) % C.d()); }, false},
|
||||
{{Pattern::Builtins::MULMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : Word((bigint(A.d()) * bigint(B.d())) % C.d()); }, false},
|
||||
{{Pattern::Builtins::SIGNEXTEND, {A, B}}, [=]() -> Word {
|
||||
{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::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},
|
||||
{{Pattern::Builtins::SHL, {A, B}}, [=]{
|
||||
{Builtins::SHL(A, B), [=]{
|
||||
if (A.d() >= Pattern::WordSize)
|
||||
return Word(0);
|
||||
return shlWorkaround(B.d(), unsigned(A.d()));
|
||||
}, false},
|
||||
{{Pattern::Builtins::SHR, {A, B}}, [=]{
|
||||
{Builtins::SHR(A, B), [=]{
|
||||
if (A.d() >= Pattern::WordSize)
|
||||
return Word(0);
|
||||
return B.d() >> unsigned(A.d());
|
||||
@ -115,6 +116,7 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart1(
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
template <class Pattern>
|
||||
std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2(
|
||||
Pattern,
|
||||
@ -125,50 +127,51 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart2(
|
||||
)
|
||||
{
|
||||
using Word = typename Pattern::Word;
|
||||
using Builtins = typename Pattern::Builtins;
|
||||
return std::vector<SimplificationRule<Pattern>> {
|
||||
// invariants involving known constants
|
||||
{{Pattern::Builtins::ADD, {X, 0}}, [=]{ return X; }, false},
|
||||
{{Pattern::Builtins::ADD, {0, X}}, [=]{ return X; }, false},
|
||||
{{Pattern::Builtins::SUB, {X, 0}}, [=]{ return X; }, false},
|
||||
{{Pattern::Builtins::SUB, {~Word(0), X}}, [=]() -> Pattern { return {Pattern::Builtins::NOT, {X}}; }, false},
|
||||
{{Pattern::Builtins::MUL, {X, 0}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::MUL, {0, X}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::MUL, {X, 1}}, [=]{ return X; }, false},
|
||||
{{Pattern::Builtins::MUL, {1, X}}, [=]{ return X; }, false},
|
||||
{{Pattern::Builtins::MUL, {X, Word(-1)}}, [=]() -> Pattern { return {Pattern::Builtins::SUB, {0, X}}; }, false},
|
||||
{{Pattern::Builtins::MUL, {Word(-1), X}}, [=]() -> Pattern { return {Pattern::Builtins::SUB, {0, X}}; }, false},
|
||||
{{Pattern::Builtins::DIV, {X, 0}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::DIV, {0, X}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::DIV, {X, 1}}, [=]{ return X; }, false},
|
||||
{{Pattern::Builtins::SDIV, {X, 0}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::SDIV, {0, X}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::SDIV, {X, 1}}, [=]{ return X; }, false},
|
||||
{{Pattern::Builtins::AND, {X, ~Word(0)}}, [=]{ return X; }, false},
|
||||
{{Pattern::Builtins::AND, {~Word(0), X}}, [=]{ return X; }, false},
|
||||
{{Pattern::Builtins::AND, {X, 0}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::AND, {0, X}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::OR, {X, 0}}, [=]{ return X; }, false},
|
||||
{{Pattern::Builtins::OR, {0, X}}, [=]{ return X; }, false},
|
||||
{{Pattern::Builtins::OR, {X, ~Word(0)}}, [=]{ return ~Word(0); }, true},
|
||||
{{Pattern::Builtins::OR, {~Word(0), X}}, [=]{ return ~Word(0); }, true},
|
||||
{{Pattern::Builtins::XOR, {X, 0}}, [=]{ return X; }, false},
|
||||
{{Pattern::Builtins::XOR, {0, X}}, [=]{ return X; }, false},
|
||||
{{Pattern::Builtins::MOD, {X, 0}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::MOD, {0, X}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::EQ, {X, 0}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; }, false },
|
||||
{{Pattern::Builtins::EQ, {0, X}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; }, false },
|
||||
{{Pattern::Builtins::SHL, {0, X}}, [=]{ return X; }, false},
|
||||
{{Pattern::Builtins::SHR, {0, X}}, [=]{ return X; }, false},
|
||||
{{Pattern::Builtins::SHL, {X, 0}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::SHR, {X, 0}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::GT, {X, 0}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}; }, false},
|
||||
{{Pattern::Builtins::LT, {0, X}}, [=]() -> Pattern { return {Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}; }, false},
|
||||
{{Pattern::Builtins::GT, {X, ~Word(0)}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::LT, {~Word(0), X}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::GT, {0, X}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::LT, {X, 0}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::AND, {{Pattern::Builtins::BYTE, {X, Y}}, {Word(0xff)}}}, [=]() -> Pattern { return {Pattern::Builtins::BYTE, {X, Y}}; }, false},
|
||||
{{Pattern::Builtins::BYTE, {Pattern::WordSize / 8 - 1, X}}, [=]() -> Pattern { return {Pattern::Builtins::AND, {X, Word(0xff)}}; }, false}
|
||||
{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}
|
||||
};
|
||||
}
|
||||
|
||||
@ -182,18 +185,19 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart3(
|
||||
)
|
||||
{
|
||||
using Word = typename Pattern::Word;
|
||||
using Builtins = typename Pattern::Builtins;
|
||||
return std::vector<SimplificationRule<Pattern>> {
|
||||
// operations involving an expression and itself
|
||||
{{Pattern::Builtins::AND, {X, X}}, [=]{ return X; }, true},
|
||||
{{Pattern::Builtins::OR, {X, X}}, [=]{ return X; }, true},
|
||||
{{Pattern::Builtins::XOR, {X, X}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::SUB, {X, X}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::EQ, {X, X}}, [=]{ return Word(1); }, true},
|
||||
{{Pattern::Builtins::LT, {X, X}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::SLT, {X, X}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::GT, {X, X}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::SGT, {X, X}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::MOD, {X, X}}, [=]{ return Word(0); }, true}
|
||||
{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}
|
||||
};
|
||||
}
|
||||
|
||||
@ -207,25 +211,26 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart4(
|
||||
)
|
||||
{
|
||||
using Word = typename Pattern::Word;
|
||||
using Builtins = typename Pattern::Builtins;
|
||||
return std::vector<SimplificationRule<Pattern>> {
|
||||
// logical instruction combinations
|
||||
{{Pattern::Builtins::NOT, {{Pattern::Builtins::NOT, {X}}}}, [=]{ return X; }, false},
|
||||
{{Pattern::Builtins::XOR, {X, {Pattern::Builtins::XOR, {X, Y}}}}, [=]{ return Y; }, true},
|
||||
{{Pattern::Builtins::XOR, {X, {Pattern::Builtins::XOR, {Y, X}}}}, [=]{ return Y; }, true},
|
||||
{{Pattern::Builtins::XOR, {{Pattern::Builtins::XOR, {X, Y}}, X}}, [=]{ return Y; }, true},
|
||||
{{Pattern::Builtins::XOR, {{Pattern::Builtins::XOR, {Y, X}}, X}}, [=]{ return Y; }, true},
|
||||
{{Pattern::Builtins::OR, {X, {Pattern::Builtins::AND, {X, Y}}}}, [=]{ return X; }, true},
|
||||
{{Pattern::Builtins::OR, {X, {Pattern::Builtins::AND, {Y, X}}}}, [=]{ return X; }, true},
|
||||
{{Pattern::Builtins::OR, {{Pattern::Builtins::AND, {X, Y}}, X}}, [=]{ return X; }, true},
|
||||
{{Pattern::Builtins::OR, {{Pattern::Builtins::AND, {Y, X}}, X}}, [=]{ return X; }, true},
|
||||
{{Pattern::Builtins::AND, {X, {Pattern::Builtins::OR, {X, Y}}}}, [=]{ return X; }, true},
|
||||
{{Pattern::Builtins::AND, {X, {Pattern::Builtins::OR, {Y, X}}}}, [=]{ return X; }, true},
|
||||
{{Pattern::Builtins::AND, {{Pattern::Builtins::OR, {X, Y}}, X}}, [=]{ return X; }, true},
|
||||
{{Pattern::Builtins::AND, {{Pattern::Builtins::OR, {Y, X}}, X}}, [=]{ return X; }, true},
|
||||
{{Pattern::Builtins::AND, {X, {Pattern::Builtins::NOT, {X}}}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::AND, {{Pattern::Builtins::NOT, {X}}, X}}, [=]{ return Word(0); }, true},
|
||||
{{Pattern::Builtins::OR, {X, {Pattern::Builtins::NOT, {X}}}}, [=]{ return ~Word(0); }, true},
|
||||
{{Pattern::Builtins::OR, {{Pattern::Builtins::NOT, {X}}, X}}, [=]{ return ~Word(0); }, true},
|
||||
{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},
|
||||
};
|
||||
}
|
||||
|
||||
@ -240,6 +245,7 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5(
|
||||
)
|
||||
{
|
||||
using Word = typename Pattern::Word;
|
||||
using Builtins = typename Pattern::Builtins;
|
||||
|
||||
std::vector<SimplificationRule<Pattern>> rules;
|
||||
|
||||
@ -248,15 +254,15 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5(
|
||||
{
|
||||
Word value = Word(1) << i;
|
||||
rules.push_back({
|
||||
{Pattern::Builtins::MOD, {X, value}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::AND, {X, value - 1}}; },
|
||||
Builtins::MOD(X, value),
|
||||
[=]() -> Pattern { return Builtins::AND(X, value - 1); },
|
||||
false
|
||||
});
|
||||
}
|
||||
|
||||
// Replace SHL >=256, X with 0
|
||||
rules.push_back({
|
||||
{Pattern::Builtins::SHL, {A, X}},
|
||||
Builtins::SHL(A, X),
|
||||
[=]() -> Pattern { return Word(0); },
|
||||
true,
|
||||
[=]() { return A.d() >= Pattern::WordSize; }
|
||||
@ -264,7 +270,7 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5(
|
||||
|
||||
// Replace SHR >=256, X with 0
|
||||
rules.push_back({
|
||||
{Pattern::Builtins::SHR, {A, X}},
|
||||
Builtins::SHR(A, X),
|
||||
[=]() -> Pattern { return Word(0); },
|
||||
true,
|
||||
[=]() { return A.d() >= Pattern::WordSize; }
|
||||
@ -272,29 +278,29 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart5(
|
||||
|
||||
// Replace BYTE(A, X), A >= 32 with 0
|
||||
rules.push_back({
|
||||
{Pattern::Builtins::BYTE, {A, X}},
|
||||
Builtins::BYTE(A, X),
|
||||
[=]() -> Pattern { return Word(0); },
|
||||
true,
|
||||
[=]() { return A.d() >= Pattern::WordSize / 8; }
|
||||
});
|
||||
|
||||
for (auto const& op: std::vector<Instruction>{
|
||||
Pattern::Builtins::ADDRESS,
|
||||
Pattern::Builtins::CALLER,
|
||||
Pattern::Builtins::ORIGIN,
|
||||
Pattern::Builtins::COINBASE
|
||||
for (auto instr: {
|
||||
Instruction::ADDRESS,
|
||||
Instruction::CALLER,
|
||||
Instruction::ORIGIN,
|
||||
Instruction::COINBASE
|
||||
})
|
||||
{
|
||||
assertThrow(Pattern::WordSize > 160, OptimizerException, "");
|
||||
Word const mask = (Word(1) << 160) - 1;
|
||||
rules.push_back({
|
||||
{Pattern::Builtins::AND, {{op, mask}}},
|
||||
[=]() -> Pattern { return op; },
|
||||
Builtins::AND(Pattern{instr}, mask),
|
||||
[=]() -> Pattern { return {instr}; },
|
||||
false
|
||||
});
|
||||
rules.push_back({
|
||||
{Pattern::Builtins::AND, {{mask, op}}},
|
||||
[=]() -> Pattern { return op; },
|
||||
Builtins::AND(mask, Pattern{instr}),
|
||||
[=]() -> Pattern { return {instr}; },
|
||||
false
|
||||
});
|
||||
}
|
||||
@ -311,30 +317,35 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart6(
|
||||
Pattern Y
|
||||
)
|
||||
{
|
||||
using Builtins = typename Pattern::Builtins;
|
||||
|
||||
std::vector<SimplificationRule<Pattern>> rules;
|
||||
// Double negation of opcodes with boolean result
|
||||
for (auto const& op: std::vector<Instruction>{
|
||||
Pattern::Builtins::EQ,
|
||||
Pattern::Builtins::LT,
|
||||
Pattern::Builtins::SLT,
|
||||
Pattern::Builtins::GT,
|
||||
Pattern::Builtins::SGT
|
||||
for (auto instr: {
|
||||
Instruction::EQ,
|
||||
Instruction::LT,
|
||||
Instruction::SLT,
|
||||
Instruction::GT,
|
||||
Instruction::SGT
|
||||
})
|
||||
{
|
||||
typename Builtins::PatternGeneratorInstance op{instr};
|
||||
rules.push_back({
|
||||
{Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {{op, {X, Y}}}}}},
|
||||
[=]() -> Pattern { return {op, {X, Y}}; },
|
||||
Builtins::ISZERO(Builtins::ISZERO(op(X, Y))),
|
||||
[=]() -> Pattern { return op(X, Y); },
|
||||
false
|
||||
});
|
||||
}
|
||||
|
||||
rules.push_back({
|
||||
{Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {{Pattern::Builtins::ISZERO, {X}}}}}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::ISZERO, {X}}; },
|
||||
Builtins::ISZERO(Builtins::ISZERO(Builtins::ISZERO(X))),
|
||||
[=]() -> Pattern { return Builtins::ISZERO(X); },
|
||||
false
|
||||
});
|
||||
|
||||
rules.push_back({
|
||||
{Pattern::Builtins::ISZERO, {{Pattern::Builtins::XOR, {X, Y}}}},
|
||||
[=]() -> Pattern { return { Pattern::Builtins::EQ, {X, Y} }; },
|
||||
Builtins::ISZERO(Builtins::XOR(X, Y)),
|
||||
[=]() -> Pattern { return Builtins::EQ(X, Y); },
|
||||
false
|
||||
});
|
||||
|
||||
@ -351,42 +362,44 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
|
||||
)
|
||||
{
|
||||
using Word = typename Pattern::Word;
|
||||
using Builtins = typename Pattern::Builtins;
|
||||
|
||||
std::vector<SimplificationRule<Pattern>> rules;
|
||||
// Associative operations
|
||||
for (auto const& opFun: std::vector<std::pair<Instruction,std::function<Word(Word const&,Word const&)>>>{
|
||||
{Pattern::Builtins::ADD, std::plus<Word>()},
|
||||
{Pattern::Builtins::MUL, std::multiplies<Word>()},
|
||||
{Pattern::Builtins::AND, std::bit_and<Word>()},
|
||||
{Pattern::Builtins::OR, std::bit_or<Word>()},
|
||||
{Pattern::Builtins::XOR, std::bit_xor<Word>()}
|
||||
for (auto&& instrAndFunc: std::vector<std::pair<Instruction, std::function<Word(Word, Word)>>>{
|
||||
{Instruction::ADD, std::plus<Word>()},
|
||||
{Instruction::MUL, std::multiplies<Word>()},
|
||||
{Instruction::AND, std::bit_and<Word>()},
|
||||
{Instruction::OR, std::bit_or<Word>()},
|
||||
{Instruction::XOR, std::bit_xor<Word>()}
|
||||
})
|
||||
{
|
||||
auto op = opFun.first;
|
||||
auto fun = opFun.second;
|
||||
typename Builtins::PatternGeneratorInstance op{instrAndFunc.first};
|
||||
std::function<Word(Word, Word)> fun = instrAndFunc.second;
|
||||
// Moving constants to the outside, order matters here - we first add rules
|
||||
// for constants and then for non-constants.
|
||||
// xa can be (X, A) or (A, X)
|
||||
for (auto xa: {std::vector<Pattern>{X, A}, std::vector<Pattern>{A, X}})
|
||||
for (auto const& opXA: {op(X, A), op(A, X)})
|
||||
{
|
||||
rules += std::vector<SimplificationRule<Pattern>>{{
|
||||
// (X+A)+B -> X+(A+B)
|
||||
{op, {{op, xa}, B}},
|
||||
[=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; },
|
||||
op(opXA, B),
|
||||
[=]() -> Pattern { return op(X, fun(A.d(), B.d())); },
|
||||
false
|
||||
}, {
|
||||
// (X+A)+Y -> (X+Y)+A
|
||||
{op, {{op, xa}, Y}},
|
||||
[=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; },
|
||||
op(opXA, Y),
|
||||
[=]() -> Pattern { return op(op(X, Y), A); },
|
||||
false
|
||||
}, {
|
||||
// B+(X+A) -> X+(A+B)
|
||||
{op, {B, {op, xa}}},
|
||||
[=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; },
|
||||
op(B, opXA),
|
||||
[=]() -> Pattern { return op(X, fun(A.d(), B.d())); },
|
||||
false
|
||||
}, {
|
||||
// Y+(X+A) -> (Y+X)+A
|
||||
{op, {Y, {op, xa}}},
|
||||
[=]() -> Pattern { return {op, {{op, {Y, X}}, A}}; },
|
||||
op(Y, opXA),
|
||||
[=]() -> Pattern { return op(op(Y, X), A); },
|
||||
false
|
||||
}};
|
||||
}
|
||||
@ -395,13 +408,13 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
|
||||
// Combine two SHL by constant
|
||||
rules.push_back({
|
||||
// SHL(B, SHL(A, X)) -> SHL(min(A+B, 256), X)
|
||||
{Pattern::Builtins::SHL, {{B}, {Pattern::Builtins::SHL, {{A}, {X}}}}},
|
||||
Builtins::SHL(B, Builtins::SHL(A, X)),
|
||||
[=]() -> Pattern {
|
||||
bigint sum = bigint(A.d()) + B.d();
|
||||
if (sum >= Pattern::WordSize)
|
||||
return {Pattern::Builtins::AND, {X, Word(0)}};
|
||||
return Builtins::AND(X, Word(0));
|
||||
else
|
||||
return {Pattern::Builtins::SHL, {Word(sum), X}};
|
||||
return Builtins::SHL(Word(sum), X);
|
||||
},
|
||||
false
|
||||
});
|
||||
@ -409,13 +422,13 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
|
||||
// Combine two SHR by constant
|
||||
rules.push_back({
|
||||
// SHR(B, SHR(A, X)) -> SHR(min(A+B, 256), X)
|
||||
{Pattern::Builtins::SHR, {{B}, {Pattern::Builtins::SHR, {{A}, {X}}}}},
|
||||
Builtins::SHR(B, Builtins::SHR(A, X)),
|
||||
[=]() -> Pattern {
|
||||
bigint sum = bigint(A.d()) + B.d();
|
||||
if (sum >= Pattern::WordSize)
|
||||
return {Pattern::Builtins::AND, {X, Word(0)}};
|
||||
return Builtins::AND(X, Word(0));
|
||||
else
|
||||
return {Pattern::Builtins::SHR, {Word(sum), X}};
|
||||
return Builtins::SHR(Word(sum), X);
|
||||
},
|
||||
false
|
||||
});
|
||||
@ -423,16 +436,16 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
|
||||
// Combine SHL-SHR by constant
|
||||
rules.push_back({
|
||||
// SHR(B, SHL(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask)
|
||||
{Pattern::Builtins::SHR, {{B}, {Pattern::Builtins::SHL, {{A}, {X}}}}},
|
||||
Builtins::SHR(B, Builtins::SHL(A, X)),
|
||||
[=]() -> Pattern {
|
||||
Word mask = shlWorkaround(~Word(0), unsigned(A.d())) >> unsigned(B.d());
|
||||
|
||||
if (A.d() > B.d())
|
||||
return {Pattern::Builtins::AND, {{Pattern::Builtins::SHL, {A.d() - B.d(), X}}, mask}};
|
||||
return Builtins::AND(Builtins::SHL(A.d() - B.d(), X), mask);
|
||||
else if (B.d() > A.d())
|
||||
return {Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {B.d() - A.d(), X}}, mask}};
|
||||
return Builtins::AND(Builtins::SHR(B.d() - A.d(), X), mask);
|
||||
else
|
||||
return {Pattern::Builtins::AND, {X, mask}};
|
||||
return Builtins::AND(X, mask);
|
||||
},
|
||||
false,
|
||||
[=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; }
|
||||
@ -441,41 +454,42 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
|
||||
// Combine SHR-SHL by constant
|
||||
rules.push_back({
|
||||
// SHL(B, SHR(A, X)) -> AND(SH[L/R]([B - A / A - B], X), Mask)
|
||||
{Pattern::Builtins::SHL, {{B}, {Pattern::Builtins::SHR, {{A}, {X}}}}},
|
||||
Builtins::SHL(B, Builtins::SHR(A, X)),
|
||||
[=]() -> Pattern {
|
||||
Word mask = shlWorkaround((~Word(0)) >> unsigned(A.d()), unsigned(B.d()));
|
||||
|
||||
if (A.d() > B.d())
|
||||
return {Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {A.d() - B.d(), X}}, mask}};
|
||||
return Builtins::AND(Builtins::SHR(A.d() - B.d(), X), mask);
|
||||
else if (B.d() > A.d())
|
||||
return {Pattern::Builtins::AND, {{Pattern::Builtins::SHL, {B.d() - A.d(), X}}, mask}};
|
||||
return Builtins::AND(Builtins::SHL(B.d() - A.d(), X), mask);
|
||||
else
|
||||
return {Pattern::Builtins::AND, {X, mask}};
|
||||
return Builtins::AND(X, mask);
|
||||
},
|
||||
false,
|
||||
[=] { return A.d() < Pattern::WordSize && B.d() < Pattern::WordSize; }
|
||||
});
|
||||
|
||||
// Move AND with constant across SHL and SHR by constant
|
||||
for (auto shiftOp: {Pattern::Builtins::SHL, Pattern::Builtins::SHR})
|
||||
for (auto instr: {Instruction::SHL, Instruction::SHR})
|
||||
{
|
||||
typename Builtins::PatternGeneratorInstance shiftOp{instr};
|
||||
auto replacement = [=]() -> Pattern {
|
||||
Word mask =
|
||||
shiftOp == Pattern::Builtins::SHL ?
|
||||
instr == Instruction::SHL ?
|
||||
shlWorkaround(A.d(), unsigned(B.d())) :
|
||||
A.d() >> unsigned(B.d());
|
||||
return {Pattern::Builtins::AND, {{shiftOp, {B.d(), X}}, std::move(mask)}};
|
||||
return Builtins::AND(shiftOp(B.d(), X), std::move(mask));
|
||||
};
|
||||
rules.push_back({
|
||||
// SH[L/R](B, AND(X, A)) -> AND(SH[L/R](B, X), [ A << B / A >> B ])
|
||||
{shiftOp, {{B}, {Pattern::Builtins::AND, {{X}, {A}}}}},
|
||||
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}, {Pattern::Builtins::AND, {{A}, {X}}}}},
|
||||
shiftOp(B, Builtins::AND(A, X)),
|
||||
replacement,
|
||||
false,
|
||||
[=] { return B.d() < Pattern::WordSize; }
|
||||
@ -484,27 +498,27 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
|
||||
|
||||
rules.push_back({
|
||||
// MUL(X, SHL(Y, 1)) -> SHL(Y, X)
|
||||
{Pattern::Builtins::MUL, {X, {Pattern::Builtins::SHL, {Y, Word(1)}}}},
|
||||
Builtins::MUL(X, Builtins::SHL(Y, Word(1))),
|
||||
[=]() -> Pattern {
|
||||
return {Pattern::Builtins::SHL, {Y, X}};
|
||||
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)
|
||||
{Pattern::Builtins::MUL, {{Pattern::Builtins::SHL, {X, Word(1)}}, Y}},
|
||||
Builtins::MUL(Builtins::SHL(X, Word(1)), Y),
|
||||
[=]() -> Pattern {
|
||||
return {Pattern::Builtins::SHL, {X, Y}};
|
||||
return Builtins::SHL(X, Y);
|
||||
},
|
||||
false
|
||||
});
|
||||
|
||||
rules.push_back({
|
||||
// DIV(X, SHL(Y, 1)) -> SHR(Y, X)
|
||||
{Pattern::Builtins::DIV, {X, {Pattern::Builtins::SHL, {Y, Word(1)}}}},
|
||||
Builtins::DIV(X, Builtins::SHL(Y, Word(1))),
|
||||
[=]() -> Pattern {
|
||||
return {Pattern::Builtins::SHR, {Y, X}};
|
||||
return Builtins::SHR(Y, X);
|
||||
},
|
||||
// Actually only changes the order, does not remove.
|
||||
true
|
||||
@ -519,16 +533,16 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
|
||||
|
||||
rules.push_back({
|
||||
// AND(A, SHR(B, X)) -> A & ((2^256-1) >> B) == ((2^256-1) >> B)
|
||||
{Pattern::Builtins::AND, {A, {Pattern::Builtins::SHR, {B, X}}}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::SHR, {B, X}}; },
|
||||
Builtins::AND(A, Builtins::SHR(B, X)),
|
||||
[=]() -> Pattern { return Builtins::SHR(B, X); },
|
||||
false,
|
||||
feasibilityFunction
|
||||
});
|
||||
|
||||
rules.push_back({
|
||||
// AND(SHR(B, X), A) -> ((2^256-1) >> B) & A == ((2^256-1) >> B)
|
||||
{Pattern::Builtins::AND, {{Pattern::Builtins::SHR, {B, X}}, A}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::SHR, {B, X}}; },
|
||||
Builtins::AND(Builtins::SHR(B, X), A),
|
||||
[=]() -> Pattern { return Builtins::SHR(B, X); },
|
||||
false,
|
||||
feasibilityFunction
|
||||
});
|
||||
@ -545,34 +559,35 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart8(
|
||||
Pattern Y
|
||||
)
|
||||
{
|
||||
using Builtins = typename Pattern::Builtins;
|
||||
std::vector<SimplificationRule<Pattern>> rules;
|
||||
|
||||
// move constants across subtractions
|
||||
rules += std::vector<SimplificationRule<Pattern>>{
|
||||
{
|
||||
// X - A -> X + (-A)
|
||||
{Pattern::Builtins::SUB, {X, A}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::ADD, {X, 0 - A.d()}}; },
|
||||
Builtins::SUB(X, A),
|
||||
[=]() -> Pattern { return Builtins::ADD(X, 0 - A.d()); },
|
||||
false
|
||||
}, {
|
||||
// (X + A) - Y -> (X - Y) + A
|
||||
{Pattern::Builtins::SUB, {{Pattern::Builtins::ADD, {X, A}}, Y}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, A}}; },
|
||||
Builtins::SUB(Builtins::ADD(X, A), Y),
|
||||
[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); },
|
||||
false
|
||||
}, {
|
||||
// (A + X) - Y -> (X - Y) + A
|
||||
{Pattern::Builtins::SUB, {{Pattern::Builtins::ADD, {A, X}}, Y}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, A}}; },
|
||||
Builtins::SUB(Builtins::ADD(A, X), Y),
|
||||
[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), A); },
|
||||
false
|
||||
}, {
|
||||
// X - (Y + A) -> (X - Y) + (-A)
|
||||
{Pattern::Builtins::SUB, {X, {Pattern::Builtins::ADD, {Y, A}}}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, 0 - A.d()}}; },
|
||||
Builtins::SUB(X, Builtins::ADD(Y, A)),
|
||||
[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); },
|
||||
false
|
||||
}, {
|
||||
// X - (A + Y) -> (X - Y) + (-A)
|
||||
{Pattern::Builtins::SUB, {X, {Pattern::Builtins::ADD, {A, Y}}}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::ADD, {{Pattern::Builtins::SUB, {X, Y}}, 0 - A.d()}}; },
|
||||
Builtins::SUB(X, Builtins::ADD(A, Y)),
|
||||
[=]() -> Pattern { return Builtins::ADD(Builtins::SUB(X, Y), 0 - A.d()); },
|
||||
false
|
||||
}
|
||||
};
|
||||
@ -591,30 +606,31 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart9(
|
||||
)
|
||||
{
|
||||
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({
|
||||
{Pattern::Builtins::AND, {{Pattern::Builtins::CREATE, {W, X, Y}}, mask}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::CREATE, {W, X, Y}}; },
|
||||
Builtins::AND(Builtins::CREATE(W, X, Y), mask),
|
||||
[=]() -> Pattern { return Builtins::CREATE(W, X, Y); },
|
||||
false
|
||||
});
|
||||
rules.push_back({
|
||||
{Pattern::Builtins::AND, {{mask, {Pattern::Builtins::CREATE, {W, X, Y}}}}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::CREATE, {W, X, Y}}; },
|
||||
Builtins::AND(mask, Builtins::CREATE(W, X, Y)),
|
||||
[=]() -> Pattern { return Builtins::CREATE(W, X, Y); },
|
||||
false
|
||||
});
|
||||
// CREATE2
|
||||
rules.push_back({
|
||||
{Pattern::Builtins::AND, {{Pattern::Builtins::CREATE2, {W, X, Y, Z}}, mask}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::CREATE2, {W, X, Y, Z}}; },
|
||||
Builtins::AND(Builtins::CREATE2(W, X, Y, Z), mask),
|
||||
[=]() -> Pattern { return Builtins::CREATE2(W, X, Y, Z); },
|
||||
false
|
||||
});
|
||||
rules.push_back({
|
||||
{Pattern::Builtins::AND, {{mask, {Pattern::Builtins::CREATE2, {W, X, Y, Z}}}}},
|
||||
[=]() -> Pattern { return {Pattern::Builtins::CREATE2, {W, X, Y, Z}}; },
|
||||
Builtins::AND(mask, Builtins::CREATE2(W, X, Y, Z)),
|
||||
[=]() -> Pattern { return Builtins::CREATE2(W, X, Y, Z); },
|
||||
false
|
||||
});
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <libevmasm/Instruction.h>
|
||||
#include <libdevcore/CommonData.h>
|
||||
#include <functional>
|
||||
|
||||
namespace dev
|
||||
@ -55,84 +56,105 @@ struct SimplificationRule
|
||||
std::function<bool()> feasible;
|
||||
};
|
||||
|
||||
template <typename Pattern>
|
||||
struct EVMBuiltins
|
||||
{
|
||||
using InstrType = Instruction;
|
||||
static auto constexpr STOP = Instruction::STOP;
|
||||
static auto constexpr ADD = Instruction::ADD;
|
||||
static auto constexpr SUB = Instruction::SUB;
|
||||
static auto constexpr MUL = Instruction::MUL;
|
||||
static auto constexpr DIV = Instruction::DIV;
|
||||
static auto constexpr SDIV = Instruction::SDIV;
|
||||
static auto constexpr MOD = Instruction::MOD;
|
||||
static auto constexpr SMOD = Instruction::SMOD;
|
||||
static auto constexpr EXP = Instruction::EXP;
|
||||
static auto constexpr NOT = Instruction::NOT;
|
||||
static auto constexpr LT = Instruction::LT;
|
||||
static auto constexpr GT = Instruction::GT;
|
||||
static auto constexpr SLT = Instruction::SLT;
|
||||
static auto constexpr SGT = Instruction::SGT;
|
||||
static auto constexpr EQ = Instruction::EQ;
|
||||
static auto constexpr ISZERO = Instruction::ISZERO;
|
||||
static auto constexpr AND = Instruction::AND;
|
||||
static auto constexpr OR = Instruction::OR;
|
||||
static auto constexpr XOR = Instruction::XOR;
|
||||
static auto constexpr BYTE = Instruction::BYTE;
|
||||
static auto constexpr SHL = Instruction::SHL;
|
||||
static auto constexpr SHR = Instruction::SHR;
|
||||
static auto constexpr SAR = Instruction::SAR;
|
||||
static auto constexpr ADDMOD = Instruction::ADDMOD;
|
||||
static auto constexpr MULMOD = Instruction::MULMOD;
|
||||
static auto constexpr SIGNEXTEND = Instruction::SIGNEXTEND;
|
||||
static auto constexpr KECCAK256 = Instruction::KECCAK256;
|
||||
static auto constexpr ADDRESS = Instruction::ADDRESS;
|
||||
static auto constexpr BALANCE = Instruction::BALANCE;
|
||||
static auto constexpr ORIGIN = Instruction::ORIGIN;
|
||||
static auto constexpr CALLER = Instruction::CALLER;
|
||||
static auto constexpr CALLVALUE = Instruction::CALLVALUE;
|
||||
static auto constexpr CALLDATALOAD = Instruction::CALLDATALOAD;
|
||||
static auto constexpr CALLDATASIZE = Instruction::CALLDATASIZE;
|
||||
static auto constexpr CALLDATACOPY = Instruction::CALLDATACOPY;
|
||||
static auto constexpr CODESIZE = Instruction::CODESIZE;
|
||||
static auto constexpr CODECOPY = Instruction::CODECOPY;
|
||||
static auto constexpr GASPRICE = Instruction::GASPRICE;
|
||||
static auto constexpr EXTCODESIZE = Instruction::EXTCODESIZE;
|
||||
static auto constexpr EXTCODECOPY = Instruction::EXTCODECOPY;
|
||||
static auto constexpr RETURNDATASIZE = Instruction::RETURNDATASIZE;
|
||||
static auto constexpr RETURNDATACOPY = Instruction::RETURNDATACOPY;
|
||||
static auto constexpr EXTCODEHASH = Instruction::EXTCODEHASH;
|
||||
static auto constexpr BLOCKHASH = Instruction::BLOCKHASH;
|
||||
static auto constexpr COINBASE = Instruction::COINBASE;
|
||||
static auto constexpr TIMESTAMP = Instruction::TIMESTAMP;
|
||||
static auto constexpr NUMBER = Instruction::NUMBER;
|
||||
static auto constexpr DIFFICULTY = Instruction::DIFFICULTY;
|
||||
static auto constexpr GASLIMIT = Instruction::GASLIMIT;
|
||||
static auto constexpr CHAINID = Instruction::CHAINID;
|
||||
static auto constexpr SELFBALANCE = Instruction::SELFBALANCE;
|
||||
static auto constexpr POP = Instruction::POP;
|
||||
static auto constexpr MLOAD = Instruction::MLOAD;
|
||||
static auto constexpr MSTORE = Instruction::MSTORE;
|
||||
static auto constexpr MSTORE8 = Instruction::MSTORE8;
|
||||
static auto constexpr SLOAD = Instruction::SLOAD;
|
||||
static auto constexpr SSTORE = Instruction::SSTORE;
|
||||
static auto constexpr PC = Instruction::PC;
|
||||
static auto constexpr MSIZE = Instruction::MSIZE;
|
||||
static auto constexpr GAS = Instruction::GAS;
|
||||
static auto constexpr LOG0 = Instruction::LOG0;
|
||||
static auto constexpr LOG1 = Instruction::LOG1;
|
||||
static auto constexpr LOG2 = Instruction::LOG2;
|
||||
static auto constexpr LOG3 = Instruction::LOG3;
|
||||
static auto constexpr LOG4 = Instruction::LOG4;
|
||||
static auto constexpr CREATE = Instruction::CREATE;
|
||||
static auto constexpr CALL = Instruction::CALL;
|
||||
static auto constexpr CALLCODE = Instruction::CALLCODE;
|
||||
static auto constexpr STATICCALL = Instruction::STATICCALL;
|
||||
static auto constexpr RETURN = Instruction::RETURN;
|
||||
static auto constexpr DELEGATECALL = Instruction::DELEGATECALL;
|
||||
static auto constexpr CREATE2 = Instruction::CREATE2;
|
||||
static auto constexpr REVERT = Instruction::REVERT;
|
||||
static auto constexpr INVALID = Instruction::INVALID;
|
||||
static auto constexpr SELFDESTRUCT = Instruction::SELFDESTRUCT;
|
||||
|
||||
template<Instruction inst>
|
||||
struct PatternGenerator
|
||||
{
|
||||
template<typename... Args> constexpr Pattern operator()(Args&&... _args) const
|
||||
{
|
||||
return {inst, {std::forward<Args>(_args)...}};
|
||||
};
|
||||
};
|
||||
|
||||
struct PatternGeneratorInstance
|
||||
{
|
||||
Instruction instruction;
|
||||
template<typename... Args> constexpr Pattern operator()(Args&&... _args) const
|
||||
{
|
||||
return {instruction, {std::forward<Args>(_args)...}};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
static auto constexpr STOP = PatternGenerator<Instruction::STOP>{};
|
||||
static auto constexpr ADD = PatternGenerator<Instruction::ADD>{};
|
||||
static auto constexpr SUB = PatternGenerator<Instruction::SUB>{};
|
||||
static auto constexpr MUL = PatternGenerator<Instruction::MUL>{};
|
||||
static auto constexpr DIV = PatternGenerator<Instruction::DIV>{};
|
||||
static auto constexpr SDIV = PatternGenerator<Instruction::SDIV>{};
|
||||
static auto constexpr MOD = PatternGenerator<Instruction::MOD>{};
|
||||
static auto constexpr SMOD = PatternGenerator<Instruction::SMOD>{};
|
||||
static auto constexpr EXP = PatternGenerator<Instruction::EXP>{};
|
||||
static auto constexpr NOT = PatternGenerator<Instruction::NOT>{};
|
||||
static auto constexpr LT = PatternGenerator<Instruction::LT>{};
|
||||
static auto constexpr GT = PatternGenerator<Instruction::GT>{};
|
||||
static auto constexpr SLT = PatternGenerator<Instruction::SLT>{};
|
||||
static auto constexpr SGT = PatternGenerator<Instruction::SGT>{};
|
||||
static auto constexpr EQ = PatternGenerator<Instruction::EQ>{};
|
||||
static auto constexpr ISZERO = PatternGenerator<Instruction::ISZERO>{};
|
||||
static auto constexpr AND = PatternGenerator<Instruction::AND>{};
|
||||
static auto constexpr OR = PatternGenerator<Instruction::OR>{};
|
||||
static auto constexpr XOR = PatternGenerator<Instruction::XOR>{};
|
||||
static auto constexpr BYTE = PatternGenerator<Instruction::BYTE>{};
|
||||
static auto constexpr SHL = PatternGenerator<Instruction::SHL>{};
|
||||
static auto constexpr SHR = PatternGenerator<Instruction::SHR>{};
|
||||
static auto constexpr SAR = PatternGenerator<Instruction::SAR>{};
|
||||
static auto constexpr ADDMOD = PatternGenerator<Instruction::ADDMOD>{};
|
||||
static auto constexpr MULMOD = PatternGenerator<Instruction::MULMOD>{};
|
||||
static auto constexpr SIGNEXTEND = PatternGenerator<Instruction::SIGNEXTEND>{};
|
||||
static auto constexpr KECCAK256 = PatternGenerator<Instruction::KECCAK256>{};
|
||||
static auto constexpr ADDRESS = PatternGenerator<Instruction::ADDRESS>{};
|
||||
static auto constexpr BALANCE = PatternGenerator<Instruction::BALANCE>{};
|
||||
static auto constexpr ORIGIN = PatternGenerator<Instruction::ORIGIN>{};
|
||||
static auto constexpr CALLER = PatternGenerator<Instruction::CALLER>{};
|
||||
static auto constexpr CALLVALUE = PatternGenerator<Instruction::CALLVALUE>{};
|
||||
static auto constexpr CALLDATALOAD = PatternGenerator<Instruction::CALLDATALOAD>{};
|
||||
static auto constexpr CALLDATASIZE = PatternGenerator<Instruction::CALLDATASIZE>{};
|
||||
static auto constexpr CALLDATACOPY = PatternGenerator<Instruction::CALLDATACOPY>{};
|
||||
static auto constexpr CODESIZE = PatternGenerator<Instruction::CODESIZE>{};
|
||||
static auto constexpr CODECOPY = PatternGenerator<Instruction::CODECOPY>{};
|
||||
static auto constexpr GASPRICE = PatternGenerator<Instruction::GASPRICE>{};
|
||||
static auto constexpr EXTCODESIZE = PatternGenerator<Instruction::EXTCODESIZE>{};
|
||||
static auto constexpr EXTCODECOPY = PatternGenerator<Instruction::EXTCODECOPY>{};
|
||||
static auto constexpr RETURNDATASIZE = PatternGenerator<Instruction::RETURNDATASIZE>{};
|
||||
static auto constexpr RETURNDATACOPY = PatternGenerator<Instruction::RETURNDATACOPY>{};
|
||||
static auto constexpr EXTCODEHASH = PatternGenerator<Instruction::EXTCODEHASH>{};
|
||||
static auto constexpr BLOCKHASH = PatternGenerator<Instruction::BLOCKHASH>{};
|
||||
static auto constexpr COINBASE = PatternGenerator<Instruction::COINBASE>{};
|
||||
static auto constexpr TIMESTAMP = PatternGenerator<Instruction::TIMESTAMP>{};
|
||||
static auto constexpr NUMBER = PatternGenerator<Instruction::NUMBER>{};
|
||||
static auto constexpr DIFFICULTY = PatternGenerator<Instruction::DIFFICULTY>{};
|
||||
static auto constexpr GASLIMIT = PatternGenerator<Instruction::GASLIMIT>{};
|
||||
static auto constexpr CHAINID = PatternGenerator<Instruction::CHAINID>{};
|
||||
static auto constexpr SELFBALANCE = PatternGenerator<Instruction::SELFBALANCE>{};
|
||||
static auto constexpr POP = PatternGenerator<Instruction::POP>{};
|
||||
static auto constexpr MLOAD = PatternGenerator<Instruction::MLOAD>{};
|
||||
static auto constexpr MSTORE = PatternGenerator<Instruction::MSTORE>{};
|
||||
static auto constexpr MSTORE8 = PatternGenerator<Instruction::MSTORE8>{};
|
||||
static auto constexpr SLOAD = PatternGenerator<Instruction::SLOAD>{};
|
||||
static auto constexpr SSTORE = PatternGenerator<Instruction::SSTORE>{};
|
||||
static auto constexpr PC = PatternGenerator<Instruction::PC>{};
|
||||
static auto constexpr MSIZE = PatternGenerator<Instruction::MSIZE>{};
|
||||
static auto constexpr GAS = PatternGenerator<Instruction::GAS>{};
|
||||
static auto constexpr LOG0 = PatternGenerator<Instruction::LOG0>{};
|
||||
static auto constexpr LOG1 = PatternGenerator<Instruction::LOG1>{};
|
||||
static auto constexpr LOG2 = PatternGenerator<Instruction::LOG2>{};
|
||||
static auto constexpr LOG3 = PatternGenerator<Instruction::LOG3>{};
|
||||
static auto constexpr LOG4 = PatternGenerator<Instruction::LOG4>{};
|
||||
static auto constexpr CREATE = PatternGenerator<Instruction::CREATE>{};
|
||||
static auto constexpr CALL = PatternGenerator<Instruction::CALL>{};
|
||||
static auto constexpr CALLCODE = PatternGenerator<Instruction::CALLCODE>{};
|
||||
static auto constexpr STATICCALL = PatternGenerator<Instruction::STATICCALL>{};
|
||||
static auto constexpr RETURN = PatternGenerator<Instruction::RETURN>{};
|
||||
static auto constexpr DELEGATECALL = PatternGenerator<Instruction::DELEGATECALL>{};
|
||||
static auto constexpr CREATE2 = PatternGenerator<Instruction::CREATE2>{};
|
||||
static auto constexpr REVERT = PatternGenerator<Instruction::REVERT>{};
|
||||
static auto constexpr INVALID = PatternGenerator<Instruction::INVALID>{};
|
||||
static auto constexpr SELFDESTRUCT = PatternGenerator<Instruction::SELFDESTRUCT>{};
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ Rules::Rules()
|
||||
assertThrow(isInitialized(), OptimizerException, "Rule list not properly initialized.");
|
||||
}
|
||||
|
||||
Pattern::Pattern(Instruction _instruction, std::vector<Pattern> const& _arguments):
|
||||
Pattern::Pattern(Instruction _instruction, std::initializer_list<Pattern> _arguments):
|
||||
m_type(Operation),
|
||||
m_instruction(_instruction),
|
||||
m_arguments(_arguments)
|
||||
|
@ -26,6 +26,8 @@
|
||||
#include <libevmasm/ExpressionClasses.h>
|
||||
#include <libevmasm/SimplificationRule.h>
|
||||
|
||||
#include <libdevcore/CommonData.h>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include <functional>
|
||||
@ -87,18 +89,20 @@ public:
|
||||
using Expression = ExpressionClasses::Expression;
|
||||
using Id = ExpressionClasses::Id;
|
||||
|
||||
using Builtins = dev::eth::EVMBuiltins;
|
||||
using Builtins = dev::eth::EVMBuiltins<Pattern>;
|
||||
static constexpr size_t WordSize = 256;
|
||||
using Word = u256;
|
||||
|
||||
// Matches a specific constant value.
|
||||
Pattern(unsigned _value): Pattern(u256(_value)) {}
|
||||
Pattern(int _value): Pattern(u256(_value)) {}
|
||||
Pattern(long unsigned _value): Pattern(u256(_value)) {}
|
||||
// Matches a specific constant value.
|
||||
Pattern(u256 const& _value): m_type(Push), m_requireDataMatch(true), m_data(std::make_shared<u256>(_value)) {}
|
||||
// Matches a specific assembly item type or anything if not given.
|
||||
Pattern(AssemblyItemType _type = UndefinedItem): m_type(_type) {}
|
||||
// Matches a given instruction with given arguments
|
||||
Pattern(Instruction _instruction, std::vector<Pattern> const& _arguments = {});
|
||||
Pattern(Instruction _instruction, std::initializer_list<Pattern> _arguments = {});
|
||||
/// Sets this pattern to be part of the match group with the identifier @a _group.
|
||||
/// Inside one rule, all patterns in the same match group have to match expressions from the
|
||||
/// same expression equivalence class.
|
||||
|
@ -209,17 +209,14 @@ void SMTEncoder::endVisit(VariableDeclarationStatement const& _varDecl)
|
||||
solAssert(symbTuple, "");
|
||||
auto const& components = symbTuple->components();
|
||||
auto const& declarations = _varDecl.declarations();
|
||||
if (!components.empty())
|
||||
{
|
||||
solAssert(components.size() == declarations.size(), "");
|
||||
for (unsigned i = 0; i < declarations.size(); ++i)
|
||||
if (
|
||||
components.at(i) &&
|
||||
declarations.at(i) &&
|
||||
m_context.knownVariable(*declarations.at(i))
|
||||
)
|
||||
assignment(*declarations.at(i), components.at(i)->currentValue(declarations.at(i)->type()));
|
||||
}
|
||||
solAssert(components.size() == declarations.size(), "");
|
||||
for (unsigned i = 0; i < declarations.size(); ++i)
|
||||
if (
|
||||
components.at(i) &&
|
||||
declarations.at(i) &&
|
||||
m_context.knownVariable(*declarations.at(i))
|
||||
)
|
||||
assignment(*declarations.at(i), components.at(i)->currentValue(declarations.at(i)->type()));
|
||||
}
|
||||
}
|
||||
else if (m_context.knownVariable(*_varDecl.declarations().front()))
|
||||
@ -321,24 +318,23 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple)
|
||||
{
|
||||
auto const& symbTuple = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(_tuple));
|
||||
solAssert(symbTuple, "");
|
||||
if (symbTuple->components().empty())
|
||||
auto const& symbComponents = symbTuple->components();
|
||||
auto const& tupleComponents = _tuple.components();
|
||||
solAssert(symbComponents.size() == _tuple.components().size(), "");
|
||||
for (unsigned i = 0; i < symbComponents.size(); ++i)
|
||||
{
|
||||
vector<shared_ptr<smt::SymbolicVariable>> components;
|
||||
for (auto const& component: _tuple.components())
|
||||
if (component)
|
||||
{
|
||||
if (auto varDecl = identifierToVariable(*component))
|
||||
components.push_back(m_context.variable(*varDecl));
|
||||
else
|
||||
{
|
||||
solAssert(m_context.knownExpression(*component), "");
|
||||
components.push_back(m_context.expression(*component));
|
||||
}
|
||||
}
|
||||
auto sComponent = symbComponents.at(i);
|
||||
auto tComponent = tupleComponents.at(i);
|
||||
if (sComponent && tComponent)
|
||||
{
|
||||
if (auto varDecl = identifierToVariable(*tComponent))
|
||||
m_context.addAssertion(sComponent->currentValue() == currentValue(*varDecl));
|
||||
else
|
||||
components.push_back(nullptr);
|
||||
solAssert(components.size() == _tuple.components().size(), "");
|
||||
symbTuple->setComponents(move(components));
|
||||
{
|
||||
solAssert(m_context.knownExpression(*tComponent), "");
|
||||
m_context.addAssertion(sComponent->currentValue() == expr(*tComponent));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -610,12 +606,22 @@ void SMTEncoder::endVisit(Identifier const& _identifier)
|
||||
defineExpr(_identifier, m_context.thisAddress());
|
||||
m_uninterpretedTerms.insert(&_identifier);
|
||||
}
|
||||
else if (smt::isSupportedType(_identifier.annotation().type->category()))
|
||||
// TODO: handle MagicVariableDeclaration here
|
||||
m_errorReporter.warning(
|
||||
_identifier.location(),
|
||||
"Assertion checker does not yet support the type of this variable."
|
||||
);
|
||||
else if (
|
||||
_identifier.annotation().type->category() != Type::Category::Modifier
|
||||
)
|
||||
createExpr(_identifier);
|
||||
}
|
||||
|
||||
void SMTEncoder::endVisit(ElementaryTypeNameExpression const& _typeName)
|
||||
{
|
||||
auto const& typeType = dynamic_cast<TypeType const&>(*_typeName.annotation().type);
|
||||
auto result = smt::newSymbolicVariable(
|
||||
*TypeProvider::uint256(),
|
||||
typeType.actualType()->toString(false),
|
||||
m_context
|
||||
);
|
||||
solAssert(!result.first && result.second, "");
|
||||
m_context.createExpression(_typeName, result.second);
|
||||
}
|
||||
|
||||
void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall)
|
||||
@ -1513,15 +1519,18 @@ void SMTEncoder::createReturnedExpressions(FunctionCall const& _funCall)
|
||||
{
|
||||
auto const& symbTuple = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(_funCall));
|
||||
solAssert(symbTuple, "");
|
||||
if (symbTuple->components().empty())
|
||||
auto const& symbComponents = symbTuple->components();
|
||||
solAssert(symbComponents.size() == returnParams.size(), "");
|
||||
for (unsigned i = 0; i < symbComponents.size(); ++i)
|
||||
{
|
||||
vector<shared_ptr<smt::SymbolicVariable>> components;
|
||||
for (auto param: returnParams)
|
||||
auto sComponent = symbComponents.at(i);
|
||||
auto param = returnParams.at(i);
|
||||
solAssert(param, "");
|
||||
if (sComponent)
|
||||
{
|
||||
solAssert(m_context.knownVariable(*param), "");
|
||||
components.push_back(m_context.variable(*param));
|
||||
m_context.addAssertion(sComponent->currentValue() == currentValue(*param));
|
||||
}
|
||||
symbTuple->setComponents(move(components));
|
||||
}
|
||||
}
|
||||
else if (returnParams.size() == 1)
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <libsolidity/formal/SymbolicVariables.h>
|
||||
#include <libsolidity/formal/VariableUsage.h>
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/ast/ASTVisitor.h>
|
||||
#include <libsolidity/interface/ReadFile.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
@ -82,6 +83,7 @@ protected:
|
||||
void endVisit(BinaryOperation const& _node) override;
|
||||
void endVisit(FunctionCall const& _node) override;
|
||||
void endVisit(Identifier const& _node) override;
|
||||
void endVisit(ElementaryTypeNameExpression const& _node) override;
|
||||
void endVisit(Literal const& _node) override;
|
||||
void endVisit(Return const& _node) override;
|
||||
bool visit(MemberAccess const& _node) override;
|
||||
|
@ -248,12 +248,16 @@ SymbolicTupleVariable::SymbolicTupleVariable(
|
||||
SymbolicVariable(_type, _type, move(_uniqueName), _context)
|
||||
{
|
||||
solAssert(isTuple(m_type->category()), "");
|
||||
}
|
||||
|
||||
void SymbolicTupleVariable::setComponents(vector<shared_ptr<SymbolicVariable>> _components)
|
||||
{
|
||||
solAssert(m_components.empty(), "");
|
||||
auto const& tupleType = dynamic_cast<solidity::TupleType const*>(m_type);
|
||||
solAssert(_components.size() == tupleType->components().size(), "");
|
||||
m_components = move(_components);
|
||||
auto const& tupleType = dynamic_cast<TupleType const&>(*m_type);
|
||||
auto const& componentsTypes = tupleType.components();
|
||||
for (unsigned i = 0; i < componentsTypes.size(); ++i)
|
||||
if (componentsTypes.at(i))
|
||||
{
|
||||
string componentName = m_uniqueName + "_component_" + to_string(i);
|
||||
auto result = smt::newSymbolicVariable(*componentsTypes.at(i), componentName, m_context);
|
||||
solAssert(result.second, "");
|
||||
m_components.emplace_back(move(result.second));
|
||||
}
|
||||
else
|
||||
m_components.emplace_back(nullptr);
|
||||
}
|
||||
|
@ -250,8 +250,6 @@ public:
|
||||
return m_components;
|
||||
}
|
||||
|
||||
void setComponents(std::vector<std::shared_ptr<SymbolicVariable>> _components);
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<SymbolicVariable>> m_components;
|
||||
};
|
||||
|
@ -111,7 +111,7 @@ SimplificationRules::SimplificationRules()
|
||||
assertThrow(isInitialized(), OptimizerException, "Rule list not properly initialized.");
|
||||
}
|
||||
|
||||
yul::Pattern::Pattern(dev::eth::Instruction _instruction, vector<Pattern> const& _arguments):
|
||||
yul::Pattern::Pattern(dev::eth::Instruction _instruction, initializer_list<Pattern> _arguments):
|
||||
m_kind(PatternKind::Operation),
|
||||
m_instruction(_instruction),
|
||||
m_arguments(_arguments)
|
||||
|
@ -25,6 +25,8 @@
|
||||
#include <libyul/AsmDataForward.h>
|
||||
#include <libyul/AsmData.h>
|
||||
|
||||
#include <libdevcore/CommonData.h>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include <functional>
|
||||
@ -85,7 +87,7 @@ enum class PatternKind
|
||||
class Pattern
|
||||
{
|
||||
public:
|
||||
using Builtins = dev::eth::EVMBuiltins;
|
||||
using Builtins = dev::eth::EVMBuiltins<Pattern>;
|
||||
static constexpr size_t WordSize = 256;
|
||||
using Word = dev::u256;
|
||||
|
||||
@ -93,10 +95,12 @@ public:
|
||||
Pattern(PatternKind _kind = PatternKind::Any): m_kind(_kind) {}
|
||||
// Matches a specific constant value.
|
||||
Pattern(unsigned _value): Pattern(dev::u256(_value)) {}
|
||||
Pattern(int _value): Pattern(dev::u256(_value)) {}
|
||||
Pattern(long unsigned _value): Pattern(dev::u256(_value)) {}
|
||||
// Matches a specific constant value.
|
||||
Pattern(dev::u256 const& _value): m_kind(PatternKind::Constant), m_data(std::make_shared<dev::u256>(_value)) {}
|
||||
// Matches a given instruction with given arguments
|
||||
Pattern(dev::eth::Instruction _instruction, std::vector<Pattern> const& _arguments = {});
|
||||
Pattern(dev::eth::Instruction _instruction, std::initializer_list<Pattern> _arguments = {});
|
||||
/// Sets this pattern to be part of the match group with the identifier @a _group.
|
||||
/// Inside one rule, all patterns in the same match group have to match expressions from the
|
||||
/// same expression equivalence class.
|
||||
|
@ -35,17 +35,17 @@ using namespace dev;
|
||||
using namespace dev::test;
|
||||
|
||||
|
||||
evmc::vm* EVMHost::getVM(string const& _path)
|
||||
evmc::VM* EVMHost::getVM(string const& _path)
|
||||
{
|
||||
static unique_ptr<evmc::vm> theVM;
|
||||
static unique_ptr<evmc::VM> theVM;
|
||||
if (!theVM && !_path.empty())
|
||||
{
|
||||
evmc_loader_error_code errorCode = {};
|
||||
evmc_instance* vm = evmc_load_and_configure(_path.c_str(), &errorCode);
|
||||
evmc_vm* vm = evmc_load_and_configure(_path.c_str(), &errorCode);
|
||||
if (vm && errorCode == EVMC_LOADER_SUCCESS)
|
||||
{
|
||||
if (evmc_vm_has_capability(vm, EVMC_CAPABILITY_EVM1))
|
||||
theVM = make_unique<evmc::vm>(vm);
|
||||
theVM = make_unique<evmc::VM>(vm);
|
||||
else
|
||||
{
|
||||
evmc_destroy(vm);
|
||||
@ -63,7 +63,7 @@ evmc::vm* EVMHost::getVM(string const& _path)
|
||||
return theVM.get();
|
||||
}
|
||||
|
||||
EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::vm* _vm):
|
||||
EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM* _vm):
|
||||
m_vm(_vm)
|
||||
{
|
||||
if (!m_vm)
|
||||
@ -83,7 +83,7 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::vm* _vm):
|
||||
else if (_evmVersion == langutil::EVMVersion::constantinople())
|
||||
m_evmVersion = EVMC_CONSTANTINOPLE;
|
||||
else if (_evmVersion == langutil::EVMVersion::istanbul())
|
||||
assertThrow(false, Exception, "Istanbul is not supported yet.");
|
||||
m_evmVersion = EVMC_ISTANBUL;
|
||||
else if (_evmVersion == langutil::EVMVersion::berlin())
|
||||
assertThrow(false, Exception, "Berlin is not supported yet.");
|
||||
else //if (_evmVersion == langutil::EVMVersion::petersburg())
|
||||
@ -227,6 +227,8 @@ evmc_tx_context EVMHost::get_tx_context() noexcept
|
||||
ctx.block_gas_limit = 20000000;
|
||||
ctx.tx_gas_price = convertToEVMC(u256("3000000000"));
|
||||
ctx.tx_origin = convertToEVMC(Address("0x9292929292929292929292929292929292929292"));
|
||||
// Mainnet according to EIP-155
|
||||
ctx.chain_id = convertToEVMC(u256(1));
|
||||
return ctx;
|
||||
}
|
||||
|
||||
|
@ -40,9 +40,9 @@ public:
|
||||
/// 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.
|
||||
static evmc::vm* getVM(std::string const& _path = {});
|
||||
static evmc::VM* getVM(std::string const& _path = {});
|
||||
|
||||
explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::vm* _vm = getVM());
|
||||
explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::VM* _vm = getVM());
|
||||
|
||||
struct Account
|
||||
{
|
||||
@ -179,7 +179,7 @@ private:
|
||||
/// @note The return value is only valid as long as @a _data is alive!
|
||||
static evmc::result resultWithGas(evmc_message const& _message, bytes const& _data) noexcept;
|
||||
|
||||
evmc::vm* m_vm = nullptr;
|
||||
evmc::VM* m_vm = nullptr;
|
||||
evmc_revision m_evmVersion;
|
||||
};
|
||||
|
||||
|
291
test/evmc/evmc.h
291
test/evmc/evmc.h
@ -44,7 +44,7 @@ enum
|
||||
*
|
||||
* @see @ref versioning
|
||||
*/
|
||||
EVMC_ABI_VERSION = 6
|
||||
EVMC_ABI_VERSION = 7
|
||||
};
|
||||
|
||||
|
||||
@ -153,9 +153,15 @@ struct evmc_tx_context
|
||||
int64_t block_timestamp; /**< The block timestamp. */
|
||||
int64_t block_gas_limit; /**< The block gas limit. */
|
||||
evmc_uint256be block_difficulty; /**< The block difficulty. */
|
||||
evmc_uint256be chain_id; /**< The blockchain's ChainID. */
|
||||
};
|
||||
|
||||
struct evmc_context;
|
||||
/**
|
||||
* @struct evmc_host_context
|
||||
* The opaque data type representing the Host execution context.
|
||||
* @see evmc_execute_fn().
|
||||
*/
|
||||
struct evmc_host_context;
|
||||
|
||||
/**
|
||||
* Get transaction context callback function.
|
||||
@ -166,7 +172,7 @@ struct evmc_context;
|
||||
* @param context The pointer to the Host execution context.
|
||||
* @return The transaction context.
|
||||
*/
|
||||
typedef struct evmc_tx_context (*evmc_get_tx_context_fn)(struct evmc_context* context);
|
||||
typedef struct evmc_tx_context (*evmc_get_tx_context_fn)(struct evmc_host_context* context);
|
||||
|
||||
/**
|
||||
* Get block hash callback function.
|
||||
@ -180,7 +186,7 @@ typedef struct evmc_tx_context (*evmc_get_tx_context_fn)(struct evmc_context* co
|
||||
* @return The block hash or null bytes
|
||||
* if the information about the block is not available.
|
||||
*/
|
||||
typedef evmc_bytes32 (*evmc_get_block_hash_fn)(struct evmc_context* context, int64_t number);
|
||||
typedef evmc_bytes32 (*evmc_get_block_hash_fn)(struct evmc_host_context* context, int64_t number);
|
||||
|
||||
/**
|
||||
* The execution status code.
|
||||
@ -420,7 +426,8 @@ struct evmc_result
|
||||
* @param address The address of the account the query is about.
|
||||
* @return true if exists, false otherwise.
|
||||
*/
|
||||
typedef bool (*evmc_account_exists_fn)(struct evmc_context* context, const evmc_address* address);
|
||||
typedef bool (*evmc_account_exists_fn)(struct evmc_host_context* context,
|
||||
const evmc_address* address);
|
||||
|
||||
/**
|
||||
* Get storage callback function.
|
||||
@ -433,7 +440,7 @@ typedef bool (*evmc_account_exists_fn)(struct evmc_context* context, const evmc_
|
||||
* @return The storage value at the given storage key or null bytes
|
||||
* if the account does not exist.
|
||||
*/
|
||||
typedef evmc_bytes32 (*evmc_get_storage_fn)(struct evmc_context* context,
|
||||
typedef evmc_bytes32 (*evmc_get_storage_fn)(struct evmc_host_context* context,
|
||||
const evmc_address* address,
|
||||
const evmc_bytes32* key);
|
||||
|
||||
@ -492,7 +499,7 @@ enum evmc_storage_status
|
||||
* @param value The value to be stored.
|
||||
* @return The effect on the storage item.
|
||||
*/
|
||||
typedef enum evmc_storage_status (*evmc_set_storage_fn)(struct evmc_context* context,
|
||||
typedef enum evmc_storage_status (*evmc_set_storage_fn)(struct evmc_host_context* context,
|
||||
const evmc_address* address,
|
||||
const evmc_bytes32* key,
|
||||
const evmc_bytes32* value);
|
||||
@ -506,7 +513,7 @@ typedef enum evmc_storage_status (*evmc_set_storage_fn)(struct evmc_context* con
|
||||
* @param address The address of the account.
|
||||
* @return The balance of the given account or 0 if the account does not exist.
|
||||
*/
|
||||
typedef evmc_uint256be (*evmc_get_balance_fn)(struct evmc_context* context,
|
||||
typedef evmc_uint256be (*evmc_get_balance_fn)(struct evmc_host_context* context,
|
||||
const evmc_address* address);
|
||||
|
||||
/**
|
||||
@ -519,7 +526,8 @@ typedef evmc_uint256be (*evmc_get_balance_fn)(struct evmc_context* context,
|
||||
* @param address The address of the account.
|
||||
* @return The size of the code in the account or 0 if the account does not exist.
|
||||
*/
|
||||
typedef size_t (*evmc_get_code_size_fn)(struct evmc_context* context, const evmc_address* address);
|
||||
typedef size_t (*evmc_get_code_size_fn)(struct evmc_host_context* context,
|
||||
const evmc_address* address);
|
||||
|
||||
/**
|
||||
* Get code size callback function.
|
||||
@ -532,28 +540,27 @@ typedef size_t (*evmc_get_code_size_fn)(struct evmc_context* context, const evmc
|
||||
* @param address The address of the account.
|
||||
* @return The hash of the code in the account or null bytes if the account does not exist.
|
||||
*/
|
||||
typedef evmc_bytes32 (*evmc_get_code_hash_fn)(struct evmc_context* context,
|
||||
typedef evmc_bytes32 (*evmc_get_code_hash_fn)(struct evmc_host_context* context,
|
||||
const evmc_address* address);
|
||||
|
||||
/**
|
||||
* Copy code callback function.
|
||||
*
|
||||
* This callback function is used by an EVM to request a copy of the code
|
||||
* of the given account to the memory buffer provided by the EVM.
|
||||
* The Client MUST copy the requested code, starting with the given offset,
|
||||
* to the provided memory buffer up to the size of the buffer or the size of
|
||||
* the code, whichever is smaller.
|
||||
* This callback function is used by an EVM to request a copy of the code
|
||||
* of the given account to the memory buffer provided by the EVM.
|
||||
* The Client MUST copy the requested code, starting with the given offset,
|
||||
* to the provided memory buffer up to the size of the buffer or the size of
|
||||
* the code, whichever is smaller.
|
||||
*
|
||||
* @param context The pointer to the Client execution context.
|
||||
* @see ::evmc_context.
|
||||
* @param address The address of the account.
|
||||
* @param code_offset The offset of the code to copy.
|
||||
* @param buffer_data The pointer to the memory buffer allocated by the EVM
|
||||
* to store a copy of the requested code.
|
||||
* @param buffer_size The size of the memory buffer.
|
||||
* @return The number of bytes copied to the buffer by the Client.
|
||||
* @param context The pointer to the Host execution context. See ::evmc_host_context.
|
||||
* @param address The address of the account.
|
||||
* @param code_offset The offset of the code to copy.
|
||||
* @param buffer_data The pointer to the memory buffer allocated by the EVM
|
||||
* to store a copy of the requested code.
|
||||
* @param buffer_size The size of the memory buffer.
|
||||
* @return The number of bytes copied to the buffer by the Client.
|
||||
*/
|
||||
typedef size_t (*evmc_copy_code_fn)(struct evmc_context* context,
|
||||
typedef size_t (*evmc_copy_code_fn)(struct evmc_host_context* context,
|
||||
const evmc_address* address,
|
||||
size_t code_offset,
|
||||
uint8_t* buffer_data,
|
||||
@ -562,34 +569,31 @@ typedef size_t (*evmc_copy_code_fn)(struct evmc_context* context,
|
||||
/**
|
||||
* Selfdestruct callback function.
|
||||
*
|
||||
* This callback function is used by an EVM to SELFDESTRUCT given contract.
|
||||
* The execution of the contract will not be stopped, that is up to the EVM.
|
||||
* This callback function is used by an EVM to SELFDESTRUCT given contract.
|
||||
* The execution of the contract will not be stopped, that is up to the EVM.
|
||||
*
|
||||
* @param context The pointer to the Host execution context.
|
||||
* @see ::evmc_context.
|
||||
* @param address The address of the contract to be selfdestructed.
|
||||
* @param beneficiary The address where the remaining ETH is going to be
|
||||
* transferred.
|
||||
* @param context The pointer to the Host execution context. See ::evmc_host_context.
|
||||
* @param address The address of the contract to be selfdestructed.
|
||||
* @param beneficiary The address where the remaining ETH is going to be transferred.
|
||||
*/
|
||||
typedef void (*evmc_selfdestruct_fn)(struct evmc_context* context,
|
||||
typedef void (*evmc_selfdestruct_fn)(struct evmc_host_context* context,
|
||||
const evmc_address* address,
|
||||
const evmc_address* beneficiary);
|
||||
|
||||
/**
|
||||
* Log callback function.
|
||||
*
|
||||
* This callback function is used by an EVM to inform about a LOG that happened
|
||||
* during an EVM bytecode execution.
|
||||
* @param context The pointer to the Host execution context.
|
||||
* @see ::evmc_context.
|
||||
* @param address The address of the contract that generated the log.
|
||||
* @param data The pointer to unindexed data attached to the log.
|
||||
* @param data_size The length of the data.
|
||||
* @param topics The pointer to the array of topics attached to the log.
|
||||
* @param topics_count The number of the topics. Valid values are between
|
||||
* 0 and 4 inclusively.
|
||||
* This callback function is used by an EVM to inform about a LOG that happened
|
||||
* during an EVM bytecode execution.
|
||||
*
|
||||
* @param context The pointer to the Host execution context. See ::evmc_host_context.
|
||||
* @param address The address of the contract that generated the log.
|
||||
* @param data The pointer to unindexed data attached to the log.
|
||||
* @param data_size The length of the data.
|
||||
* @param topics The pointer to the array of topics attached to the log.
|
||||
* @param topics_count The number of the topics. Valid values are between 0 and 4 inclusively.
|
||||
*/
|
||||
typedef void (*evmc_emit_log_fn)(struct evmc_context* context,
|
||||
typedef void (*evmc_emit_log_fn)(struct evmc_host_context* context,
|
||||
const evmc_address* address,
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
@ -599,11 +603,11 @@ typedef void (*evmc_emit_log_fn)(struct evmc_context* context,
|
||||
/**
|
||||
* Pointer to the callback function supporting EVM calls.
|
||||
*
|
||||
* @param context The pointer to the Host execution context.
|
||||
* @param msg The call parameters.
|
||||
* @param context The pointer to the Host execution context.
|
||||
* @param msg The call parameters.
|
||||
* @return The result of the call.
|
||||
*/
|
||||
typedef struct evmc_result (*evmc_call_fn)(struct evmc_context* context,
|
||||
typedef struct evmc_result (*evmc_call_fn)(struct evmc_host_context* context,
|
||||
const struct evmc_message* msg);
|
||||
|
||||
/**
|
||||
@ -654,31 +658,15 @@ struct evmc_host_interface
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Execution context managed by the Host.
|
||||
*
|
||||
* The Host MUST pass the pointer to the execution context to ::evmc_execute_fn.
|
||||
* The VM MUST pass the same pointer back to the Host in every callback function.
|
||||
* The context MUST contain at least the function table defining
|
||||
* the context callback interface.
|
||||
* Optionally, the Host MAY include in the context additional data.
|
||||
*/
|
||||
struct evmc_context
|
||||
{
|
||||
/** The Host interface. */
|
||||
const struct evmc_host_interface* host;
|
||||
};
|
||||
|
||||
|
||||
/* Forward declaration. */
|
||||
struct evmc_instance;
|
||||
struct evmc_vm;
|
||||
|
||||
/**
|
||||
* Destroys the EVM instance.
|
||||
* Destroys the VM instance.
|
||||
*
|
||||
* @param evm The EVM instance to be destroyed.
|
||||
* @param vm The VM instance to be destroyed.
|
||||
*/
|
||||
typedef void (*evmc_destroy_fn)(struct evmc_instance* evm);
|
||||
typedef void (*evmc_destroy_fn)(struct evmc_vm* vm);
|
||||
|
||||
/**
|
||||
* Possible outcomes of evmc_set_option.
|
||||
@ -691,19 +679,19 @@ enum evmc_set_option_result
|
||||
};
|
||||
|
||||
/**
|
||||
* Configures the EVM instance.
|
||||
* Configures the VM instance.
|
||||
*
|
||||
* Allows modifying options of the EVM instance.
|
||||
* Options:
|
||||
* - code cache behavior: on, off, read-only, ...
|
||||
* - optimizations,
|
||||
* Allows modifying options of the VM instance.
|
||||
* Options:
|
||||
* - code cache behavior: on, off, read-only, ...
|
||||
* - optimizations,
|
||||
*
|
||||
* @param evm The EVM instance to be configured.
|
||||
* @param name The option name. NULL-terminated string. Cannot be NULL.
|
||||
* @param value The new option value. NULL-terminated string. Cannot be NULL.
|
||||
* @return The outcome of the operation.
|
||||
* @param vm The VM instance to be configured.
|
||||
* @param name The option name. NULL-terminated string. Cannot be NULL.
|
||||
* @param value The new option value. NULL-terminated string. Cannot be NULL.
|
||||
* @return The outcome of the operation.
|
||||
*/
|
||||
typedef enum evmc_set_option_result (*evmc_set_option_fn)(struct evmc_instance* evm,
|
||||
typedef enum evmc_set_option_result (*evmc_set_option_fn)(struct evmc_vm* vm,
|
||||
char const* name,
|
||||
char const* value);
|
||||
|
||||
@ -762,6 +750,7 @@ enum evmc_revision
|
||||
* The Petersburg revision.
|
||||
*
|
||||
* Other names: Constantinople2, ConstantinopleFix.
|
||||
*
|
||||
* https://eips.ethereum.org/EIPS/eip-1716
|
||||
*/
|
||||
EVMC_PETERSBURG = 6,
|
||||
@ -773,23 +762,15 @@ enum evmc_revision
|
||||
*/
|
||||
EVMC_ISTANBUL = 7,
|
||||
|
||||
/**
|
||||
* The Berlin revision.
|
||||
*
|
||||
* The spec draft: https://eips.ethereum.org/EIPS/eip-2070.
|
||||
*/
|
||||
EVMC_BERLIN = 8,
|
||||
|
||||
/** The maximum EVM revision supported. */
|
||||
EVMC_MAX_REVISION = EVMC_ISTANBUL,
|
||||
|
||||
|
||||
/**
|
||||
* Reserved for the post-Constantinople upgrade.
|
||||
*
|
||||
* @deprecated Replaced with ::EVMC_PETERSBURG.
|
||||
*/
|
||||
EVMC_CONSTANTINOPLE2 EVMC_DEPRECATED = EVMC_PETERSBURG,
|
||||
|
||||
/**
|
||||
* The latests EVM revision supported.
|
||||
*
|
||||
* @deprecated Replaced with ::EVMC_MAX_REVISION.
|
||||
*/
|
||||
EVMC_LATEST_REVISION EVMC_DEPRECATED = EVMC_MAX_REVISION
|
||||
EVMC_MAX_REVISION = EVMC_BERLIN
|
||||
};
|
||||
|
||||
|
||||
@ -798,19 +779,22 @@ enum evmc_revision
|
||||
*
|
||||
* This function MAY be invoked multiple times for a single VM instance.
|
||||
*
|
||||
* @param instance The VM instance. This argument MUST NOT be NULL.
|
||||
* @param context The pointer to the Host execution context to be passed
|
||||
* to the Host interface methods (::evmc_host_interface).
|
||||
* This argument MUST NOT be NULL unless
|
||||
* the @p instance has the ::EVMC_CAPABILITY_PRECOMPILES capability.
|
||||
* @param vm The VM instance. This argument MUST NOT be NULL.
|
||||
* @param host The Host interface. This argument MUST NOT be NULL unless
|
||||
* the @p vm has the ::EVMC_CAPABILITY_PRECOMPILES capability.
|
||||
* @param context The opaque pointer to the Host execution context.
|
||||
* This argument MAY be NULL. The VM MUST pass the same
|
||||
* pointer to the methods of the @p host interface.
|
||||
* The VM MUST NOT dereference the pointer.
|
||||
* @param rev The requested EVM specification revision.
|
||||
* @param msg The call parameters. See ::evmc_message. This argument MUST NOT be NULL.
|
||||
* @param code The reference to the code to be executed. This argument MAY be NULL.
|
||||
* @param code_size The length of the code. If @p code is NULL this argument MUST be 0.
|
||||
* @return The execution result.
|
||||
*/
|
||||
typedef struct evmc_result (*evmc_execute_fn)(struct evmc_instance* instance,
|
||||
struct evmc_context* context,
|
||||
typedef struct evmc_result (*evmc_execute_fn)(struct evmc_vm* vm,
|
||||
const struct evmc_host_interface* host,
|
||||
struct evmc_host_context* context,
|
||||
enum evmc_revision rev,
|
||||
const struct evmc_message* msg,
|
||||
uint8_t const* code,
|
||||
@ -855,98 +839,20 @@ typedef uint32_t evmc_capabilities_flagset;
|
||||
* Return the supported capabilities of the VM instance.
|
||||
*
|
||||
* This function MAY be invoked multiple times for a single VM instance,
|
||||
* and its value MAY be influenced by calls to evmc_instance::set_option.
|
||||
* and its value MAY be influenced by calls to evmc_vm::set_option.
|
||||
*
|
||||
* @param instance The EVM instance.
|
||||
* @return The supported capabilities of the VM. @see evmc_capabilities.
|
||||
* @param vm The VM instance.
|
||||
* @return The supported capabilities of the VM. @see evmc_capabilities.
|
||||
*/
|
||||
typedef evmc_capabilities_flagset (*evmc_get_capabilities_fn)(struct evmc_instance* instance);
|
||||
|
||||
/**
|
||||
* The opaque type representing a Client-side tracer object.
|
||||
*
|
||||
* @deprecated Deprecated since EVMC 6.3, see evmc_instance::set_tracer().
|
||||
*/
|
||||
struct evmc_tracer_context;
|
||||
|
||||
/**
|
||||
* The callback to trace instructions execution in an EVM.
|
||||
*
|
||||
* This function informs the Client what instruction has been executed in the EVM implementation
|
||||
* and what are the results of executing this particular instruction.
|
||||
* The message level information (like call depth, destination address, etc.) are not provided here.
|
||||
* This piece of information can be acquired by inspecting messages being sent to the EVM in
|
||||
* ::evmc_execute_fn and the results of the messages execution.
|
||||
*
|
||||
* @deprecated Deprecated since EVMC 6.3, see evmc_instance::set_tracer().
|
||||
*
|
||||
* @param context The pointer to the Client-side tracing context. This allows to
|
||||
* implement the tracer in OOP manner.
|
||||
* @param code_offset The current instruction position in the code.
|
||||
* @param status_code The status code of the instruction execution.
|
||||
* @param gas_left The amount of the gas left after the instruction execution.
|
||||
* @param stack_num_items The current EVM stack height after the instruction execution.
|
||||
* @param pushed_stack_item The top EVM stack item pushed as the result of the instruction
|
||||
* execution. This value is null when the instruction does not push
|
||||
* anything to the stack.
|
||||
* @param memory_size The size of the EVM memory after the instruction execution.
|
||||
* @param changed_memory_offset The offset in number of bytes of the beginning of the memory area
|
||||
* modified as the result of the instruction execution.
|
||||
* The Client MAY use this information together with
|
||||
* @p changed_memory_size and @p changed_memory to incrementally
|
||||
* update the copy of the full VM's memory.
|
||||
* @param changed_memory_size The size of the memory area modified as the result of
|
||||
* the instruction execution.
|
||||
* @param changed_memory The pointer to the memory area modified as the result of
|
||||
* the instruction execution.
|
||||
* The Client MAY access the pointed memory area
|
||||
* (limited by the @p changed_memory_size) only during the current
|
||||
* execution of the evmc_trace_callback().
|
||||
* The pointer MUST NOT be stored by the Client.
|
||||
* The Client MUST NOT assume that
|
||||
* `changed_memory - changed_memory_offset` is a valid base pointer
|
||||
* of the VM memory.
|
||||
*/
|
||||
typedef void (*evmc_trace_callback)(struct evmc_tracer_context* context,
|
||||
size_t code_offset,
|
||||
enum evmc_status_code status_code,
|
||||
int64_t gas_left,
|
||||
size_t stack_num_items,
|
||||
const evmc_uint256be* pushed_stack_item,
|
||||
size_t memory_size,
|
||||
size_t changed_memory_offset,
|
||||
size_t changed_memory_size,
|
||||
const uint8_t* changed_memory);
|
||||
|
||||
/**
|
||||
* Sets the EVM instruction tracer.
|
||||
*
|
||||
* When the tracer is set in the EVM instance, the EVM SHOULD call back the tracer with information
|
||||
* about instructions execution in the EVM.
|
||||
* @see ::evmc_trace_callback.
|
||||
*
|
||||
* This will overwrite the previous settings (the callback and the context).
|
||||
*
|
||||
* @deprecated Deprecated since EVMC 6.3, see evmc_instance::set_tracer().
|
||||
*
|
||||
* @param instance The EVM instance.
|
||||
* @param callback The tracer callback function. This argument MAY be NULL to disable previously
|
||||
* set tracer.
|
||||
* @param context The Client-side tracer context. This argument MAY be NULL in case the tracer
|
||||
* does not require any context. This argument MUST be NULL if the callback
|
||||
* argument is NULL.
|
||||
*/
|
||||
typedef void (*evmc_set_tracer_fn)(struct evmc_instance* instance,
|
||||
evmc_trace_callback callback,
|
||||
struct evmc_tracer_context* context);
|
||||
typedef evmc_capabilities_flagset (*evmc_get_capabilities_fn)(struct evmc_vm* vm);
|
||||
|
||||
|
||||
/**
|
||||
* The EVM instance.
|
||||
* The VM instance.
|
||||
*
|
||||
* Defines the base struct of the VM implementation.
|
||||
*/
|
||||
struct evmc_instance
|
||||
struct evmc_vm
|
||||
{
|
||||
/**
|
||||
* EVMC ABI version implemented by the VM instance.
|
||||
@ -973,14 +879,14 @@ struct evmc_instance
|
||||
const char* version;
|
||||
|
||||
/**
|
||||
* Pointer to function destroying the EVM instance.
|
||||
* Pointer to function destroying the VM instance.
|
||||
*
|
||||
* This is a mandatory method and MUST NOT be set to NULL.
|
||||
*/
|
||||
evmc_destroy_fn destroy;
|
||||
|
||||
/**
|
||||
* Pointer to function executing a code by the EVM instance.
|
||||
* Pointer to function executing a code by the VM instance.
|
||||
*
|
||||
* This is a mandatory method and MUST NOT be set to NULL.
|
||||
*/
|
||||
@ -998,17 +904,6 @@ struct evmc_instance
|
||||
*/
|
||||
evmc_get_capabilities_fn get_capabilities;
|
||||
|
||||
/**
|
||||
* Optional pointer to function setting the EVM instruction tracer.
|
||||
*
|
||||
* If the EVM does not support this feature the pointer can be NULL.
|
||||
*
|
||||
* @deprecated
|
||||
* Since EVMC 6.3, the tracing API has been deprecated as there have been some
|
||||
* design flaws discovered. New API is expected to be introduced in future.
|
||||
*/
|
||||
evmc_set_tracer_fn set_tracer;
|
||||
|
||||
/**
|
||||
* Optional pointer to function modifying VM's options.
|
||||
*
|
||||
@ -1033,9 +928,9 @@ struct evmc_instance
|
||||
* For example, the shared library with the "beta-interpreter" implementation may be named
|
||||
* `libbeta-interpreter.so`.
|
||||
*
|
||||
* @return EVM instance or NULL indicating instance creation failure.
|
||||
* @return The VM instance or NULL indicating instance creation failure.
|
||||
*/
|
||||
struct evmc_instance* evmc_create_example_vm(void);
|
||||
struct evmc_vm* evmc_create_example_vm(void);
|
||||
#endif
|
||||
|
||||
#if __cplusplus
|
||||
|
@ -334,49 +334,46 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/// @copybrief evmc_instance
|
||||
class Host;
|
||||
|
||||
/// @copybrief evmc_vm
|
||||
///
|
||||
/// This is a RAII wrapper for evmc_instance and objects of this type
|
||||
/// This is a RAII wrapper for evmc_vm and objects of this type
|
||||
/// automatically destroys the VM instance.
|
||||
class vm
|
||||
class VM
|
||||
{
|
||||
public:
|
||||
vm() noexcept = default;
|
||||
VM() noexcept = default;
|
||||
|
||||
/// Converting constructor from evmc_instance.
|
||||
explicit vm(evmc_instance* instance) noexcept : m_instance{instance} {}
|
||||
/// Converting constructor from evmc_vm.
|
||||
explicit VM(evmc_vm* vm) noexcept : m_instance{vm} {}
|
||||
|
||||
/// Destructor responsible for automatically destroying the VM instance.
|
||||
~vm() noexcept
|
||||
~VM() noexcept
|
||||
{
|
||||
if (m_instance)
|
||||
m_instance->destroy(m_instance);
|
||||
}
|
||||
|
||||
vm(const vm&) = delete;
|
||||
vm& operator=(const vm&) = delete;
|
||||
VM(const VM&) = delete;
|
||||
VM& operator=(const VM&) = delete;
|
||||
|
||||
/// Move constructor.
|
||||
vm(vm&& other) noexcept : m_instance{other.m_instance} { other.m_instance = nullptr; }
|
||||
VM(VM&& other) noexcept : m_instance{other.m_instance} { other.m_instance = nullptr; }
|
||||
|
||||
/// Move assignment operator.
|
||||
vm& operator=(vm&& other) noexcept
|
||||
VM& operator=(VM&& other) noexcept
|
||||
{
|
||||
this->~vm();
|
||||
this->~VM();
|
||||
m_instance = other.m_instance;
|
||||
other.m_instance = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// The constructor that captures a VM instance and configures the instance
|
||||
/// with provided list of options.
|
||||
vm(evmc_instance* instance,
|
||||
std::initializer_list<std::pair<const char*, const char*>> options) noexcept
|
||||
: m_instance{instance}
|
||||
{
|
||||
for (auto option : options)
|
||||
set_option(option.first, option.second);
|
||||
}
|
||||
/// with the provided list of options.
|
||||
inline VM(evmc_vm* vm,
|
||||
std::initializer_list<std::pair<const char*, const char*>> options) noexcept;
|
||||
|
||||
/// Checks if contains a valid pointer to the VM instance.
|
||||
explicit operator bool() const noexcept { return m_instance != nullptr; }
|
||||
@ -384,13 +381,13 @@ public:
|
||||
/// Checks whenever the VM instance is ABI compatible with the current EVMC API.
|
||||
bool is_abi_compatible() const noexcept { return m_instance->abi_version == EVMC_ABI_VERSION; }
|
||||
|
||||
/// @copydoc evmc_instance::name
|
||||
/// @copydoc evmc_vm::name
|
||||
char const* name() const noexcept { return m_instance->name; }
|
||||
|
||||
/// @copydoc evmc_instance::version
|
||||
/// @copydoc evmc_vm::version
|
||||
char const* version() const noexcept { return m_instance->version; }
|
||||
|
||||
/// @copydoc evmc::instance::get_capabilities
|
||||
/// @copydoc evmc::vm::get_capabilities
|
||||
evmc_capabilities_flagset get_capabilities() const noexcept
|
||||
{
|
||||
return m_instance->get_capabilities(m_instance);
|
||||
@ -403,19 +400,53 @@ public:
|
||||
}
|
||||
|
||||
/// @copydoc evmc_execute()
|
||||
result execute(evmc_context& ctx,
|
||||
result execute(const evmc_host_interface& host,
|
||||
evmc_host_context* ctx,
|
||||
evmc_revision rev,
|
||||
const evmc_message& msg,
|
||||
const uint8_t* code,
|
||||
size_t code_size) noexcept
|
||||
{
|
||||
return result{m_instance->execute(m_instance, &ctx, rev, &msg, code, code_size)};
|
||||
return result{m_instance->execute(m_instance, &host, ctx, rev, &msg, code, code_size)};
|
||||
}
|
||||
|
||||
/// Convenient variant of the VM::execute() that takes reference to evmc::Host class.
|
||||
inline result execute(Host& host,
|
||||
evmc_revision rev,
|
||||
const evmc_message& msg,
|
||||
const uint8_t* code,
|
||||
size_t code_size) noexcept;
|
||||
|
||||
/// Executes code without the Host context.
|
||||
///
|
||||
/// The same as
|
||||
/// execute(const evmc_host_interface&, evmc_host_context*, evmc_revision,
|
||||
/// const evmc_message&, const uint8_t*, size_t),
|
||||
/// but without providing the Host context and interface.
|
||||
/// This method is for experimental precompiles support where execution is
|
||||
/// guaranteed not to require any Host access.
|
||||
result execute(evmc_revision rev,
|
||||
const evmc_message& msg,
|
||||
const uint8_t* code,
|
||||
size_t code_size) noexcept
|
||||
{
|
||||
return result{
|
||||
m_instance->execute(m_instance, nullptr, nullptr, rev, &msg, code, code_size)};
|
||||
}
|
||||
|
||||
private:
|
||||
evmc_instance* m_instance = nullptr;
|
||||
evmc_vm* m_instance = nullptr;
|
||||
};
|
||||
|
||||
inline VM::VM(evmc_vm* vm,
|
||||
std::initializer_list<std::pair<const char*, const char*>> options) noexcept
|
||||
: m_instance{vm}
|
||||
{
|
||||
for (const auto& option : options)
|
||||
set_option(option.first, option.second);
|
||||
}
|
||||
|
||||
|
||||
/// The EVMC Host interface
|
||||
class HostInterface
|
||||
{
|
||||
@ -471,46 +502,52 @@ public:
|
||||
|
||||
/// Wrapper around EVMC host context / host interface.
|
||||
///
|
||||
/// To be used by VM implementations as better alternative to using ::evmc_context directly.
|
||||
/// To be used by VM implementations as better alternative to using ::evmc_host_context directly.
|
||||
class HostContext : public HostInterface
|
||||
{
|
||||
evmc_context* context = nullptr;
|
||||
const evmc_host_interface* host = nullptr;
|
||||
evmc_host_context* context = nullptr;
|
||||
evmc_tx_context tx_context = {};
|
||||
|
||||
public:
|
||||
/// Implicit converting constructor from evmc_context.
|
||||
HostContext(evmc_context* ctx) noexcept : context{ctx} {} // NOLINT
|
||||
/// Default constructor for null Host context.
|
||||
HostContext() = default;
|
||||
|
||||
/// Constructor from the EVMC Host primitives.
|
||||
HostContext(const evmc_host_interface* interface, evmc_host_context* ctx) noexcept
|
||||
: host{interface}, context{ctx}
|
||||
{}
|
||||
|
||||
bool account_exists(const address& address) noexcept final
|
||||
{
|
||||
return context->host->account_exists(context, &address);
|
||||
return host->account_exists(context, &address);
|
||||
}
|
||||
|
||||
bytes32 get_storage(const address& address, const bytes32& key) noexcept final
|
||||
{
|
||||
return context->host->get_storage(context, &address, &key);
|
||||
return host->get_storage(context, &address, &key);
|
||||
}
|
||||
|
||||
evmc_storage_status set_storage(const address& address,
|
||||
const bytes32& key,
|
||||
const bytes32& value) noexcept final
|
||||
{
|
||||
return context->host->set_storage(context, &address, &key, &value);
|
||||
return host->set_storage(context, &address, &key, &value);
|
||||
}
|
||||
|
||||
uint256be get_balance(const address& address) noexcept final
|
||||
{
|
||||
return context->host->get_balance(context, &address);
|
||||
return host->get_balance(context, &address);
|
||||
}
|
||||
|
||||
size_t get_code_size(const address& address) noexcept final
|
||||
{
|
||||
return context->host->get_code_size(context, &address);
|
||||
return host->get_code_size(context, &address);
|
||||
}
|
||||
|
||||
bytes32 get_code_hash(const address& address) noexcept final
|
||||
{
|
||||
return context->host->get_code_hash(context, &address);
|
||||
return host->get_code_hash(context, &address);
|
||||
}
|
||||
|
||||
size_t copy_code(const address& address,
|
||||
@ -518,17 +555,17 @@ public:
|
||||
uint8_t* buffer_data,
|
||||
size_t buffer_size) noexcept final
|
||||
{
|
||||
return context->host->copy_code(context, &address, code_offset, buffer_data, buffer_size);
|
||||
return host->copy_code(context, &address, code_offset, buffer_data, buffer_size);
|
||||
}
|
||||
|
||||
void selfdestruct(const address& addr, const address& beneficiary) noexcept final
|
||||
{
|
||||
context->host->selfdestruct(context, &addr, &beneficiary);
|
||||
host->selfdestruct(context, &addr, &beneficiary);
|
||||
}
|
||||
|
||||
result call(const evmc_message& message) noexcept final
|
||||
{
|
||||
return result{context->host->call(context, &message)};
|
||||
return result{host->call(context, &message)};
|
||||
}
|
||||
|
||||
/// @copydoc HostInterface::get_tx_context()
|
||||
@ -540,13 +577,13 @@ public:
|
||||
evmc_tx_context get_tx_context() noexcept final
|
||||
{
|
||||
if (tx_context.block_timestamp == 0)
|
||||
tx_context = context->host->get_tx_context(context);
|
||||
tx_context = host->get_tx_context(context);
|
||||
return tx_context;
|
||||
}
|
||||
|
||||
bytes32 get_block_hash(int64_t number) noexcept final
|
||||
{
|
||||
return context->host->get_block_hash(context, number);
|
||||
return host->get_block_hash(context, number);
|
||||
}
|
||||
|
||||
void emit_log(const address& addr,
|
||||
@ -555,7 +592,7 @@ public:
|
||||
const bytes32 topics[],
|
||||
size_t topics_count) noexcept final
|
||||
{
|
||||
context->host->emit_log(context, &addr, data, data_size, topics, topics_count);
|
||||
host->emit_log(context, &addr, data, data_size, topics, topics_count);
|
||||
}
|
||||
};
|
||||
|
||||
@ -563,89 +600,135 @@ public:
|
||||
///
|
||||
/// When implementing EVMC Host, you can directly inherit from the evmc::Host class.
|
||||
/// This way your implementation will be simpler by avoiding manual handling
|
||||
/// of the ::evmc_context and the ::evmc_context::host.
|
||||
class Host : public HostInterface, public evmc_context
|
||||
/// of the ::evmc_host_context and the ::evmc_host_interface.
|
||||
class Host : public HostInterface
|
||||
{
|
||||
public:
|
||||
inline Host() noexcept;
|
||||
/// Provides access to the global host interface.
|
||||
/// @returns Reference to the host interface object.
|
||||
static const evmc_host_interface& get_interface() noexcept;
|
||||
|
||||
/// Converts the Host object to the opaque host context pointer.
|
||||
/// @returns Pointer to evmc_host_context.
|
||||
evmc_host_context* to_context() noexcept { return reinterpret_cast<evmc_host_context*>(this); }
|
||||
|
||||
/// Converts the opaque host context pointer back to the original Host object.
|
||||
/// @tparam DerivedClass The class derived from the Host class.
|
||||
/// @param context The opaque host context pointer.
|
||||
/// @returns The pointer to DerivedClass.
|
||||
template <typename DerivedClass = Host>
|
||||
static DerivedClass* from_context(evmc_host_context* context) noexcept
|
||||
{
|
||||
// Get pointer of the Host base class.
|
||||
auto* h = reinterpret_cast<Host*>(context);
|
||||
|
||||
// Additional downcast, only possible if DerivedClass inherits from Host.
|
||||
return static_cast<DerivedClass*>(h);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
inline result VM::execute(Host& host,
|
||||
evmc_revision rev,
|
||||
const evmc_message& msg,
|
||||
const uint8_t* code,
|
||||
size_t code_size) noexcept
|
||||
{
|
||||
return execute(Host::get_interface(), host.to_context(), rev, msg, code, code_size);
|
||||
}
|
||||
|
||||
|
||||
namespace internal
|
||||
{
|
||||
inline bool account_exists(evmc_context* h, const evmc_address* addr) noexcept
|
||||
inline bool account_exists(evmc_host_context* h, const evmc_address* addr) noexcept
|
||||
{
|
||||
return static_cast<Host*>(h)->account_exists(*addr);
|
||||
return Host::from_context(h)->account_exists(*addr);
|
||||
}
|
||||
inline evmc_bytes32 get_storage(evmc_context* h,
|
||||
|
||||
inline evmc_bytes32 get_storage(evmc_host_context* h,
|
||||
const evmc_address* addr,
|
||||
const evmc_bytes32* key) noexcept
|
||||
{
|
||||
return static_cast<Host*>(h)->get_storage(*addr, *key);
|
||||
return Host::from_context(h)->get_storage(*addr, *key);
|
||||
}
|
||||
inline evmc_storage_status set_storage(evmc_context* h,
|
||||
|
||||
inline evmc_storage_status set_storage(evmc_host_context* h,
|
||||
const evmc_address* addr,
|
||||
const evmc_bytes32* key,
|
||||
const evmc_bytes32* value) noexcept
|
||||
{
|
||||
return static_cast<Host*>(h)->set_storage(*addr, *key, *value);
|
||||
return Host::from_context(h)->set_storage(*addr, *key, *value);
|
||||
}
|
||||
inline evmc_uint256be get_balance(evmc_context* h, const evmc_address* addr) noexcept
|
||||
|
||||
inline evmc_uint256be get_balance(evmc_host_context* h, const evmc_address* addr) noexcept
|
||||
{
|
||||
return static_cast<Host*>(h)->get_balance(*addr);
|
||||
return Host::from_context(h)->get_balance(*addr);
|
||||
}
|
||||
inline size_t get_code_size(evmc_context* h, const evmc_address* addr) noexcept
|
||||
|
||||
inline size_t get_code_size(evmc_host_context* h, const evmc_address* addr) noexcept
|
||||
{
|
||||
return static_cast<Host*>(h)->get_code_size(*addr);
|
||||
return Host::from_context(h)->get_code_size(*addr);
|
||||
}
|
||||
inline evmc_bytes32 get_code_hash(evmc_context* h, const evmc_address* addr) noexcept
|
||||
|
||||
inline evmc_bytes32 get_code_hash(evmc_host_context* h, const evmc_address* addr) noexcept
|
||||
{
|
||||
return static_cast<Host*>(h)->get_code_hash(*addr);
|
||||
return Host::from_context(h)->get_code_hash(*addr);
|
||||
}
|
||||
inline size_t copy_code(evmc_context* h,
|
||||
|
||||
inline size_t copy_code(evmc_host_context* h,
|
||||
const evmc_address* addr,
|
||||
size_t code_offset,
|
||||
uint8_t* buffer_data,
|
||||
size_t buffer_size) noexcept
|
||||
{
|
||||
return static_cast<Host*>(h)->copy_code(*addr, code_offset, buffer_data, buffer_size);
|
||||
return Host::from_context(h)->copy_code(*addr, code_offset, buffer_data, buffer_size);
|
||||
}
|
||||
inline void selfdestruct(evmc_context* h,
|
||||
|
||||
inline void selfdestruct(evmc_host_context* h,
|
||||
const evmc_address* addr,
|
||||
const evmc_address* beneficiary) noexcept
|
||||
{
|
||||
static_cast<Host*>(h)->selfdestruct(*addr, *beneficiary);
|
||||
Host::from_context(h)->selfdestruct(*addr, *beneficiary);
|
||||
}
|
||||
inline evmc_result call(evmc_context* h, const evmc_message* msg) noexcept
|
||||
|
||||
inline evmc_result call(evmc_host_context* h, const evmc_message* msg) noexcept
|
||||
{
|
||||
return static_cast<Host*>(h)->call(*msg).release_raw();
|
||||
return Host::from_context(h)->call(*msg).release_raw();
|
||||
}
|
||||
inline evmc_tx_context get_tx_context(evmc_context* h) noexcept
|
||||
|
||||
inline evmc_tx_context get_tx_context(evmc_host_context* h) noexcept
|
||||
{
|
||||
return static_cast<Host*>(h)->get_tx_context();
|
||||
return Host::from_context(h)->get_tx_context();
|
||||
}
|
||||
inline evmc_bytes32 get_block_hash(evmc_context* h, int64_t block_number) noexcept
|
||||
|
||||
inline evmc_bytes32 get_block_hash(evmc_host_context* h, int64_t block_number) noexcept
|
||||
{
|
||||
return static_cast<Host*>(h)->get_block_hash(block_number);
|
||||
return Host::from_context(h)->get_block_hash(block_number);
|
||||
}
|
||||
inline void emit_log(evmc_context* h,
|
||||
|
||||
inline void emit_log(evmc_host_context* h,
|
||||
const evmc_address* addr,
|
||||
const uint8_t* data,
|
||||
size_t data_size,
|
||||
const evmc_bytes32 topics[],
|
||||
size_t num_topics) noexcept
|
||||
{
|
||||
static_cast<Host*>(h)->emit_log(*addr, data, data_size, static_cast<const bytes32*>(topics),
|
||||
Host::from_context(h)->emit_log(*addr, data, data_size, static_cast<const bytes32*>(topics),
|
||||
num_topics);
|
||||
}
|
||||
|
||||
constexpr evmc_host_interface interface{
|
||||
account_exists, get_storage, set_storage, get_balance, get_code_size, get_code_hash,
|
||||
copy_code, selfdestruct, call, get_tx_context, get_block_hash, emit_log,
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
inline Host::Host() noexcept : evmc_context{&evmc::internal::interface} {}
|
||||
|
||||
inline const evmc_host_interface& Host::get_interface() noexcept
|
||||
{
|
||||
static constexpr evmc_host_interface interface{
|
||||
::evmc::internal::account_exists, ::evmc::internal::get_storage,
|
||||
::evmc::internal::set_storage, ::evmc::internal::get_balance,
|
||||
::evmc::internal::get_code_size, ::evmc::internal::get_code_hash,
|
||||
::evmc::internal::copy_code, ::evmc::internal::selfdestruct,
|
||||
::evmc::internal::call, ::evmc::internal::get_tx_context,
|
||||
::evmc::internal::get_block_hash, ::evmc::internal::emit_log};
|
||||
return interface;
|
||||
}
|
||||
} // namespace evmc
|
||||
|
||||
|
||||
|
@ -22,36 +22,35 @@
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* Returns true if the VM instance has a compatible ABI version.
|
||||
* Returns true if the VM has a compatible ABI version.
|
||||
*/
|
||||
static inline int evmc_is_abi_compatible(struct evmc_instance* instance)
|
||||
static inline bool evmc_is_abi_compatible(struct evmc_vm* vm)
|
||||
{
|
||||
return instance->abi_version == EVMC_ABI_VERSION;
|
||||
return vm->abi_version == EVMC_ABI_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the VM instance.
|
||||
* Returns the name of the VM.
|
||||
*/
|
||||
static inline const char* evmc_vm_name(struct evmc_instance* instance)
|
||||
static inline const char* evmc_vm_name(struct evmc_vm* vm)
|
||||
{
|
||||
return instance->name;
|
||||
return vm->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the VM instance.
|
||||
* Returns the version of the VM.
|
||||
*/
|
||||
static inline const char* evmc_vm_version(struct evmc_instance* instance)
|
||||
static inline const char* evmc_vm_version(struct evmc_vm* vm)
|
||||
{
|
||||
return instance->version;
|
||||
return vm->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the VM instance has the given capability.
|
||||
* Checks if the VM has the given capability.
|
||||
*
|
||||
* @see evmc_get_capabilities_fn
|
||||
*/
|
||||
static inline bool evmc_vm_has_capability(struct evmc_instance* vm,
|
||||
enum evmc_capabilities capability)
|
||||
static inline bool evmc_vm_has_capability(struct evmc_vm* vm, enum evmc_capabilities capability)
|
||||
{
|
||||
return (vm->get_capabilities(vm) & (evmc_capabilities_flagset)capability) != 0;
|
||||
}
|
||||
@ -61,52 +60,39 @@ static inline bool evmc_vm_has_capability(struct evmc_instance* vm,
|
||||
*
|
||||
* @see evmc_destroy_fn
|
||||
*/
|
||||
static inline void evmc_destroy(struct evmc_instance* instance)
|
||||
static inline void evmc_destroy(struct evmc_vm* vm)
|
||||
{
|
||||
instance->destroy(instance);
|
||||
vm->destroy(vm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the option for the VM instance, if the feature is supported by the VM.
|
||||
* Sets the option for the VM, if the feature is supported by the VM.
|
||||
*
|
||||
* @see evmc_set_option_fn
|
||||
*/
|
||||
static inline enum evmc_set_option_result evmc_set_option(struct evmc_instance* instance,
|
||||
static inline enum evmc_set_option_result evmc_set_option(struct evmc_vm* vm,
|
||||
char const* name,
|
||||
char const* value)
|
||||
{
|
||||
if (instance->set_option)
|
||||
return instance->set_option(instance, name, value);
|
||||
if (vm->set_option)
|
||||
return vm->set_option(vm, name, value);
|
||||
return EVMC_SET_OPTION_INVALID_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tracer callback for the VM instance, if the feature is supported by the VM.
|
||||
*
|
||||
* @see evmc_set_tracer_fn
|
||||
*/
|
||||
EVMC_DEPRECATED
|
||||
static inline void evmc_set_tracer(struct evmc_instance* instance,
|
||||
evmc_trace_callback callback,
|
||||
struct evmc_tracer_context* context)
|
||||
{
|
||||
if (instance->set_tracer)
|
||||
instance->set_tracer(instance, callback, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes code in the VM instance.
|
||||
*
|
||||
* @see evmc_execute_fn.
|
||||
*/
|
||||
static inline struct evmc_result evmc_execute(struct evmc_instance* instance,
|
||||
struct evmc_context* context,
|
||||
static inline struct evmc_result evmc_execute(struct evmc_vm* vm,
|
||||
const struct evmc_host_interface* host,
|
||||
struct evmc_host_context* context,
|
||||
enum evmc_revision rev,
|
||||
const struct evmc_message* msg,
|
||||
uint8_t const* code,
|
||||
size_t code_size)
|
||||
{
|
||||
return instance->execute(instance, context, rev, msg, code, code_size);
|
||||
return vm->execute(vm, host, context, rev, msg, code, code_size);
|
||||
}
|
||||
|
||||
/// The evmc_result release function using free() for releasing the memory.
|
||||
|
@ -152,8 +152,8 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro
|
||||
char* base_name = prefixed_name + prefix_length;
|
||||
strcpy_sx(base_name, PATH_MAX_LENGTH, name_pos);
|
||||
|
||||
// Trim the file extension.
|
||||
char* ext_pos = strrchr(prefixed_name, '.');
|
||||
// Trim all file extensions.
|
||||
char* ext_pos = strchr(prefixed_name, '.');
|
||||
if (ext_pos)
|
||||
*ext_pos = 0;
|
||||
|
||||
@ -163,15 +163,7 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro
|
||||
*dash_pos++ = '_';
|
||||
|
||||
// Search for the built function name.
|
||||
while ((create_fn = DLL_GET_CREATE_FN(handle, prefixed_name)) == NULL)
|
||||
{
|
||||
// Shorten the base name by skipping the `word_` segment.
|
||||
const char* shorter_name_pos = strchr(base_name, '_');
|
||||
if (!shorter_name_pos)
|
||||
break;
|
||||
|
||||
memmove(base_name, shorter_name_pos + 1, strlen(shorter_name_pos) + 1);
|
||||
}
|
||||
create_fn = DLL_GET_CREATE_FN(handle, prefixed_name);
|
||||
|
||||
if (!create_fn)
|
||||
create_fn = DLL_GET_CREATE_FN(handle, "evmc_create");
|
||||
@ -196,8 +188,7 @@ const char* evmc_last_error_msg()
|
||||
return m;
|
||||
}
|
||||
|
||||
struct evmc_instance* evmc_load_and_create(const char* filename,
|
||||
enum evmc_loader_error_code* error_code)
|
||||
struct evmc_vm* evmc_load_and_create(const char* filename, enum evmc_loader_error_code* error_code)
|
||||
{
|
||||
// First load the DLL. This also resets the last_error_msg;
|
||||
evmc_create_fn create_fn = evmc_load(filename, error_code);
|
||||
@ -207,21 +198,21 @@ struct evmc_instance* evmc_load_and_create(const char* filename,
|
||||
|
||||
enum evmc_loader_error_code ec = EVMC_LOADER_SUCCESS;
|
||||
|
||||
struct evmc_instance* instance = create_fn();
|
||||
if (!instance)
|
||||
struct evmc_vm* vm = create_fn();
|
||||
if (!vm)
|
||||
{
|
||||
ec = set_error(EVMC_LOADER_INSTANCE_CREATION_FAILURE,
|
||||
"creating EVMC instance of %s has failed", filename);
|
||||
ec = set_error(EVMC_LOADER_VM_CREATION_FAILURE, "creating EVMC VM of %s has failed",
|
||||
filename);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!evmc_is_abi_compatible(instance))
|
||||
if (!evmc_is_abi_compatible(vm))
|
||||
{
|
||||
ec = set_error(EVMC_LOADER_ABI_VERSION_MISMATCH,
|
||||
"EVMC ABI version %d of %s mismatches the expected version %d",
|
||||
instance->abi_version, filename, EVMC_ABI_VERSION);
|
||||
evmc_destroy(instance);
|
||||
instance = NULL;
|
||||
vm->abi_version, filename, EVMC_ABI_VERSION);
|
||||
evmc_destroy(vm);
|
||||
vm = NULL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
@ -229,7 +220,7 @@ exit:
|
||||
if (error_code)
|
||||
*error_code = ec;
|
||||
|
||||
return instance;
|
||||
return vm;
|
||||
}
|
||||
|
||||
/// Gets the token delimited by @p delim character of the string pointed by the @p str_ptr.
|
||||
@ -255,11 +246,10 @@ static char* get_token(char** str_ptr, char delim)
|
||||
return str;
|
||||
}
|
||||
|
||||
struct evmc_instance* evmc_load_and_configure(const char* config,
|
||||
enum evmc_loader_error_code* error_code)
|
||||
struct evmc_vm* evmc_load_and_configure(const char* config, enum evmc_loader_error_code* error_code)
|
||||
{
|
||||
enum evmc_loader_error_code ec = EVMC_LOADER_SUCCESS;
|
||||
struct evmc_instance* instance = NULL;
|
||||
struct evmc_vm* vm = NULL;
|
||||
|
||||
char config_copy_buffer[PATH_MAX_LENGTH];
|
||||
if (strcpy_sx(config_copy_buffer, sizeof(config_copy_buffer), config) != 0)
|
||||
@ -273,14 +263,14 @@ struct evmc_instance* evmc_load_and_configure(const char* config,
|
||||
char* options = config_copy_buffer;
|
||||
const char* path = get_token(&options, ',');
|
||||
|
||||
instance = evmc_load_and_create(path, error_code);
|
||||
if (!instance)
|
||||
vm = evmc_load_and_create(path, error_code);
|
||||
if (!vm)
|
||||
return NULL;
|
||||
|
||||
if (instance->set_option == NULL && strlen(options) != 0)
|
||||
if (vm->set_option == NULL && strlen(options) != 0)
|
||||
{
|
||||
ec = set_error(EVMC_LOADER_INVALID_OPTION_NAME, "%s (%s) does not support any options",
|
||||
instance->name, path);
|
||||
vm->name, path);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
@ -293,18 +283,18 @@ struct evmc_instance* evmc_load_and_configure(const char* config,
|
||||
// The option variable will have the value, can be empty.
|
||||
const char* name = get_token(&option, '=');
|
||||
|
||||
enum evmc_set_option_result r = instance->set_option(instance, name, option);
|
||||
enum evmc_set_option_result r = vm->set_option(vm, name, option);
|
||||
switch (r)
|
||||
{
|
||||
case EVMC_SET_OPTION_SUCCESS:
|
||||
break;
|
||||
case EVMC_SET_OPTION_INVALID_NAME:
|
||||
ec = set_error(EVMC_LOADER_INVALID_OPTION_NAME, "%s (%s): unknown option '%s'",
|
||||
instance->name, path, name);
|
||||
vm->name, path, name);
|
||||
goto exit;
|
||||
case EVMC_SET_OPTION_INVALID_VALUE:
|
||||
ec = set_error(EVMC_LOADER_INVALID_OPTION_VALUE,
|
||||
"%s (%s): unsupported value '%s' for option '%s'", instance->name, path,
|
||||
"%s (%s): unsupported value '%s' for option '%s'", vm->name, path,
|
||||
option, name);
|
||||
goto exit;
|
||||
}
|
||||
@ -315,9 +305,9 @@ exit:
|
||||
*error_code = ec;
|
||||
|
||||
if (ec == EVMC_LOADER_SUCCESS)
|
||||
return instance;
|
||||
return vm;
|
||||
|
||||
if (instance)
|
||||
evmc_destroy(instance);
|
||||
if (vm)
|
||||
evmc_destroy(vm);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
/** The function pointer type for EVMC create functions. */
|
||||
typedef struct evmc_instance* (*evmc_create_fn)(void);
|
||||
typedef struct evmc_vm* (*evmc_create_fn)(void);
|
||||
|
||||
/** Error codes for the EVMC loader. */
|
||||
enum evmc_loader_error_code
|
||||
@ -37,7 +37,7 @@ enum evmc_loader_error_code
|
||||
EVMC_LOADER_INVALID_ARGUMENT = 3,
|
||||
|
||||
/** The creation of a VM instance has failed. */
|
||||
EVMC_LOADER_INSTANCE_CREATION_FAILURE = 4,
|
||||
EVMC_LOADER_VM_CREATION_FAILURE = 4,
|
||||
|
||||
/** The ABI version of the VM instance has mismatched. */
|
||||
EVMC_LOADER_ABI_VERSION_MISMATCH = 5,
|
||||
@ -61,21 +61,16 @@ enum evmc_loader_error_code
|
||||
* After the DLL is successfully loaded the function tries to find the EVM create function in the
|
||||
* library. The `filename` is used to guess the EVM name and the name of the create function.
|
||||
* The create function name is constructed by the following rules. Consider example path:
|
||||
* "/ethereum/libexample-interpreter.so".
|
||||
* "/ethereum/libexample-interpreter.so.1.0".
|
||||
* - the filename is taken from the path:
|
||||
* "libexample-interpreter.so",
|
||||
* - the "lib" prefix and file extension are stripped from the name:
|
||||
* "libexample-interpreter.so.1.0",
|
||||
* - the "lib" prefix and all file extensions are stripped from the name:
|
||||
* "example-interpreter"
|
||||
* - all "-" are replaced with "_" to construct _base name_:
|
||||
* "example_interpreter",
|
||||
* - the function name "evmc_create_" + _base name_ is searched in the library:
|
||||
* "evmc_create_example_interpreter",
|
||||
* - if function not found, the _base name_ is shorten by skipping the first word separated by "_":
|
||||
* "interpreter",
|
||||
* - then, the function of the shorter name "evmc_create_" + _base name_ is searched in the library:
|
||||
* "evmc_create_interpreter",
|
||||
* - the name shortening continues until a function is found or the name cannot be shorten more,
|
||||
* - lastly, when no function found, the function name "evmc_create" is searched in the library.
|
||||
* - if the function is not found, the function name "evmc_create" is searched in the library.
|
||||
*
|
||||
* If the create function is found in the library, the pointer to the function is returned.
|
||||
* Otherwise, the ::EVMC_LOADER_SYMBOL_NOT_FOUND error code is signaled and NULL is returned.
|
||||
@ -98,7 +93,7 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro
|
||||
*
|
||||
* This is a macro for creating the VM instance with the function returned from evmc_load().
|
||||
* The function signals the same errors as evmc_load() and additionally:
|
||||
* - ::EVMC_LOADER_INSTANCE_CREATION_FAILURE when the create function returns NULL,
|
||||
* - ::EVMC_LOADER_VM_CREATION_FAILURE when the create function returns NULL,
|
||||
* - ::EVMC_LOADER_ABI_VERSION_MISMATCH when the created VM instance has ABI version different
|
||||
* from the ABI version of this library (::EVMC_ABI_VERSION).
|
||||
*
|
||||
@ -114,8 +109,7 @@ evmc_create_fn evmc_load(const char* filename, enum evmc_loader_error_code* erro
|
||||
* ::EVMC_LOADER_SUCCESS on success or any other error code as described above.
|
||||
* @return The pointer to the created VM or NULL in case of error.
|
||||
*/
|
||||
struct evmc_instance* evmc_load_and_create(const char* filename,
|
||||
enum evmc_loader_error_code* error_code);
|
||||
struct evmc_vm* evmc_load_and_create(const char* filename, enum evmc_loader_error_code* error_code);
|
||||
|
||||
/**
|
||||
* Dynamically loads the EVMC module, then creates and configures the VM instance.
|
||||
@ -151,8 +145,8 @@ struct evmc_instance* evmc_load_and_create(const char* filename,
|
||||
* ::EVMC_LOADER_SUCCESS on success or any other error code as described above.
|
||||
* @return The pointer to the created VM or NULL in case of error.
|
||||
*/
|
||||
struct evmc_instance* evmc_load_and_configure(const char* config,
|
||||
enum evmc_loader_error_code* error_code);
|
||||
struct evmc_vm* evmc_load_and_configure(const char* config,
|
||||
enum evmc_loader_error_code* error_code);
|
||||
|
||||
/**
|
||||
* Returns the human-readable message describing the most recent error
|
||||
|
@ -35,6 +35,8 @@ library MerkleProof {
|
||||
|
||||
// ----
|
||||
// Warning: (755-767): Assertion checker does not yet support this expression.
|
||||
// Warning: (988-991): Assertion checker does not yet implement type abi
|
||||
// Warning: (988-1032): Assertion checker does not yet implement this type of function call.
|
||||
// Warning: (1175-1178): Assertion checker does not yet implement type abi
|
||||
// Warning: (1175-1219): Assertion checker does not yet implement this type of function call.
|
||||
// Warning: (755-767): Assertion checker does not yet support this expression.
|
||||
|
@ -83,7 +83,7 @@ contract InternalCall {
|
||||
// Warning: (1144-1206): Function state mutability can be restricted to pure
|
||||
// Warning: (1212-1274): Function state mutability can be restricted to pure
|
||||
// Warning: (1280-1342): Function state mutability can be restricted to pure
|
||||
// Warning: (771-774): Assertion checker does not yet implement type abi
|
||||
// Warning: (782-813): Type conversion is not yet fully supported and might yield false positives.
|
||||
// Warning: (771-814): Assertion checker does not yet implement this type of function call.
|
||||
// Warning: (825-830): Assertion checker does not yet support the type of this variable.
|
||||
// Warning: (1403-1408): Assertion checker does not yet implement this type of function call.
|
||||
|
@ -9,5 +9,6 @@ contract C {
|
||||
// ----
|
||||
// Warning: (133-143): Unused local variable.
|
||||
// Warning: (133-143): Assertion checker does not yet support the type of this variable.
|
||||
// Warning: (146-147): Assertion checker does not yet implement type type(struct C.A storage pointer)
|
||||
// Warning: (146-163): Assertion checker does not yet implement type struct C.A memory
|
||||
// Warning: (146-163): Assertion checker does not yet implement this expression.
|
||||
|
@ -6,5 +6,7 @@ contract C {
|
||||
}
|
||||
// ----
|
||||
// Warning: (31-64): Experimental features are turned on. Do not use experimental features on live deployments.
|
||||
// Warning: (162-165): Assertion checker does not yet implement type abi
|
||||
// Warning: (162-176): Assertion checker does not yet implement this type of function call.
|
||||
// Warning: (178-181): Assertion checker does not yet implement type abi
|
||||
// Warning: (178-203): Assertion checker does not yet implement this type of function call.
|
||||
|
@ -16,3 +16,5 @@ contract C
|
||||
assert(y < 10000);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (228-229): Assertion checker does not yet implement type type(library L)
|
||||
|
@ -17,4 +17,5 @@ contract C
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (228-229): Assertion checker does not yet implement type type(library L)
|
||||
// Warning: (245-261): Assertion violation happens here
|
||||
|
@ -0,0 +1,15 @@
|
||||
pragma experimental SMTChecker;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
contract C {
|
||||
struct S { uint x; uint[] b; }
|
||||
function f() public pure returns (S memory, bytes memory) {
|
||||
return abi.decode("abc", (S, bytes));
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (32-67): Experimental features are turned on. Do not use experimental features on live deployments.
|
||||
// Warning: (151-159): Assertion checker does not yet support the type of this variable.
|
||||
// Warning: (188-191): Assertion checker does not yet implement type abi
|
||||
// Warning: (207-208): Assertion checker does not yet implement type type(struct C.S storage pointer)
|
||||
// Warning: (188-217): Assertion checker does not yet implement this type of function call.
|
@ -0,0 +1,20 @@
|
||||
pragma experimental SMTChecker;
|
||||
pragma experimental "ABIEncoderV2";
|
||||
|
||||
contract C {
|
||||
function f() public pure {
|
||||
(uint x1, bool b1) = abi.decode("abc", (uint, bool));
|
||||
(uint x2, bool b2) = abi.decode("abc", (uint, bool));
|
||||
// False positive until abi.* are implemented as uninterpreted functions.
|
||||
assert(x1 == x2);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (32-67): Experimental features are turned on. Do not use experimental features on live deployments.
|
||||
// Warning: (125-132): Unused local variable.
|
||||
// Warning: (183-190): Unused local variable.
|
||||
// Warning: (136-139): Assertion checker does not yet implement type abi
|
||||
// Warning: (136-167): Assertion checker does not yet implement this type of function call.
|
||||
// Warning: (194-197): Assertion checker does not yet implement type abi
|
||||
// Warning: (194-225): Assertion checker does not yet implement this type of function call.
|
||||
// Warning: (303-319): Assertion violation happens here
|
@ -0,0 +1,24 @@
|
||||
pragma experimental SMTChecker;
|
||||
contract C {
|
||||
function f() public pure {
|
||||
(uint a1, bytes32 b1, C c1) = abi.decode("abc", (uint, bytes32, C));
|
||||
(uint a2, bytes32 b2, C c2) = abi.decode("abc", (uint, bytes32, C));
|
||||
// False positive until abi.* are implemented as uninterpreted functions.
|
||||
assert(a1 == a2);
|
||||
assert(a1 != a2);
|
||||
}
|
||||
|
||||
}
|
||||
// ----
|
||||
// Warning: (88-98): Unused local variable.
|
||||
// Warning: (100-104): Unused local variable.
|
||||
// Warning: (161-171): Unused local variable.
|
||||
// Warning: (173-177): Unused local variable.
|
||||
// Warning: (108-111): Assertion checker does not yet implement type abi
|
||||
// Warning: (142-143): Assertion checker does not yet implement type type(contract C)
|
||||
// Warning: (108-145): Assertion checker does not yet implement this type of function call.
|
||||
// Warning: (181-184): Assertion checker does not yet implement type abi
|
||||
// Warning: (215-216): Assertion checker does not yet implement type type(contract C)
|
||||
// Warning: (181-218): Assertion checker does not yet implement this type of function call.
|
||||
// Warning: (296-312): Assertion violation happens here
|
||||
// Warning: (315-331): Assertion violation happens here
|
@ -10,5 +10,6 @@ contract C
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (132-133): Assertion checker does not yet implement type type(enum C.D)
|
||||
// Warning: (132-136): Type conversion is not yet fully supported and might yield false positives.
|
||||
// Warning: (140-160): Assertion violation happens here
|
||||
|
@ -20,3 +20,6 @@ contract C
|
||||
// ----
|
||||
// Warning: (224-240): Unused local variable.
|
||||
// Warning: (260-275): Assertion violation happens here
|
||||
// Warning: (279-293): Assertion violation happens here
|
||||
// Warning: (297-316): Assertion violation happens here
|
||||
// Warning: (320-344): Assertion violation happens here
|
||||
|
@ -20,3 +20,6 @@ contract C
|
||||
// ----
|
||||
// Warning: (224-240): Unused local variable.
|
||||
// Warning: (268-283): Assertion violation happens here
|
||||
// Warning: (287-301): Assertion violation happens here
|
||||
// Warning: (305-324): Assertion violation happens here
|
||||
// Warning: (328-352): Assertion violation happens here
|
||||
|
@ -20,3 +20,6 @@ contract C
|
||||
// ----
|
||||
// Warning: (224-240): Unused local variable.
|
||||
// Warning: (266-281): Assertion violation happens here
|
||||
// Warning: (285-299): Assertion violation happens here
|
||||
// Warning: (303-322): Assertion violation happens here
|
||||
// Warning: (326-350): Assertion violation happens here
|
||||
|
@ -17,8 +17,10 @@ contract C
|
||||
// Warning: (157-170): Unused local variable.
|
||||
// Warning: (157-170): Assertion checker does not yet support the type of this variable.
|
||||
// Warning: (139-146): Assertion checker does not yet implement type struct C.S storage ref
|
||||
// Warning: (149-150): Assertion checker does not yet implement type type(struct C.S storage pointer)
|
||||
// Warning: (149-153): Assertion checker does not yet implement type struct C.S memory
|
||||
// Warning: (149-153): Assertion checker does not yet implement this expression.
|
||||
// Warning: (139-153): Assertion checker does not yet implement type struct C.S storage ref
|
||||
// Warning: (173-174): Assertion checker does not yet implement type type(struct C.S storage pointer)
|
||||
// Warning: (173-177): Assertion checker does not yet implement type struct C.S memory
|
||||
// Warning: (173-177): Assertion checker does not yet implement this expression.
|
||||
|
@ -15,9 +15,11 @@ contract C {
|
||||
}
|
||||
// ----
|
||||
// Warning: (112-120): Assertion checker does not yet support the type of this variable.
|
||||
// Warning: (137-138): Assertion checker does not yet implement type type(struct C.S storage pointer)
|
||||
// Warning: (137-141): Assertion checker does not yet implement type struct C.S memory
|
||||
// Warning: (137-141): Assertion checker does not yet implement this expression.
|
||||
// Warning: (193-203): Assertion checker does not yet support the type of this variable.
|
||||
// Warning: (137-138): Assertion checker does not yet implement type type(struct C.S storage pointer)
|
||||
// Warning: (137-141): Assertion checker does not yet implement type struct C.S memory
|
||||
// Warning: (137-141): Assertion checker does not yet implement this expression.
|
||||
// Warning: (227-228): Assertion checker does not yet implement type struct C.S memory
|
||||
|
@ -71,7 +71,8 @@ if (OSSFUZZ)
|
||||
/usr/include/libprotobuf-mutator
|
||||
)
|
||||
target_link_libraries(abiv2_proto_ossfuzz PRIVATE solidity
|
||||
evmc evmone intx ethash evmc-instructions
|
||||
evmc
|
||||
evmone-standalone
|
||||
protobuf-mutator-libfuzzer.a
|
||||
protobuf-mutator.a
|
||||
protobuf.a
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
#include <fstream>
|
||||
|
||||
static evmc::vm evmone = evmc::vm{evmc_create_evmone()};
|
||||
static evmc::VM evmone = evmc::VM{evmc_create_evmone()};
|
||||
|
||||
using namespace dev::test::abiv2fuzzer;
|
||||
using namespace dev::test;
|
||||
|
@ -437,6 +437,12 @@ void ProtoConverter::visit(NullaryOp const& _x)
|
||||
case NullaryOp::GASLIMIT:
|
||||
m_output << "gaslimit()";
|
||||
break;
|
||||
case NullaryOp::SELFBALANCE:
|
||||
m_output << "selfbalance()";
|
||||
break;
|
||||
case NullaryOp::CHAINID:
|
||||
m_output << "chainid()";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -237,6 +237,8 @@ message NullaryOp {
|
||||
NUMBER = 14;
|
||||
DIFFICULTY = 15;
|
||||
GASLIMIT = 16;
|
||||
SELFBALANCE = 17;
|
||||
CHAINID = 18;
|
||||
}
|
||||
required NOp op = 1;
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ DEFINE_PROTO_FUZZER(Program const& _input)
|
||||
|
||||
// AssemblyStack entry point
|
||||
AssemblyStack stack(
|
||||
langutil::EVMVersion(),
|
||||
langutil::EVMVersion(langutil::EVMVersion::istanbul()),
|
||||
AssemblyStack::Language::StrictAssembly,
|
||||
dev::solidity::OptimiserSettings::full()
|
||||
);
|
||||
@ -95,7 +95,7 @@ DEFINE_PROTO_FUZZER(Program const& _input)
|
||||
yulFuzzerUtil::TerminationReason termReason = yulFuzzerUtil::interpret(
|
||||
os1,
|
||||
stack.parserResult()->code,
|
||||
EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion())
|
||||
EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion(langutil::EVMVersion::istanbul()))
|
||||
);
|
||||
|
||||
if (termReason == yulFuzzerUtil::TerminationReason::StepLimitReached)
|
||||
@ -105,7 +105,7 @@ DEFINE_PROTO_FUZZER(Program const& _input)
|
||||
termReason = yulFuzzerUtil::interpret(
|
||||
os2,
|
||||
stack.parserResult()->code,
|
||||
EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion()),
|
||||
EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion(langutil::EVMVersion::istanbul())),
|
||||
(yul::test::yul_fuzzer::yulFuzzerUtil::maxSteps * 4)
|
||||
);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user