Merge remote-tracking branch 'origin/develop' into develop_060

Had to adjust gas costs during merge.
This commit is contained in:
chriseth 2019-09-24 10:06:14 +02:00
commit f3c4f466f6
43 changed files with 828 additions and 149 deletions

View File

@ -190,7 +190,7 @@ defaults:
-i -H "Content-Type: application/json"
-H "Accept: application/json"
-H "Authorization: Bearer $GITTER_API_TOKEN" "https://api.gitter.im/v1/rooms/$GITTER_NOTIFY_ROOM_ID/chatMessages"
-d '{"text":" ❌ Nightly job **'$CIRCLE_JOB'** failed. Please check '$CIRCLE_BUILD_URL' for details."}'
-d '{"text":" ❌ Nightly job **'$CIRCLE_JOB'** failed on **'$CIRCLE_BRANCH'**. Please see '$CIRCLE_BUILD_URL' for details."}'
when: on_fail
- gitter_notify_success: &gitter_notify_success
@ -200,7 +200,7 @@ defaults:
-i -H "Content-Type: application/json"
-H "Accept: application/json"
-H "Authorization: Bearer $GITTER_API_TOKEN" "https://api.gitter.im/v1/rooms/$GITTER_NOTIFY_ROOM_ID/chatMessages"
-d '{"text":" ✅ Nightly job **'$CIRCLE_JOB'** succeeded. Please check '$CIRCLE_BUILD_URL' for details."}'
-d '{"text":" ✅ Nightly job **'$CIRCLE_JOB'** succeeded on **'$CIRCLE_BRANCH'**. Please see '$CIRCLE_BUILD_URL' for details."}'
when: on_success
# -----------------------------------------------------------------------------------------------
@ -656,6 +656,7 @@ workflows:
branches:
only:
- develop
- develop_060
jobs:
# Emscripten builds and external tests

View File

@ -0,0 +1,131 @@
# vim:syntax=dockerfile
#------------------------------------------------------------------------------
# Dockerfile for building and testing Solidity Compiler on CI
# Target: Ubuntu 19.04 (Disco Dingo) Clang variant
# URL: https://hub.docker.com/r/ethereum/solidity-buildpack-deps
#
# This file is part of solidity.
#
# solidity is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# solidity is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with solidity. If not, see <http://www.gnu.org/licenses/>
#
# (c) 2016-2019 solidity contributors.
#------------------------------------------------------------------------------
FROM buildpack-deps:disco AS base
ARG DEBIAN_FRONTEND=noninteractive
RUN set -ex; \
dist=$(grep DISTRIB_CODENAME /etc/lsb-release | cut -d= -f2); \
echo "deb http://ppa.launchpad.net/ethereum/cpp-build-deps/ubuntu $dist main" >> /etc/apt/sources.list ; \
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1c52189c923f6ca9 ; \
apt-get update; \
apt-get install -qqy --no-install-recommends \
build-essential \
software-properties-common \
cmake ninja-build \
clang++-8 llvm-8-dev \
libjsoncpp-dev \
libleveldb1d \
; \
apt-get install -qy python-pip python-sphinx; \
update-alternatives --install /usr/bin/llvm-symbolizer llvm-symbolizer /usr/bin/llvm-symbolizer-8 1; \
update-alternatives --install /usr/bin/clang clang /usr/bin/clang-8 1; \
update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-8 1; \
pip install codecov; \
rm -rf /var/lib/apt/lists/*
FROM base AS libraries
ENV CC clang
ENV CXX clang++
# Boost
RUN git clone --recursive -b boost-1.69.0 https://github.com/boostorg/boost.git \
/usr/src/boost; \
cd /usr/src/boost; \
./bootstrap.sh --with-toolset=clang --prefix=/usr; \
./b2 toolset=clang headers; \
./b2 toolset=clang variant=release \
system regex filesystem unit_test_framework program_options \
install -j $(($(nproc)/2)); \
rm -rf /usr/src/boost
# Z3
RUN git clone --depth 1 -b Z3-4.8.5 https://github.com/Z3Prover/z3.git \
/usr/src/z3; \
cd /usr/src/z3; \
python scripts/mk_make.py --prefix=/usr ; \
cd build; \
make -j; \
make install; \
rm -rf /usr/src/z3;
# OSSFUZZ: libprotobuf-mutator
RUN set -ex; \
git clone https://github.com/google/libprotobuf-mutator.git \
/usr/src/libprotobuf-mutator; \
cd /usr/src/libprotobuf-mutator; \
git checkout d1fe8a7d8ae18f3d454f055eba5213c291986f21; \
mkdir build; \
cd build; \
cmake .. -GNinja -DLIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF=ON \
-DLIB_PROTO_MUTATOR_TESTING=OFF -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="/usr"; \
ninja; \
cp -vpr external.protobuf/bin/* /usr/bin/; \
cp -vpr external.protobuf/include/* /usr/include/; \
cp -vpr external.protobuf/lib/* /usr/lib/; \
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; \
cd evmone; \
mkdir build; \
cd build; \
cmake -G Ninja -DBUILD_SHARED_LIBS=ON -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

View File

@ -44,6 +44,7 @@ Compiler Features:
Bugfixes:
* Fix internal error when popping a dynamic storage array of mappings.
* Yul Optimizer: Fix reordering bug in connection with shifted one and mul/div-instructions in for loop conditions.
### 0.5.11 (2019-08-12)

View File

@ -1,9 +1,11 @@
# Inherit default options
include("${CMAKE_CURRENT_LIST_DIR}/default.cmake")
# Disable Z3 and CVC4 since none of the existing fuzzers need them
set(USE_Z3 OFF CACHE BOOL "" FORCE)
set(USE_CVC4 OFF CACHE BOOL "" FORCE)
set(USE_Z3 OFF CACHE BOOL "Disable Z3" FORCE)
set(USE_CVC4 OFF CACHE BOOL "Disable CVC4" FORCE)
# Build fuzzing binaries
set(OSSFUZZ 1)
set(OSSFUZZ ON CACHE BOOL "Enable fuzzer build" FORCE)
# Use libfuzzer as the fuzzing back-end
set(LIB_FUZZING_ENGINE "-fsanitize=fuzzer" CACHE STRING "Use libfuzzer back-end" FORCE)
# clang/libfuzzer specific flags for ASan instrumentation
set(CMAKE_CXX_FLAGS "-O1 -gline-tables-only -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link -stdlib=libstdc++")
set(CMAKE_CXX_FLAGS "-O1 -gline-tables-only -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link -stdlib=libstdc++" CACHE STRING "Custom compilation flags" FORCE)

View File

@ -0,0 +1,11 @@
# Inherit default options
include("${CMAKE_CURRENT_LIST_DIR}/default.cmake")
# Disable CVC4.
set(USE_CVC4 OFF CACHE BOOL "Disable CVC4" FORCE)
# Enable fuzzers
set(OSSFUZZ ON CACHE BOOL "Enable fuzzer build" FORCE)
set(LIB_FUZZING_ENGINE $ENV{LIB_FUZZING_ENGINE} CACHE STRING "Use fuzzer back-end defined by environment variable" FORCE)
# Link statically against boost libraries
set(BOOST_FOUND ON CACHE BOOL "" FORCE)
set(Boost_USE_STATIC_LIBS ON CACHE BOOL "Link against static Boost libraries" FORCE)
set(Boost_USE_STATIC_RUNTIME ON CACHE BOOL "Link against static Boost runtime library" FORCE)

View File

@ -163,7 +163,8 @@ If an opcode takes arguments (always from the top of the stack), they are given
Note that the order of arguments can be seen to be reversed in non-functional style (explained below).
Opcodes marked with ``-`` do not push an item onto the stack (do not return a result),
those marked with ``*`` are special and all others push exactly one item onto the stack (their "return value").
Opcodes marked with ``F``, ``H``, ``B`` or ``C`` are present since Frontier, Homestead, Byzantium or Constantinople, respectively.
Opcodes marked with ``F``, ``H``, ``B``, ``C`` or ``I`` are present since Frontier, Homestead,
Byzantium, Constantinople or Istanbul, respectively.
In the following, ``mem[a...b)`` signifies the bytes of memory starting at position ``a`` up to
but not including position ``b`` and ``storage[p]`` signifies the storage contents at position ``p``.
@ -259,6 +260,8 @@ In the grammar, opcodes are represented as pre-defined identifiers.
+-------------------------+-----+---+-----------------------------------------------------------------+
| balance(a) | | F | wei balance at address a |
+-------------------------+-----+---+-----------------------------------------------------------------+
| selfbalance() | | I | equivalent to balance(address()), but cheaper |
+-------------------------+-----+---+-----------------------------------------------------------------+
| caller | | F | call sender (excluding ``delegatecall``) |
+-------------------------+-----+---+-----------------------------------------------------------------+
| callvalue | | F | wei sent together with the current call |
@ -325,6 +328,8 @@ In the grammar, opcodes are represented as pre-defined identifiers.
| log4(p, s, t1, t2, t3, | `-` | F | log with topics t1, t2, t3, t4 and data mem[p...(p+s)) |
| t4) | | | |
+-------------------------+-----+---+-----------------------------------------------------------------+
| chainid | | I | ID of the executing chain (EIP 1344) |
+-------------------------+-----+---+-----------------------------------------------------------------+
| origin | | F | transaction sender |
+-------------------------+-----+---+-----------------------------------------------------------------+
| gasprice | | F | gas price of the transaction |

View File

@ -254,6 +254,61 @@ if you want all overflows to cause a revert.
Code such as ``require((balanceOf[_to] + _value) >= balanceOf[_to])`` can also help you check if values are what you expect.
.. _clearing-mappings:
Clearing Mappings
=================
The Solidity type ``mapping`` (see :ref:`mapping-types`) is a storage-only key-value data structure that
does not keep track of the keys that were assigned a non-zero value.
Because of that, cleaning a mapping without extra information about the written
keys is not possible.
If a ``mapping`` is used as the base type of a dynamic storage array, deleting
or popping the array will have no effect over the ``mapping`` elements.
The same happens, for example, if a ``mapping`` is used as the type of a member
field of a ``struct`` that is the base type of a dynamic storage array.
The ``mapping`` is also ignored in assignments of structs or arrays containing
a ``mapping``.
::
pragma solidity >=0.5.0 <0.7.0;
contract Map {
mapping (uint => uint)[] array;
function allocate(uint _newMaps) public {
array.length += _newMaps;
}
function writeMap(uint _map, uint _key, uint _value) public {
array[_map][_key] = _value;
}
function readMap(uint _map, uint _key) public view returns (uint) {
return array[_map][_key];
}
function eraseMaps() public {
delete array;
}
}
Consider the example above and the following sequence of calls: ``allocate(10)``,
``writeMap(4, 128, 256)``.
At this point, calling ``readMap(4, 128)`` returns 256.
If we call ``eraseMaps``, the length of state variable ``array`` is zeroed, but
since its ``mapping`` elements cannot be zeroed, their information stays alive
in the contract's storage.
After deleting ``array``, calling ``allocate(5)`` allows us to access
``array[4]`` again, and calling ``readMap(4, 128)`` returns 256 even without
another call to ``writeMap``.
If your ``mapping`` information must be deleted, consider using a library similar to
`iterable mapping <https://github.com/ethereum/dapp-bin/blob/master/library/iterable_mapping.sol>`_,
allowing you to traverse the keys and delete their values in the appropriate
``mapping``.
Minor Details
=============

View File

@ -17,7 +17,8 @@ byte-representation is all zeros, a type's :ref:`default value <default-value>`.
mapping, only its ``keccak256`` hash is used to look up the value.
Because of this, mappings do not have a length or a concept of a key or
value being set.
value being set, and therefore cannot be erased without extra information
regarding the assigned keys (see :ref:`clearing-mappings`).
Mappings can only have a data location of ``storage`` and thus
are allowed for state variables, as storage reference types

View File

@ -122,7 +122,8 @@ at each version. Backward compatibility is not guaranteed between each version.
- Shifting operators use shifting opcodes and thus need less gas.
- ``petersburg`` (**default**)
- The compiler behaves the same way as with constantinople.
- ``istanbul`` (**experimental**)
- ``istanbul``
- Opcodes ``chainid`` and ``selfbalance`` are available in assembly.
- ``berlin`` (**experimental**)

View File

@ -98,6 +98,14 @@ inline std::set<T> operator+(std::set<T>&& _a, U&& _b)
ret += std::forward<U>(_b);
return ret;
}
/// Remove one set from another one.
template <class T>
inline std::set<T>& operator-=(std::set<T>& _a, std::set<T> const& _b)
{
for (auto const& x: _b)
_a.erase(x);
return _a;
}
namespace dev
{

View File

@ -475,7 +475,8 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
[=]() -> Pattern {
return {Instruction::SHL, {Y, X}};
},
false
// Actually only changes the order, does not remove.
true
});
rules.push_back({
// MUL(SHL(X, 1), Y) -> SHL(X, Y)
@ -492,7 +493,8 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleListPart7(
[=]() -> Pattern {
return {Instruction::SHR, {Y, X}};
},
false
// Actually only changes the order, does not remove.
true
});
std::function<bool()> feasibilityFunction = [=]() {

View File

@ -88,6 +88,8 @@ add_library(yul
optimiser/ExpressionSplitter.h
optimiser/ForLoopConditionIntoBody.cpp
optimiser/ForLoopConditionIntoBody.h
optimiser/ForLoopConditionOutOfBody.cpp
optimiser/ForLoopConditionOutOfBody.h
optimiser/ForLoopInitRewriter.cpp
optimiser/ForLoopInitRewriter.h
optimiser/FullInliner.cpp

View File

@ -0,0 +1,69 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libyul/optimiser/ForLoopConditionOutOfBody.h>
#include <libyul/optimiser/Semantics.h>
#include <libyul/AsmData.h>
#include <libyul/Utilities.h>
#include <libdevcore/CommonData.h>
using namespace std;
using namespace dev;
using namespace yul;
void ForLoopConditionOutOfBody::operator()(ForLoop& _forLoop)
{
ASTModifier::operator()(_forLoop);
if (
!m_dialect.booleanNegationFunction() ||
_forLoop.condition->type() != typeid(Literal) ||
valueOfLiteral(boost::get<Literal>(*_forLoop.condition)) == u256(0) ||
_forLoop.body.statements.empty() ||
_forLoop.body.statements.front().type() != typeid(If)
)
return;
If& firstStatement = boost::get<If>(_forLoop.body.statements.front());
if (
firstStatement.body.statements.empty() ||
firstStatement.body.statements.front().type() != typeid(Break)
)
return;
if (!SideEffectsCollector(m_dialect, *firstStatement.condition).movable())
return;
YulString iszero = m_dialect.booleanNegationFunction()->name;
langutil::SourceLocation location = locationOf(*firstStatement.condition);
if (
firstStatement.condition->type() == typeid(FunctionCall) &&
boost::get<FunctionCall>(*firstStatement.condition).functionName.name == iszero
)
_forLoop.condition = make_unique<Expression>(std::move(boost::get<FunctionCall>(*firstStatement.condition).arguments.front()));
else
_forLoop.condition = make_unique<Expression>(FunctionCall{
location,
Identifier{location, iszero},
make_vector<Expression>(
std::move(*firstStatement.condition)
)
});
_forLoop.body.statements.erase(_forLoop.body.statements.begin());
}

View File

@ -0,0 +1,69 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/Dialect.h>
namespace yul
{
/**
* Reverses the transformation of ForLoopConditionIntoBody.
*
* For any movable ``c``, it turns
*
* for { ... } 1 { ... } {
* if iszero(c) { break }
* ...
* }
*
* into
*
* for { ... } c { ... } {
* ...
* }
*
* and it turns
*
* for { ... } 1 { ... } {
* if c { break }
* ...
* }
*
* into
*
* for { ... } iszero(c) { ... } {
* ...
* }
*
* The LiteralRematerialiser should be run before this step.
*/
class ForLoopConditionOutOfBody: public ASTModifier
{
public:
ForLoopConditionOutOfBody(Dialect const& _dialect):
m_dialect(_dialect)
{}
using ASTModifier::operator();
void operator()(ForLoop& _forLoop) override;
private:
Dialect const& m_dialect;
};
}

View File

@ -119,8 +119,9 @@ so that other components can more easily work with it. The final representation
will be similar to a static-single-assignment (SSA) form, with the difference
that it does not make use of explicit "phi" functions which combines the values
from different branches of control flow because such a feature does not exist
in the Yul language. Instead, assignments to existing variables are
used.
in the Yul language. Instead, when control flow merges, if a variable is re-assigned
in one of the branches, a new SSA variable is declared to hold its current value,
so that the following expressions still only need to reference SSA variables.
An example transformation is the following:
@ -139,21 +140,25 @@ as follows:
{
let _1 := 0
let a_1 := calldataload(_1)
let a_9 := calldataload(_1)
let a := a_9
let _2 := 0x20
let b_1 := calldataload(_2)
let b := b_1
let b_10 := calldataload(_2)
let b := b_10
let _3 := 0
let _4 := gt(a_1, _3)
if _4 {
let _4 := gt(a_9, _3)
if _4
{
let _5 := 0x20
let b_2 := mul(b_1, _5)
b := b_2
let b_11 := mul(b_10, _5)
b := b_11
}
let a_2 := add(a_1, 1)
let _6 := 0x20
let _7 := add(b, _6)
sstore(a_2, _7)
let b_12 := b
let _6 := 1
let a_13 := add(a_9, _6)
let _7 := 0x20
let _8 := add(b_12, _7)
sstore(a_13, _8)
}
Note that the only variable that is re-assigned in this snippet is ``b``.
@ -240,6 +245,10 @@ reference to ``a`` by ``a_i``.
The current value mapping is cleared for a variable ``a`` at the end of each block
in which it was assigned to and at the end of the for loop init block if it is assigned
inside the for loop body or post block.
If a variable's value is cleared according to the rule above and the variable is declared outside
the block, a new SSA variable will be created at the location where control flow joins,
this includes the beginning of loop post/body block and the location right after
If/Switch/ForLoop/Block statement.
After this stage, the Redundant Assign Eliminator is recommended to remove the unnecessary
intermediate assignments.

View File

@ -50,8 +50,6 @@ public:
private:
NameDispenser& m_nameDispenser;
/// This is a set of all variables that are assigned to anywhere in the code.
/// Variables that are only declared but never re-assigned are not touched.
set<YulString> const& m_variablesToReplace;
};
@ -130,7 +128,142 @@ void IntroduceSSA::operator()(Block& _block)
}
/**
* Second step of SSA transform: Replace the references to variables-to-be-replaced
* Second step of SSA transform: Introduces new SSA variables at each control-flow join
* and at the beginning of functions.
*/
class IntroduceControlFlowSSA: public ASTModifier
{
public:
explicit IntroduceControlFlowSSA(
NameDispenser& _nameDispenser,
set<YulString> const& _variablesToReplace
):
m_nameDispenser(_nameDispenser), m_variablesToReplace(_variablesToReplace)
{ }
void operator()(FunctionDefinition& _function) override;
void operator()(ForLoop& _forLoop) override;
void operator()(Switch& _switch) override;
void operator()(Block& _block) override;
private:
NameDispenser& m_nameDispenser;
set<YulString> const& m_variablesToReplace;
/// Variables (that are to be replaced) currently in scope.
set<YulString> m_variablesInScope;
/// Set of variables that do not have a specific value.
set<YulString> m_variablesToReassign;
};
void IntroduceControlFlowSSA::operator()(FunctionDefinition& _function)
{
set<YulString> varsInScope;
std::swap(varsInScope, m_variablesInScope);
set<YulString> toReassign;
std::swap(toReassign, m_variablesToReassign);
for (auto const& param: _function.parameters)
if (m_variablesToReplace.count(param.name))
{
m_variablesInScope.insert(param.name);
m_variablesToReassign.insert(param.name);
}
ASTModifier::operator()(_function);
m_variablesInScope = std::move(varsInScope);
m_variablesToReassign = std::move(toReassign);
}
void IntroduceControlFlowSSA::operator()(ForLoop& _for)
{
(*this)(_for.pre);
Assignments assignments;
assignments(_for.body);
assignments(_for.post);
for (auto const& var: assignments.names())
if (m_variablesInScope.count(var))
m_variablesToReassign.insert(var);
(*this)(_for.body);
(*this)(_for.post);
}
void IntroduceControlFlowSSA::operator()(Switch& _switch)
{
yulAssert(m_variablesToReassign.empty(), "");
set<YulString> toReassign;
for (auto& c: _switch.cases)
{
(*this)(c.body);
toReassign += m_variablesToReassign;
}
m_variablesToReassign += toReassign;
}
void IntroduceControlFlowSSA::operator()(Block& _block)
{
set<YulString> variablesDeclaredHere;
set<YulString> assignedVariables;
iterateReplacing(
_block.statements,
[&](Statement& _s) -> boost::optional<vector<Statement>>
{
vector<Statement> toPrepend;
for (YulString toReassign: m_variablesToReassign)
{
YulString newName = m_nameDispenser.newName(toReassign);
toPrepend.emplace_back(VariableDeclaration{
locationOf(_s),
{TypedName{locationOf(_s), newName, {}}},
make_unique<Expression>(Identifier{locationOf(_s), toReassign})
});
assignedVariables.insert(toReassign);
}
m_variablesToReassign.clear();
if (_s.type() == typeid(VariableDeclaration))
{
VariableDeclaration& varDecl = boost::get<VariableDeclaration>(_s);
for (auto const& var: varDecl.variables)
if (m_variablesToReplace.count(var.name))
{
variablesDeclaredHere.insert(var.name);
m_variablesInScope.insert(var.name);
}
}
else if (_s.type() == typeid(Assignment))
{
Assignment& assignment = boost::get<Assignment>(_s);
for (auto const& var: assignment.variableNames)
if (m_variablesToReplace.count(var.name))
assignedVariables.insert(var.name);
}
else
visit(_s);
if (toPrepend.empty())
return {};
else
{
toPrepend.emplace_back(std::move(_s));
return toPrepend;
}
}
);
m_variablesToReassign += assignedVariables;
m_variablesInScope -= variablesDeclaredHere;
m_variablesToReassign -= variablesDeclaredHere;
}
/**
* Third step of SSA transform: Replace the references to variables-to-be-replaced
* by their current values.
*/
class PropagateValues: public ASTModifier
@ -166,13 +299,27 @@ void PropagateValues::operator()(VariableDeclaration& _varDecl)
if (_varDecl.variables.size() != 1)
return;
YulString name = _varDecl.variables.front().name;
if (!m_variablesToReplace.count(name))
return;
yulAssert(_varDecl.value->type() == typeid(Identifier), "");
m_currentVariableValues[name] = boost::get<Identifier>(*_varDecl.value).name;
m_clearAtEndOfBlock.insert(name);
YulString variable = _varDecl.variables.front().name;
if (m_variablesToReplace.count(variable))
{
// `let a := a_1` - regular declaration of non-SSA variable
yulAssert(_varDecl.value->type() == typeid(Identifier), "");
m_currentVariableValues[variable] = boost::get<Identifier>(*_varDecl.value).name;
m_clearAtEndOfBlock.insert(variable);
}
else if (_varDecl.value && _varDecl.value->type() == typeid(Identifier))
{
// `let a_1 := a` - assignment to SSA variable after a branch.
YulString value = boost::get<Identifier>(*_varDecl.value).name;
if (m_variablesToReplace.count(value))
{
// This is safe because `a_1` is not a "variable to replace" and thus
// will not be re-assigned.
m_currentVariableValues[value] = variable;
m_clearAtEndOfBlock.insert(value);
}
}
}
@ -186,7 +333,7 @@ void PropagateValues::operator()(Assignment& _assignment)
if (!m_variablesToReplace.count(name))
return;
yulAssert(_assignment.value->type() == typeid(Identifier), "");
yulAssert(_assignment.value && _assignment.value->type() == typeid(Identifier), "");
m_currentVariableValues[name] = boost::get<Identifier>(*_assignment.value).name;
m_clearAtEndOfBlock.insert(name);
}
@ -231,6 +378,7 @@ void SSATransform::run(Block& _ast, NameDispenser& _nameDispenser)
Assignments assignments;
assignments(_ast);
IntroduceSSA{_nameDispenser, assignments.names()}(_ast);
IntroduceControlFlowSSA{_nameDispenser, assignments.names()}(_ast);
PropagateValues{assignments.names()}(_ast);
}

View File

@ -23,6 +23,8 @@
#include <libyul/AsmDataForward.h>
#include <libyul/optimiser/ASTWalker.h>
#include <liblangutil/SourceLocation.h>
#include <vector>
namespace yul
@ -61,8 +63,10 @@ class NameDispenser;
* Furthermore, always note the current variable/value assigned to a and replace each
* reference to a by this variable.
* The current value mapping is cleared for a variable a at the end of each block
* in which it was assigned and just after the for loop init block if it is assigned
* inside the for loop.
* in which it was assigned. We compensate that by appending a declaration
* of the form of "let a_1 := a" right after the location where control flow joins so
* variable references can use the SSA variable. The only exception to this rule are
* for loop conditions, as we cannot insert a variable declaration there.
*
* After this stage, redundantAssignmentRemover is recommended to remove the unnecessary
* intermediate assignments.
@ -70,7 +74,17 @@ class NameDispenser;
* This stage provides best results if CSE is run right before it, because
* then it does not generate excessive amounts of variables.
*
* The transform is implemented in three stages. All stages are only concerned
* with variables that are assigned somewhere in the code (excluding declarations).
* The first stage inserts new SSA variables for each declaration and assignment of
* such variables.
* The second stage inserts new SSA variables at control flow joins.
* The last stage replaces references to variables that are assigned to somewhere in the
* code by their current SSA variable.
*
* TODO Which transforms are required to keep this idempotent?
*
* Prerequisite: Disambiguator.
*/
class SSATransform: public ASTModifier
{

View File

@ -34,6 +34,7 @@
#include <libyul/optimiser/ExpressionInliner.h>
#include <libyul/optimiser/FullInliner.h>
#include <libyul/optimiser/ForLoopConditionIntoBody.h>
#include <libyul/optimiser/ForLoopConditionOutOfBody.h>
#include <libyul/optimiser/ForLoopInitRewriter.h>
#include <libyul/optimiser/Rematerialiser.h>
#include <libyul/optimiser/UnusedPruner.h>
@ -127,12 +128,14 @@ void OptimiserSuite::run(
{
// still in SSA, perform structural simplification
ControlFlowSimplifier{_dialect}(ast);
LiteralRematerialiser{_dialect}(ast);
ForLoopConditionOutOfBody{_dialect}(ast);
ControlFlowSimplifier{_dialect}(ast);
StructuralSimplifier{}(ast);
ControlFlowSimplifier{_dialect}(ast);
BlockFlattener{}(ast);
DeadCodeEliminator{_dialect}(ast);
ForLoopConditionIntoBody{_dialect}(ast);
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
}
@ -187,6 +190,7 @@ void OptimiserSuite::run(
LoadResolver::run(_dialect, ast);
ExpressionSimplifier::run(_dialect, ast);
LiteralRematerialiser{_dialect}(ast);
ForLoopConditionOutOfBody{_dialect}(ast);
StructuralSimplifier{}(ast);
BlockFlattener{}(ast);
DeadCodeEliminator{_dialect}(ast);
@ -195,6 +199,7 @@ void OptimiserSuite::run(
SSATransform::run(ast, dispenser);
RedundantAssignEliminator::run(_dialect, ast);
RedundantAssignEliminator::run(_dialect, ast);
ForLoopConditionIntoBody{_dialect}(ast);
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
CommonSubexpressionEliminator::run(_dialect, ast);
}
@ -212,6 +217,9 @@ void OptimiserSuite::run(
SSAReverser::run(ast);
CommonSubexpressionEliminator::run(_dialect, ast);
LiteralRematerialiser{_dialect}(ast);
ForLoopConditionOutOfBody{_dialect}(ast);
CommonSubexpressionEliminator::run(_dialect, ast);
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
ExpressionJoiner::run(ast);
@ -232,6 +240,9 @@ void OptimiserSuite::run(
BlockFlattener{}(ast);
DeadCodeEliminator{_dialect}(ast);
ControlFlowSimplifier{_dialect}(ast);
LiteralRematerialiser{_dialect}(ast);
ForLoopConditionOutOfBody{_dialect}(ast);
CommonSubexpressionEliminator::run(_dialect, ast);
FunctionGrouper{}(ast);

View File

@ -26,9 +26,9 @@
(param $x4 i64)
(result i64)
(local $v i64)
(if (i64.ne (i64.const 0) (i64.or (i64.or (get_local $x1) (get_local $x2)) (get_local $x3))) (then
(if (i64.ne (get_local $v) (i64.or (i64.or (get_local $x1) (get_local $x2)) (get_local $x3))) (then
(unreachable)))
(if (i64.ne (i64.const 0) (i64.shr_u (get_local $x4) (i64.const 32))) (then
(if (i64.ne (get_local $v) (i64.shr_u (get_local $x4) (i64.const 32))) (then
(unreachable)))
(set_local $v (get_local $x4))
(get_local $v)
@ -107,9 +107,9 @@
(param $x4 i64)
(result i64)
(local $v i64)
(if (i64.ne (i64.const 0) (i64.or (i64.or (get_local $x1) (get_local $x2)) (get_local $x3))) (then
(if (i64.ne (get_local $v) (i64.or (i64.or (get_local $x1) (get_local $x2)) (get_local $x3))) (then
(unreachable)))
(if (i64.ne (i64.const 0) (i64.shr_u (get_local $x4) (i64.const 32))) (then
(if (i64.ne (get_local $v) (i64.shr_u (get_local $x4) (i64.const 32))) (then
(unreachable)))
(set_local $v (get_local $x4))
(get_local $v)

View File

@ -836,6 +836,20 @@ BOOST_AUTO_TEST_CASE(shift_constantinople_warning)
CHECK_PARSE_WARNING("{ pop(sar(10, 32)) }", TypeError, "The \"sar\" instruction is only available for Constantinople-compatible VMs");
}
BOOST_AUTO_TEST_CASE(chainid_instanbul_warning)
{
if (dev::test::Options::get().evmVersion().hasChainID())
return;
CHECK_PARSE_WARNING("{ pop(chainid()) }", TypeError, "The \"chainid\" instruction is only available for Istanbul-compatible VMs");
}
BOOST_AUTO_TEST_CASE(selfbalance_instanbul_warning)
{
if (dev::test::Options::get().evmVersion().hasSelfBalance())
return;
CHECK_PARSE_WARNING("{ pop(selfbalance()) }", TypeError, "The \"selfbalance\" instruction is only available for Istanbul-compatible VMs");
}
BOOST_AUTO_TEST_CASE(jump_warning)
{
CHECK_PARSE_WARNING("{ 1 jump }", Warning, "Jump instructions");

View File

@ -17,9 +17,9 @@ contract C {
// optimize-yul: true
// ----
// creation:
// codeDepositCost: 624400
// executionCost: 657
// totalCost: 625057
// codeDepositCost: 608600
// executionCost: 645
// totalCost: 609245
// external:
// a(): 429
// b(uint256): 884

View File

@ -0,0 +1,15 @@
contract C {
function f() pure external {
assembly {
pop(chainid())
}
}
function g() view external {
assembly {
pop(selfbalance())
}
}
}
// ====
// EVMVersion: >=istanbul
// ----

View File

@ -36,6 +36,7 @@
#include <libyul/optimiser/ExpressionInliner.h>
#include <libyul/optimiser/FullInliner.h>
#include <libyul/optimiser/ForLoopConditionIntoBody.h>
#include <libyul/optimiser/ForLoopConditionOutOfBody.h>
#include <libyul/optimiser/ForLoopInitRewriter.h>
#include <libyul/optimiser/LoadResolver.h>
#include <libyul/optimiser/MainFunction.h>

View File

@ -0,0 +1,13 @@
{
for {} div(create(0, 1, 0), shl(msize(), 1)) {}
{
}
}
// ====
// step: expressionSimplifier
// EVMVersion: >byzantium
// ----
// {
// for { } div(create(0, 1, 0), shl(msize(), 1)) { }
// { }
// }

View File

@ -465,21 +465,19 @@
// let _1 := 0
// let _2 := mload(_1)
// let pos := 0x20
// let pos_1 := pos
// let length := mload(_2)
// mstore(pos, length)
// pos := 64
// let srcPtr := add(_2, pos_1)
// let srcPtr := add(_2, 0x20)
// let i := _1
// for { } 1 { i := add(i, 1) }
// for { } lt(i, length) { i := add(i, 1) }
// {
// if iszero(lt(i, length)) { break }
// abi_encode_t_array$_t_contract$_C_$55_$3_memory_to_t_array$_t_address_$3_memory_ptr(mload(srcPtr), pos)
// srcPtr := add(srcPtr, pos_1)
// srcPtr := add(srcPtr, 0x20)
// pos := add(pos, 0x60)
// }
// let _3 := mload(64)
// let _4 := mload(pos_1)
// let _4 := mload(0x20)
// if slt(sub(_3, _4), 128) { revert(_1, _1) }
// let offset := calldataload(add(_4, 64))
// let _5 := 0xffffffffffffffff
@ -488,7 +486,7 @@
// let offset_1 := calldataload(add(_4, 96))
// if gt(offset_1, _5) { revert(_1, _1) }
// let value3 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(_4, offset_1), _3)
// sstore(calldataload(_4), calldataload(add(_4, pos_1)))
// sstore(calldataload(_4), calldataload(add(_4, 0x20)))
// sstore(value2, value3)
// sstore(_1, pos)
// }
@ -504,9 +502,8 @@
// let src := add(offset, _1)
// if gt(add(add(offset, mul(length, 0x40)), _1), end) { revert(0, 0) }
// let i := 0
// for { } 1 { i := add(i, 1) }
// for { } lt(i, length) { i := add(i, 1) }
// {
// if iszero(lt(i, length)) { break }
// if iszero(slt(add(src, 0x1f), end)) { revert(0, 0) }
// let dst_1 := allocateMemory(array_allocation_size_t_array$_t_uint256_$2_memory(0x2))
// let dst_2 := dst_1
@ -514,9 +511,8 @@
// let _2 := add(src, 0x40)
// if gt(_2, end) { revert(0, 0) }
// let i_1 := 0
// for { } 1 { i_1 := add(i_1, 1) }
// for { } lt(i_1, 0x2) { i_1 := add(i_1, 1) }
// {
// if iszero(lt(i_1, 0x2)) { break }
// mstore(dst_1, calldataload(src_1))
// dst_1 := add(dst_1, _1)
// src_1 := add(src_1, _1)
@ -538,9 +534,8 @@
// let src := add(offset, _1)
// if gt(add(add(offset, mul(length, _1)), _1), end) { revert(0, 0) }
// let i := 0
// for { } 1 { i := add(i, 1) }
// for { } lt(i, length) { i := add(i, 1) }
// {
// if iszero(lt(i, length)) { break }
// mstore(dst, calldataload(src))
// dst := add(dst, _1)
// src := add(src, _1)
@ -550,9 +545,8 @@
// {
// let srcPtr := value
// let i := 0
// for { } 1 { i := add(i, 1) }
// for { } lt(i, 0x3) { i := add(i, 1) }
// {
// if iszero(lt(i, 0x3)) { break }
// mstore(pos, and(mload(srcPtr), sub(shl(160, 1), 1)))
// srcPtr := add(srcPtr, 0x20)
// pos := add(pos, 0x20)
@ -567,12 +561,12 @@
// }
// function array_allocation_size_t_array$_t_address_$dyn_memory(length) -> size
// {
// if gt(length, 0xffffffffffffffff) { revert(0, 0) }
// if gt(length, 0xffffffffffffffff) { revert(size, size) }
// size := add(mul(length, 0x20), 0x20)
// }
// function array_allocation_size_t_array$_t_uint256_$2_memory(length) -> size
// {
// if gt(length, 0xffffffffffffffff) { revert(0, 0) }
// if gt(length, 0xffffffffffffffff) { revert(size, size) }
// size := mul(length, 0x20)
// }
// }

View File

@ -233,13 +233,14 @@
// ----
// {
// {
// mstore(0x80, 7673901602397024137095011250362199966051872585513276903826533215767972925880)
// let _1 := 0x80
// mstore(_1, 7673901602397024137095011250362199966051872585513276903826533215767972925880)
// mstore(0xa0, 8489654445897228341090914135473290831551238522473825886865492707826370766375)
// let notes := add(0x04, calldataload(0x04))
// let m := calldataload(0x24)
// let n := calldataload(notes)
// let _1 := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
// let challenge := mod(calldataload(0x44), _1)
// let _2 := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001
// let challenge := mod(calldataload(0x44), _2)
// if gt(m, n)
// {
// mstore(0x00, 404)
@ -249,77 +250,75 @@
// mstore(0x2a0, caller())
// mstore(0x2c0, kn)
// mstore(0x2e0, m)
// kn := mulmod(sub(_1, kn), challenge, _1)
// kn := mulmod(sub(_2, kn), challenge, _2)
// hashCommitments(notes, n)
// let b := add(0x300, mul(n, 0x80))
// let b := add(0x300, mul(n, _1))
// let i := 0
// let i_1 := i
// for { } 1 { i := add(i, 0x01) }
// for { } lt(i, n) { i := add(i, 0x01) }
// {
// if iszero(lt(i, n)) { break }
// let _2 := add(calldataload(0x04), mul(i, 0xc0))
// let noteIndex := add(_2, 0x24)
// let k := i_1
// let a := calldataload(add(_2, 0x44))
// let _3 := add(calldataload(0x04), mul(i, 0xc0))
// let noteIndex := add(_3, 0x24)
// let k := 0
// let a := calldataload(add(_3, 0x44))
// let c := challenge
// let _3 := add(i, 0x01)
// switch eq(_3, n)
// let _4 := add(i, 0x01)
// switch eq(_4, n)
// case 1 {
// k := kn
// if eq(m, n) { k := sub(_1, kn) }
// if eq(m, n) { k := sub(_2, kn) }
// }
// case 0 { k := calldataload(noteIndex) }
// validateCommitment(noteIndex, k, a)
// switch gt(_3, m)
// switch gt(_4, m)
// case 1 {
// kn := addmod(kn, sub(_1, k), _1)
// let x := mod(mload(i_1), _1)
// k := mulmod(k, x, _1)
// a := mulmod(a, x, _1)
// c := mulmod(challenge, x, _1)
// mstore(i_1, keccak256(i_1, 0x20))
// kn := addmod(kn, sub(_2, k), _2)
// let x := mod(mload(0), _2)
// k := mulmod(k, x, _2)
// a := mulmod(a, x, _2)
// c := mulmod(challenge, x, _2)
// mstore(0, keccak256(0, 0x20))
// }
// case 0 { kn := addmod(kn, k, _1) }
// let _4 := 0x40
// calldatacopy(0xe0, add(_2, 164), _4)
// calldatacopy(0x20, add(_2, 100), _4)
// mstore(0x120, sub(_1, c))
// case 0 { kn := addmod(kn, k, _2) }
// let _5 := 0x40
// calldatacopy(0xe0, add(_3, 164), _5)
// calldatacopy(0x20, add(_3, 100), _5)
// mstore(0x120, sub(_2, c))
// mstore(0x60, k)
// mstore(0xc0, a)
// let result := call(gas(), 7, i_1, 0xe0, 0x60, 0x1a0, _4)
// let result_1 := and(result, call(gas(), 7, i_1, 0x20, 0x60, 0x120, _4))
// let result_2 := and(result_1, call(gas(), 7, i_1, 0x80, 0x60, 0x160, _4))
// let result_3 := and(result_2, call(gas(), 6, i_1, 0x120, 0x80, 0x160, _4))
// result := and(result_3, call(gas(), 6, i_1, 0x160, 0x80, b, _4))
// let result := call(gas(), 7, 0, 0xe0, 0x60, 0x1a0, _5)
// let result_1 := and(result, call(gas(), 7, 0, 0x20, 0x60, 0x120, _5))
// let result_2 := and(result_1, call(gas(), 7, 0, _1, 0x60, 0x160, _5))
// let result_3 := and(result_2, call(gas(), 6, 0, 0x120, _1, 0x160, _5))
// result := and(result_3, call(gas(), 6, 0, 0x160, _1, b, _5))
// if eq(i, m)
// {
// mstore(0x260, mload(0x20))
// mstore(0x280, mload(_4))
// mstore(0x280, mload(_5))
// mstore(0x1e0, mload(0xe0))
// mstore(0x200, sub(0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47, mload(0x100)))
// }
// if gt(i, m)
// {
// mstore(0x60, c)
// let result_4 := and(result, call(gas(), 7, i_1, 0x20, 0x60, 0x220, _4))
// let result_5 := and(result_4, call(gas(), 6, i_1, 0x220, 0x80, 0x260, _4))
// result := and(result_5, call(gas(), 6, i_1, 0x1a0, 0x80, 0x1e0, _4))
// let result_4 := and(result, call(gas(), 7, 0, 0x20, 0x60, 0x220, _5))
// let result_5 := and(result_4, call(gas(), 6, 0, 0x220, _1, 0x260, _5))
// result := and(result_5, call(gas(), 6, 0, 0x1a0, _1, 0x1e0, _5))
// }
// if iszero(result)
// {
// mstore(i_1, 400)
// revert(i_1, 0x20)
// mstore(0, 400)
// revert(0, 0x20)
// }
// b := add(b, _4)
// b := add(b, _5)
// }
// if lt(m, n) { validatePairing(0x64) }
// if iszero(eq(mod(keccak256(0x2a0, add(b, not(671))), _1), challenge))
// if iszero(eq(mod(keccak256(0x2a0, add(b, not(671))), _2), challenge))
// {
// mstore(i_1, 404)
// revert(i_1, 0x20)
// mstore(0, 404)
// revert(0, 0x20)
// }
// mstore(i_1, 0x01)
// return(i_1, 0x20)
// mstore(0, 0x01)
// return(0, 0x20)
// }
// function validatePairing(t2)
// {
@ -374,9 +373,8 @@
// function hashCommitments(notes, n)
// {
// let i := 0
// for { } 1 { i := add(i, 0x01) }
// for { } lt(i, n) { i := add(i, 0x01) }
// {
// if iszero(lt(i, n)) { break }
// calldatacopy(add(0x300, mul(i, 0x80)), add(add(notes, mul(i, 0xc0)), 0x60), 0x80)
// }
// mstore(0, keccak256(0x300, mul(n, 0x80)))

View File

@ -47,9 +47,9 @@
// }
// function abi_decode_t_bytes_calldata_ptr(offset, end) -> arrayPos, length
// {
// if iszero(slt(add(offset, 0x1f), end)) { revert(0, 0) }
// if iszero(slt(add(offset, 0x1f), end)) { revert(arrayPos, arrayPos) }
// length := calldataload(offset)
// if gt(length, 0xffffffffffffffff) { revert(0, 0) }
// if gt(length, 0xffffffffffffffff) { revert(arrayPos, arrayPos) }
// arrayPos := add(offset, 0x20)
// if gt(add(add(offset, length), 0x20), end) { revert(0, 0) }
// }

View File

@ -24,7 +24,7 @@
// for { } lt(mload(a), mload(b)) { a := mload(b) }
// {
// let b_4 := mload(a)
// let a_7 := mload(b_4)
// b := mload(a_7)
// a := mload(b_4)
// b := mload(a)
// }
// }

View File

@ -16,21 +16,25 @@
// {
// function copy(from, to) -> length
// {
// let length_1 := mload(from)
// let from_6 := from
// let to_7 := to
// let length_1 := mload(from_6)
// length := length_1
// mstore(to, length_1)
// let from_2 := add(from, 0x20)
// let to_3 := add(to, 0x20)
// mstore(to_7, length_1)
// let from_2 := add(from_6, 0x20)
// let to_3 := add(to_7, 0x20)
// let x_4 := 1
// let x := x_4
// for { }
// lt(x, length_1)
// {
// let x_5 := add(x, 0x20)
// let x_9 := x
// let x_5 := add(x_9, 0x20)
// x := x_5
// }
// {
// mstore(add(to_3, x), mload(add(from_2, x)))
// let x_8 := x
// mstore(add(to_3, x_8), mload(add(from_2, x_8)))
// }
// }
// }

View File

@ -20,7 +20,8 @@
// let a_3 := add(a_2, 1)
// a := a_3
// }
// let a_4 := add(a, 1)
// let a_5 := a
// let a_4 := add(a_5, 1)
// a := a_4
// mstore(a_4, 1)
// }

View File

@ -12,10 +12,17 @@
// {
// let a_1 := mload(0)
// let a := a_1
// for { mstore(0, a_1) } a { mstore(0, a) }
// for { mstore(0, a_1) }
// a
// {
// let a_2 := add(a, 3)
// let a_4 := a
// mstore(0, a_4)
// }
// {
// let a_3 := a
// let a_2 := add(a_3, 3)
// a := a_2
// }
// mstore(0, a)
// let a_5 := a
// mstore(0, a_5)
// }

View File

@ -17,7 +17,14 @@
// a := a_2
// }
// a
// { mstore(0, a) }
// { mstore(0, a) }
// mstore(0, a)
// {
// let a_4 := a
// mstore(0, a_4)
// }
// {
// let a_3 := a
// mstore(0, a_3)
// }
// let a_5 := a
// mstore(0, a_5)
// }

View File

@ -15,9 +15,14 @@
// for { mstore(0, a_1) }
// a
// {
// let a_2 := add(a, 3)
// let a_4 := a
// let a_2 := add(a_4, 3)
// a := a_2
// }
// { mstore(0, a) }
// mstore(0, a)
// {
// let a_3 := a
// mstore(0, a_3)
// }
// let a_5 := a
// mstore(0, a_5)
// }

View File

@ -26,23 +26,28 @@
// let a_3 := add(a_2, 2)
// a := a_3
// }
// let a_9 := a
// {
// let a_4 := add(a, 4)
// let a_4 := add(a_9, 4)
// a := a_4
// }
// let a_10 := a
// for {
// let a_5 := add(a, 3)
// let a_5 := add(a_10, 3)
// a := a_5
// }
// a
// {
// let a_6 := add(a, 6)
// let a_12 := a
// let a_6 := add(a_12, 6)
// a := a_6
// }
// {
// let a_7 := add(a, 12)
// let a_11 := a
// let a_7 := add(a_11, 12)
// a := a_7
// }
// let a_8 := add(a, 8)
// let a_13 := a
// let a_8 := add(a_13, 8)
// a := a_8
// }

View File

@ -12,13 +12,15 @@
// {
// function f(a, b) -> c, d
// {
// let b_1 := add(b, a)
// let b_5 := b
// let a_6 := a
// let b_1 := add(b_5, a_6)
// b := b_1
// let c_2 := add(c, b_1)
// c := c_2
// let d_3 := add(d, c_2)
// d := d_3
// let a_4 := add(a, d_3)
// let a_4 := add(a_6, d_3)
// a := a_4
// }
// }

View File

@ -28,6 +28,7 @@
// let a_6 := 4
// a := a_6
// }
// let a_7 := add(b_4, a)
// let a_8 := a
// let a_7 := add(b_4, a_8)
// a := a_7
// }

View File

@ -0,0 +1,32 @@
{
let a
let b
let x
if a {
if b {
x := 2
}
}
// Should create new SSA variables for x here,
// but not above because end of block
mstore(0, x)
}
// ====
// step: ssaTransform
// ----
// {
// let a
// let b
// let x_1
// let x := x_1
// if a
// {
// if b
// {
// let x_2 := 2
// x := x_2
// }
// }
// let x_3 := x
// mstore(0, x_3)
// }

View File

@ -20,8 +20,10 @@
// a := a_2
// }
// default {
// let a_3 := add(a, 8)
// let a_4 := a
// let a_3 := add(a_4, 8)
// a := a_3
// }
// mstore(0, a)
// let a_5 := a
// mstore(0, a_5)
// }

View File

@ -0,0 +1,23 @@
{
let a := mload(0)
switch a
case 0 { a := add(a, 4) }
default { }
// should still create an SSA variable for a
mstore(0, a)
}
// ====
// step: ssaTransform
// ----
// {
// let a_1 := mload(0)
// let a := a_1
// switch a_1
// case 0 {
// let a_2 := add(a_1, 4)
// a := a_2
// }
// default { }
// let a_3 := a
// mstore(0, a_3)
// }

View File

@ -33,7 +33,8 @@
// a := a_4
// mstore(a_4, 0)
// }
// mstore(a, 0)
// let a_6 := a
// mstore(a_6, 0)
// let a_5 := 4
// a := a_5
// mstore(a_5, 0)

View File

@ -28,6 +28,15 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
static vector<string> s_evmVersions = {
"homestead",
"tangerineWhistle",
"spuriousDragon",
"byzantium",
"constantinople",
"petersburg"
};
void FuzzerUtil::runCompiler(string const& _input, bool _quiet)
{
if (!_quiet)
@ -76,6 +85,7 @@ void FuzzerUtil::testCompiler(string const& _input, bool _optimize, bool _quiet)
config["settings"]["optimizer"] = Json::objectValue;
config["settings"]["optimizer"]["enabled"] = _optimize;
config["settings"]["optimizer"]["runs"] = 200;
config["settings"]["evmVersion"] = s_evmVersions[_input.size() % s_evmVersions.size()];
// Enable all SourceUnit-level outputs.
config["settings"]["outputSelection"]["*"][""][0] = "*";

View File

@ -19,27 +19,27 @@ endif()
if (OSSFUZZ)
add_executable(solc_opt_ossfuzz solc_opt_ossfuzz.cpp ../fuzzer_common.cpp)
target_link_libraries(solc_opt_ossfuzz PRIVATE libsolc evmasm)
set_target_properties(solc_opt_ossfuzz PROPERTIES LINK_FLAGS "-fsanitize=fuzzer")
set_target_properties(solc_opt_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
add_executable(solc_noopt_ossfuzz solc_noopt_ossfuzz.cpp ../fuzzer_common.cpp)
target_link_libraries(solc_noopt_ossfuzz PRIVATE libsolc evmasm)
set_target_properties(solc_noopt_ossfuzz PROPERTIES LINK_FLAGS "-fsanitize=fuzzer")
set_target_properties(solc_noopt_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
add_executable(const_opt_ossfuzz const_opt_ossfuzz.cpp ../fuzzer_common.cpp)
target_link_libraries(const_opt_ossfuzz PRIVATE libsolc evmasm)
set_target_properties(const_opt_ossfuzz PROPERTIES LINK_FLAGS "-fsanitize=fuzzer")
set_target_properties(const_opt_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
add_executable(strictasm_diff_ossfuzz strictasm_diff_ossfuzz.cpp yulFuzzerCommon.cpp)
target_link_libraries(strictasm_diff_ossfuzz PRIVATE libsolc evmasm yulInterpreter)
set_target_properties(strictasm_diff_ossfuzz PROPERTIES LINK_FLAGS "-fsanitize=fuzzer")
set_target_properties(strictasm_diff_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
add_executable(strictasm_opt_ossfuzz strictasm_opt_ossfuzz.cpp)
target_link_libraries(strictasm_opt_ossfuzz PRIVATE yul)
set_target_properties(strictasm_opt_ossfuzz PROPERTIES LINK_FLAGS "-fsanitize=fuzzer")
set_target_properties(strictasm_opt_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
add_executable(strictasm_assembly_ossfuzz strictasm_assembly_ossfuzz.cpp)
target_link_libraries(strictasm_assembly_ossfuzz PRIVATE yul)
set_target_properties(strictasm_assembly_ossfuzz PROPERTIES LINK_FLAGS "-fsanitize=fuzzer")
set_target_properties(strictasm_assembly_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
add_executable(yul_proto_ossfuzz yulProtoFuzzer.cpp protoToYul.cpp yulProto.pb.cc)
target_include_directories(yul_proto_ossfuzz PRIVATE /usr/include/libprotobuf-mutator)
@ -48,7 +48,7 @@ if (OSSFUZZ)
protobuf-mutator.a
protobuf.a
)
set_target_properties(yul_proto_ossfuzz PROPERTIES LINK_FLAGS "-fsanitize=fuzzer")
set_target_properties(yul_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
add_executable(yul_proto_diff_ossfuzz yulProto_diff_ossfuzz.cpp yulFuzzerCommon.cpp protoToYul.cpp yulProto.pb.cc)
target_include_directories(yul_proto_diff_ossfuzz PRIVATE /usr/include/libprotobuf-mutator)
@ -58,7 +58,7 @@ if (OSSFUZZ)
protobuf-mutator.a
protobuf.a
)
set_target_properties(yul_proto_diff_ossfuzz PROPERTIES LINK_FLAGS "-fsanitize=fuzzer")
set_target_properties(yul_proto_diff_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
add_executable(abiv2_proto_ossfuzz
../../EVMHost.cpp
@ -76,7 +76,7 @@ if (OSSFUZZ)
protobuf-mutator.a
protobuf.a
)
set_target_properties(abiv2_proto_ossfuzz PROPERTIES LINK_FLAGS "-fsanitize=fuzzer")
set_target_properties(abiv2_proto_ossfuzz PROPERTIES LINK_FLAGS ${LIB_FUZZING_ENGINE})
else()
add_library(solc_opt_ossfuzz
solc_opt_ossfuzz.cpp

View File

@ -43,6 +43,7 @@
#include <libyul/optimiser/ExpressionInliner.h>
#include <libyul/optimiser/FullInliner.h>
#include <libyul/optimiser/ForLoopConditionIntoBody.h>
#include <libyul/optimiser/ForLoopConditionOutOfBody.h>
#include <libyul/optimiser/ForLoopInitRewriter.h>
#include <libyul/optimiser/MainFunction.h>
#include <libyul/optimiser/Rematerialiser.h>
@ -134,8 +135,8 @@ public:
}
cout << "(q)quit/(f)flatten/(c)se/initialize var(d)ecls/(x)plit/(j)oin/(g)rouper/(h)oister/" << endl;
cout << " (e)xpr inline/(i)nline/(s)implify/varname c(l)eaner/(u)nusedprune/ss(a) transform/" << endl;
cout << " (r)edundant assign elim./re(m)aterializer/f(o)r-loop-init-rewriter/f(O)r-loop-condition-into-body/" << endl;
cout << " s(t)ructural simplifier/equi(v)alent function combiner/ssa re(V)erser/? " << endl;
cout << " (r)edundant assign elim./re(m)aterializer/f(o)r-loop-init-rewriter/for-loop-condition-(I)nto-body/" << endl;
cout << " for-loop-condition-(O)ut-of-body/s(t)ructural simplifier/equi(v)alent function combiner/ssa re(V)erser/" << endl;
cout << " co(n)trol flow simplifier/stack com(p)ressor/(D)ead code eliminator/(L)oad resolver/? " << endl;
cout.flush();
int option = readStandardInputChar();
@ -151,6 +152,9 @@ public:
ForLoopInitRewriter{}(*m_ast);
break;
case 'O':
ForLoopConditionOutOfBody{m_dialect}(*m_ast);
break;
case 'I':
ForLoopConditionIntoBody{m_dialect}(*m_ast);
break;
case 'c':