mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #7353 from ethereum/develop
Update 0.6.0 from develop
This commit is contained in:
commit
fd687f50ff
@ -176,6 +176,28 @@ defaults:
|
||||
requires:
|
||||
- b_ubu_ossfuzz
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# Notification Templates
|
||||
- gitter_notify_failure: &gitter_notify_failure
|
||||
name: Gitter notify failure
|
||||
command: >-
|
||||
curl -X POST -i
|
||||
-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."}'
|
||||
when: on_fail
|
||||
|
||||
- gitter_notify_success: &gitter_notify_success
|
||||
name: Gitter notify success
|
||||
command: >-
|
||||
curl -X POST -i
|
||||
-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."}'
|
||||
when: on_success
|
||||
|
||||
# -----------------------------------------------------------------------------------------------
|
||||
jobs:
|
||||
|
||||
@ -341,6 +363,8 @@ jobs:
|
||||
mkdir -p test_results
|
||||
export ASAN_OPTIONS="check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2"
|
||||
scripts/regressions.py -o test_results
|
||||
- run: *gitter_notify_failure
|
||||
- run: *gitter_notify_success
|
||||
- store_test_results: *store_test_results
|
||||
- store_artifacts: *artifacts_test_results
|
||||
|
||||
@ -526,6 +550,8 @@ jobs:
|
||||
name: External GnosisSafe tests
|
||||
command: |
|
||||
test/externalTests/gnosis.sh /tmp/workspace/soljson.js || test/externalTests/gnosis.sh /tmp/workspace/soljson.js
|
||||
- run: *gitter_notify_failure
|
||||
- run: *gitter_notify_success
|
||||
|
||||
t_ems_external_zeppelin:
|
||||
docker:
|
||||
@ -540,6 +566,8 @@ jobs:
|
||||
name: External Zeppelin tests
|
||||
command: |
|
||||
test/externalTests/zeppelin.sh /tmp/workspace/soljson.js || test/externalTests/zeppelin.sh /tmp/workspace/soljson.js
|
||||
- run: *gitter_notify_failure
|
||||
- run: *gitter_notify_success
|
||||
|
||||
t_ems_external_colony:
|
||||
docker:
|
||||
@ -558,6 +586,8 @@ jobs:
|
||||
name: External ColonyNetworks tests
|
||||
command: |
|
||||
test/externalTests/colony.sh /tmp/workspace/soljson.js || test/externalTests/colony.sh /tmp/workspace/soljson.js
|
||||
- run: *gitter_notify_failure
|
||||
- run: *gitter_notify_success
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
|
@ -22,7 +22,9 @@ Language Features:
|
||||
|
||||
|
||||
Compiler Features:
|
||||
* ABI Output: Change sorting order of functions from selector to kind, name.
|
||||
* Optimizer: Add rule that replaces the BYTE opcode by 0 if the first argument is larger than 31.
|
||||
* Yul Optimizer: Take side-effect-freeness of user-defined functions into account.
|
||||
|
||||
|
||||
Bugfixes:
|
||||
|
@ -171,7 +171,7 @@ AssemblyVariableDeclaration = 'let' AssemblyIdentifierList ( ':=' AssemblyExpres
|
||||
AssemblyAssignment = AssemblyIdentifierList ':=' AssemblyExpression
|
||||
AssemblyExpression = AssemblyFunctionCall | Identifier | Literal
|
||||
AssemblyIf = 'if' AssemblyExpression AssemblyBlock
|
||||
AssemblySwitch = 'switch' AssemblyExpression ( Case+ AssemblyDefault? | AssemblyDefault )
|
||||
AssemblySwitch = 'switch' AssemblyExpression ( AssemblyCase+ AssemblyDefault? | AssemblyDefault )
|
||||
AssemblyCase = 'case' Literal AssemblyBlock
|
||||
AssemblyDefault = 'default' AssemblyBlock
|
||||
AssemblyForLoop = 'for' AssemblyBlock AssemblyExpression AssemblyBlock AssemblyBlock
|
||||
|
@ -122,6 +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**)
|
||||
- ``berlin`` (**experimental**)
|
||||
|
||||
|
||||
.. _compiler-api:
|
||||
@ -229,7 +231,7 @@ Input Description
|
||||
},
|
||||
// Version of the EVM to compile for.
|
||||
// Affects type checking and code generation. Can be homestead,
|
||||
// tangerineWhistle, spuriousDragon, byzantium, constantinople or petersburg
|
||||
// tangerineWhistle, spuriousDragon, byzantium, constantinople, petersburg, istanbul or berlin
|
||||
"evmVersion": "byzantium",
|
||||
// Metadata settings (optional)
|
||||
"metadata": {
|
||||
|
@ -596,12 +596,14 @@ LinkerObject const& Assembly::assemble() const
|
||||
ret.bytecode.resize(ret.bytecode.size() + bytesPerDataRef);
|
||||
break;
|
||||
case PushSub:
|
||||
assertThrow(i.data() <= size_t(-1), AssemblyException, "");
|
||||
ret.bytecode.push_back(dataRefPush);
|
||||
subRef.insert(make_pair(size_t(i.data()), ret.bytecode.size()));
|
||||
ret.bytecode.resize(ret.bytecode.size() + bytesPerDataRef);
|
||||
break;
|
||||
case PushSubSize:
|
||||
{
|
||||
assertThrow(i.data() <= size_t(-1), AssemblyException, "");
|
||||
auto s = m_subs.at(size_t(i.data()))->assemble().bytecode.size();
|
||||
i.setPushedValue(u256(s));
|
||||
uint8_t b = max<unsigned>(1, dev::bytesRequired(s));
|
||||
|
@ -48,10 +48,12 @@ public:
|
||||
static EVMVersion byzantium() { return {Version::Byzantium}; }
|
||||
static EVMVersion constantinople() { return {Version::Constantinople}; }
|
||||
static EVMVersion petersburg() { return {Version::Petersburg}; }
|
||||
static EVMVersion istanbul() { return {Version::Istanbul}; }
|
||||
static EVMVersion berlin() { return {Version::Berlin}; }
|
||||
|
||||
static boost::optional<EVMVersion> fromString(std::string const& _version)
|
||||
{
|
||||
for (auto const& v: {homestead(), tangerineWhistle(), spuriousDragon(), byzantium(), constantinople(), petersburg()})
|
||||
for (auto const& v: {homestead(), tangerineWhistle(), spuriousDragon(), byzantium(), constantinople(), petersburg(), istanbul(), berlin()})
|
||||
if (_version == v.name())
|
||||
return v;
|
||||
return {};
|
||||
@ -70,6 +72,8 @@ public:
|
||||
case Version::Byzantium: return "byzantium";
|
||||
case Version::Constantinople: return "constantinople";
|
||||
case Version::Petersburg: return "petersburg";
|
||||
case Version::Istanbul: return "istanbul";
|
||||
case Version::Berlin: return "berlin";
|
||||
}
|
||||
return "INVALID";
|
||||
}
|
||||
@ -88,7 +92,7 @@ public:
|
||||
bool canOverchargeGasForCall() const { return *this >= tangerineWhistle(); }
|
||||
|
||||
private:
|
||||
enum class Version { Homestead, TangerineWhistle, SpuriousDragon, Byzantium, Constantinople, Petersburg };
|
||||
enum class Version { Homestead, TangerineWhistle, SpuriousDragon, Byzantium, Constantinople, Petersburg, Istanbul, Berlin };
|
||||
|
||||
EVMVersion(Version _version): m_version(_version) {}
|
||||
|
||||
|
@ -137,6 +137,8 @@ CVC4::Expr CVC4Interface::toCVC4Expr(Expression const& _expr)
|
||||
return m_context.mkConst(true);
|
||||
else if (n == "false")
|
||||
return m_context.mkConst(false);
|
||||
else if (auto sortSort = dynamic_pointer_cast<SortSort>(_expr.sort))
|
||||
return m_context.mkVar(n, cvc4Sort(*sortSort->inner));
|
||||
else
|
||||
try
|
||||
{
|
||||
@ -187,6 +189,12 @@ CVC4::Expr CVC4Interface::toCVC4Expr(Expression const& _expr)
|
||||
return m_context.mkExpr(CVC4::kind::SELECT, arguments[0], arguments[1]);
|
||||
else if (n == "store")
|
||||
return m_context.mkExpr(CVC4::kind::STORE, arguments[0], arguments[1], arguments[2]);
|
||||
else if (n == "const_array")
|
||||
{
|
||||
shared_ptr<SortSort> sortSort = std::dynamic_pointer_cast<SortSort>(_expr.arguments[0].sort);
|
||||
solAssert(sortSort, "");
|
||||
return m_context.mkConst(CVC4::ArrayStoreAll(cvc4Sort(*sortSort->inner), arguments[1]));
|
||||
}
|
||||
|
||||
solAssert(false, "");
|
||||
}
|
||||
|
@ -17,8 +17,6 @@
|
||||
|
||||
#include <libsolidity/formal/SMTLib2Interface.h>
|
||||
|
||||
#include <libsolidity/interface/ReadFile.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <libdevcore/Keccak256.h>
|
||||
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
@ -30,7 +28,6 @@
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
@ -96,12 +93,12 @@ void SMTLib2Interface::declareFunction(string const& _name, Sort const& _sort)
|
||||
}
|
||||
}
|
||||
|
||||
void SMTLib2Interface::addAssertion(Expression const& _expr)
|
||||
void SMTLib2Interface::addAssertion(smt::Expression const& _expr)
|
||||
{
|
||||
write("(assert " + toSExpr(_expr) + ")");
|
||||
}
|
||||
|
||||
pair<CheckResult, vector<string>> SMTLib2Interface::check(vector<Expression> const& _expressionsToEvaluate)
|
||||
pair<CheckResult, vector<string>> SMTLib2Interface::check(vector<smt::Expression> const& _expressionsToEvaluate)
|
||||
{
|
||||
string response = querySolver(
|
||||
boost::algorithm::join(m_accumulatedOutput, "\n") +
|
||||
@ -125,7 +122,7 @@ pair<CheckResult, vector<string>> SMTLib2Interface::check(vector<Expression> con
|
||||
return make_pair(result, values);
|
||||
}
|
||||
|
||||
string SMTLib2Interface::toSExpr(Expression const& _expr)
|
||||
string SMTLib2Interface::toSExpr(smt::Expression const& _expr)
|
||||
{
|
||||
if (_expr.arguments.empty())
|
||||
return _expr.name;
|
||||
@ -169,7 +166,7 @@ void SMTLib2Interface::write(string _data)
|
||||
m_accumulatedOutput.back() += move(_data) + "\n";
|
||||
}
|
||||
|
||||
string SMTLib2Interface::checkSatAndGetValuesCommand(vector<Expression> const& _expressionsToEvaluate)
|
||||
string SMTLib2Interface::checkSatAndGetValuesCommand(vector<smt::Expression> const& _expressionsToEvaluate)
|
||||
{
|
||||
string command;
|
||||
if (_expressionsToEvaluate.empty())
|
||||
|
@ -50,21 +50,21 @@ public:
|
||||
|
||||
void declareVariable(std::string const&, Sort const&) override;
|
||||
|
||||
void addAssertion(Expression const& _expr) override;
|
||||
std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override;
|
||||
void addAssertion(smt::Expression const& _expr) override;
|
||||
std::pair<CheckResult, std::vector<std::string>> check(std::vector<smt::Expression> const& _expressionsToEvaluate) override;
|
||||
|
||||
std::vector<std::string> unhandledQueries() override { return m_unhandledQueries; }
|
||||
|
||||
private:
|
||||
void declareFunction(std::string const&, Sort const&);
|
||||
|
||||
std::string toSExpr(Expression const& _expr);
|
||||
std::string toSExpr(smt::Expression const& _expr);
|
||||
std::string toSmtLibSort(Sort const& _sort);
|
||||
std::string toSmtLibSort(std::vector<SortPointer> const& _sort);
|
||||
|
||||
void write(std::string _data);
|
||||
|
||||
std::string checkSatAndGetValuesCommand(std::vector<Expression> const& _expressionsToEvaluate);
|
||||
std::string checkSatAndGetValuesCommand(std::vector<smt::Expression> const& _expressionsToEvaluate);
|
||||
std::vector<std::string> parseValues(std::string::const_iterator _start, std::string::const_iterator _end);
|
||||
|
||||
/// Communicates with the solver via the callback. Throws SMTSolverError on error.
|
||||
|
@ -65,7 +65,7 @@ void SMTPortfolio::declareVariable(string const& _name, Sort const& _sort)
|
||||
s->declareVariable(_name, _sort);
|
||||
}
|
||||
|
||||
void SMTPortfolio::addAssertion(Expression const& _expr)
|
||||
void SMTPortfolio::addAssertion(smt::Expression const& _expr)
|
||||
{
|
||||
for (auto const& s: m_solvers)
|
||||
s->addAssertion(_expr);
|
||||
@ -101,7 +101,7 @@ void SMTPortfolio::addAssertion(Expression const& _expr)
|
||||
*
|
||||
* If all solvers return ERROR, the result is ERROR.
|
||||
*/
|
||||
pair<CheckResult, vector<string>> SMTPortfolio::check(vector<Expression> const& _expressionsToEvaluate)
|
||||
pair<CheckResult, vector<string>> SMTPortfolio::check(vector<smt::Expression> const& _expressionsToEvaluate)
|
||||
{
|
||||
CheckResult lastResult = CheckResult::ERROR;
|
||||
vector<string> finalValues;
|
||||
|
@ -51,9 +51,9 @@ public:
|
||||
|
||||
void declareVariable(std::string const&, Sort const&) override;
|
||||
|
||||
void addAssertion(Expression const& _expr) override;
|
||||
void addAssertion(smt::Expression const& _expr) override;
|
||||
|
||||
std::pair<CheckResult, std::vector<std::string>> check(std::vector<Expression> const& _expressionsToEvaluate) override;
|
||||
std::pair<CheckResult, std::vector<std::string>> check(std::vector<smt::Expression> const& _expressionsToEvaluate) override;
|
||||
|
||||
std::vector<std::string> unhandledQueries() override;
|
||||
unsigned solvers() override { return m_solvers.size(); }
|
||||
@ -62,7 +62,7 @@ private:
|
||||
|
||||
std::vector<std::unique_ptr<smt::SolverInterface>> m_solvers;
|
||||
|
||||
std::vector<Expression> m_assertions;
|
||||
std::vector<smt::Expression> m_assertions;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/ast/Types.h>
|
||||
#include <libsolidity/interface/ReadFile.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <libdevcore/Common.h>
|
||||
@ -45,7 +46,8 @@ enum class Kind
|
||||
Int,
|
||||
Bool,
|
||||
Function,
|
||||
Array
|
||||
Array,
|
||||
Sort
|
||||
};
|
||||
|
||||
struct Sort
|
||||
@ -110,12 +112,33 @@ struct ArraySort: public Sort
|
||||
SortPointer range;
|
||||
};
|
||||
|
||||
struct SortSort: public Sort
|
||||
{
|
||||
SortSort(SortPointer _inner): Sort(Kind::Sort), inner(std::move(_inner)) {}
|
||||
bool operator==(Sort const& _other) const override
|
||||
{
|
||||
if (!Sort::operator==(_other))
|
||||
return false;
|
||||
auto _otherSort = dynamic_cast<SortSort const*>(&_other);
|
||||
solAssert(_otherSort, "");
|
||||
solAssert(_otherSort->inner, "");
|
||||
solAssert(inner, "");
|
||||
return *inner == *_otherSort->inner;
|
||||
}
|
||||
|
||||
SortPointer inner;
|
||||
};
|
||||
|
||||
// Forward declaration.
|
||||
SortPointer smtSort(solidity::Type const& _type);
|
||||
|
||||
/// C++ representation of an SMTLIB2 expression.
|
||||
class Expression
|
||||
{
|
||||
friend class SolverInterface;
|
||||
public:
|
||||
explicit Expression(bool _v): Expression(_v ? "true" : "false", Kind::Bool) {}
|
||||
explicit Expression(solidity::TypePointer _type): Expression(_type->toString(), {}, std::make_shared<SortSort>(smtSort(*_type))) {}
|
||||
Expression(size_t _number): Expression(std::to_string(_number), Kind::Int) {}
|
||||
Expression(u256 const& _number): Expression(_number.str(), Kind::Int) {}
|
||||
Expression(s256 const& _number): Expression(_number.str(), Kind::Int) {}
|
||||
@ -145,7 +168,8 @@ public:
|
||||
{"/", 2},
|
||||
{"mod", 2},
|
||||
{"select", 2},
|
||||
{"store", 3}
|
||||
{"store", 3},
|
||||
{"const_array", 2}
|
||||
};
|
||||
return operatorsArity.count(name) && operatorsArity.at(name) == arguments.size();
|
||||
}
|
||||
@ -202,6 +226,21 @@ public:
|
||||
);
|
||||
}
|
||||
|
||||
static Expression const_array(Expression _sort, Expression _value)
|
||||
{
|
||||
solAssert(_sort.sort->kind == Kind::Sort, "");
|
||||
auto sortSort = std::dynamic_pointer_cast<SortSort>(_sort.sort);
|
||||
auto arraySort = std::dynamic_pointer_cast<ArraySort>(sortSort->inner);
|
||||
solAssert(sortSort && arraySort, "");
|
||||
solAssert(_value.sort, "");
|
||||
solAssert(*arraySort->range == *_value.sort, "");
|
||||
return Expression(
|
||||
"const_array",
|
||||
std::vector<Expression>{std::move(_sort), std::move(_value)},
|
||||
arraySort
|
||||
);
|
||||
}
|
||||
|
||||
friend Expression operator!(Expression _a)
|
||||
{
|
||||
return Expression("not", std::move(_a), Kind::Bool);
|
||||
|
@ -276,10 +276,31 @@ void setSymbolicZeroValue(SymbolicVariable const& _variable, EncodingContext& _c
|
||||
void setSymbolicZeroValue(Expression _expr, solidity::TypePointer const& _type, EncodingContext& _context)
|
||||
{
|
||||
solAssert(_type, "");
|
||||
_context.addAssertion(_expr == zeroValue(_type));
|
||||
}
|
||||
|
||||
Expression zeroValue(solidity::TypePointer const& _type)
|
||||
{
|
||||
solAssert(_type, "");
|
||||
if (isSupportedType(_type->category()))
|
||||
{
|
||||
if (isNumber(_type->category()))
|
||||
_context.addAssertion(_expr == 0);
|
||||
else if (isBool(_type->category()))
|
||||
_context.addAssertion(_expr == Expression(false));
|
||||
return 0;
|
||||
if (isBool(_type->category()))
|
||||
return Expression(false);
|
||||
if (isArray(_type->category()) || isMapping(_type->category()))
|
||||
{
|
||||
if (auto arrayType = dynamic_cast<ArrayType const*>(_type))
|
||||
return Expression::const_array(Expression(arrayType), zeroValue(arrayType->baseType()));
|
||||
auto mappingType = dynamic_cast<MappingType const*>(_type);
|
||||
solAssert(mappingType, "");
|
||||
return Expression::const_array(Expression(mappingType), zeroValue(mappingType->valueType()));
|
||||
|
||||
}
|
||||
solAssert(false, "");
|
||||
}
|
||||
// Unsupported types are abstracted as Int.
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setSymbolicUnknownValue(SymbolicVariable const& _variable, EncodingContext& _context)
|
||||
|
@ -63,6 +63,7 @@ std::pair<bool, std::shared_ptr<SymbolicVariable>> newSymbolicVariable(solidity:
|
||||
|
||||
Expression minValue(solidity::IntegerType const& _type);
|
||||
Expression maxValue(solidity::IntegerType const& _type);
|
||||
Expression zeroValue(solidity::TypePointer const& _type);
|
||||
|
||||
void setSymbolicZeroValue(SymbolicVariable const& _variable, EncodingContext& _context);
|
||||
void setSymbolicZeroValue(Expression _expr, solidity::TypePointer const& _type, EncodingContext& _context);
|
||||
|
@ -33,6 +33,17 @@ Z3CHCInterface::Z3CHCInterface():
|
||||
z3::set_param("rewriter.pull_cheap_ite", true);
|
||||
// This needs to be set in the context.
|
||||
m_context->set("timeout", queryTimeout);
|
||||
|
||||
// Spacer options.
|
||||
// These needs to be set in the solver.
|
||||
// https://github.com/Z3Prover/z3/blob/master/src/muz/base/fp_params.pyg
|
||||
z3::params p(*m_context);
|
||||
// These are useful for solving problems with arrays and loops.
|
||||
// Use quantified lemma generalizer.
|
||||
p.set("fp.spacer.q3.use_qgen", true);
|
||||
// Ground pobs by using values from a model.
|
||||
p.set("fp.spacer.ground_pobs", false);
|
||||
m_solver.set(p);
|
||||
}
|
||||
|
||||
void Z3CHCInterface::declareVariable(string const& _name, Sort const& _sort)
|
||||
@ -82,9 +93,11 @@ pair<CheckResult, vector<string>> Z3CHCInterface::query(Expression const& _expr)
|
||||
break;
|
||||
}
|
||||
case z3::check_result::unknown:
|
||||
{
|
||||
result = CheckResult::UNKNOWN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// TODO retrieve model / invariants
|
||||
}
|
||||
catch (z3::exception const& _e)
|
||||
|
@ -132,6 +132,12 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr)
|
||||
return m_context.bool_val(true);
|
||||
else if (n == "false")
|
||||
return m_context.bool_val(false);
|
||||
else if (_expr.sort->kind == Kind::Sort)
|
||||
{
|
||||
auto sortSort = dynamic_pointer_cast<SortSort>(_expr.sort);
|
||||
solAssert(sortSort, "");
|
||||
return m_context.constant(n.c_str(), z3Sort(*sortSort->inner));
|
||||
}
|
||||
else
|
||||
try
|
||||
{
|
||||
@ -178,6 +184,14 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr)
|
||||
return z3::select(arguments[0], arguments[1]);
|
||||
else if (n == "store")
|
||||
return z3::store(arguments[0], arguments[1], arguments[2]);
|
||||
else if (n == "const_array")
|
||||
{
|
||||
shared_ptr<SortSort> sortSort = std::dynamic_pointer_cast<SortSort>(_expr.arguments[0].sort);
|
||||
solAssert(sortSort, "");
|
||||
auto arraySort = dynamic_pointer_cast<ArraySort>(sortSort->inner);
|
||||
solAssert(arraySort && arraySort->domain, "");
|
||||
return z3::const_array(z3Sort(*arraySort->domain), arguments[1]);
|
||||
}
|
||||
|
||||
solAssert(false, "");
|
||||
}
|
||||
|
@ -40,7 +40,10 @@ bool anyDataStoredInStorage(TypePointers const& _pointers)
|
||||
|
||||
Json::Value ABI::generate(ContractDefinition const& _contractDef)
|
||||
{
|
||||
Json::Value abi(Json::arrayValue);
|
||||
auto compare = [](Json::Value const& _a, Json::Value const& _b) -> bool {
|
||||
return make_tuple(_a["type"], _a["name"]) < make_tuple(_b["type"], _b["name"]);
|
||||
};
|
||||
multiset<Json::Value, decltype(compare)> abi(compare);
|
||||
|
||||
for (auto it: _contractDef.interfaceFunctions())
|
||||
{
|
||||
@ -71,7 +74,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef)
|
||||
it.second->returnParameterTypes(),
|
||||
_contractDef.isLibrary()
|
||||
);
|
||||
abi.append(std::move(method));
|
||||
abi.emplace(std::move(method));
|
||||
}
|
||||
if (_contractDef.constructor())
|
||||
{
|
||||
@ -88,7 +91,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef)
|
||||
constrType.parameterTypes(),
|
||||
_contractDef.isLibrary()
|
||||
);
|
||||
abi.append(std::move(method));
|
||||
abi.emplace(std::move(method));
|
||||
}
|
||||
if (_contractDef.fallbackFunction())
|
||||
{
|
||||
@ -98,7 +101,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef)
|
||||
method["type"] = "fallback";
|
||||
method["payable"] = externalFunctionType->isPayable();
|
||||
method["stateMutability"] = stateMutabilityToString(externalFunctionType->stateMutability());
|
||||
abi.append(std::move(method));
|
||||
abi.emplace(std::move(method));
|
||||
}
|
||||
for (auto const& it: _contractDef.interfaceEvents())
|
||||
{
|
||||
@ -117,10 +120,13 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef)
|
||||
params.append(std::move(param));
|
||||
}
|
||||
event["inputs"] = std::move(params);
|
||||
abi.append(std::move(event));
|
||||
abi.emplace(std::move(event));
|
||||
}
|
||||
|
||||
return abi;
|
||||
Json::Value abiJson{Json::arrayValue};
|
||||
for (auto& f: abi)
|
||||
abiJson.append(std::move(f));
|
||||
return abiJson;
|
||||
}
|
||||
|
||||
Json::Value ABI::formatTypeList(
|
||||
|
@ -23,6 +23,9 @@
|
||||
|
||||
#include <libyul/optimiser/Metrics.h>
|
||||
#include <libyul/optimiser/SyntacticalEquality.h>
|
||||
#include <libyul/optimiser/CallGraphGenerator.h>
|
||||
#include <libyul/optimiser/Semantics.h>
|
||||
#include <libyul/SideEffects.h>
|
||||
#include <libyul/Exceptions.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <libyul/Dialect.h>
|
||||
@ -31,6 +34,23 @@ using namespace std;
|
||||
using namespace dev;
|
||||
using namespace yul;
|
||||
|
||||
void CommonSubexpressionEliminator::run(Dialect const& _dialect, Block& _ast)
|
||||
{
|
||||
CommonSubexpressionEliminator cse{
|
||||
_dialect,
|
||||
SideEffectsPropagator::sideEffects(_dialect, CallGraphGenerator::callGraph(_ast))
|
||||
};
|
||||
cse(_ast);
|
||||
}
|
||||
|
||||
CommonSubexpressionEliminator::CommonSubexpressionEliminator(
|
||||
Dialect const& _dialect,
|
||||
map<YulString, SideEffects> _functionSideEffects
|
||||
):
|
||||
DataFlowAnalyzer(_dialect, std::move(_functionSideEffects))
|
||||
{
|
||||
}
|
||||
|
||||
void CommonSubexpressionEliminator::visit(Expression& _e)
|
||||
{
|
||||
bool descend = true;
|
||||
|
@ -27,6 +27,7 @@ namespace yul
|
||||
{
|
||||
|
||||
struct Dialect;
|
||||
struct SideEffects;
|
||||
|
||||
/**
|
||||
* Optimisation stage that replaces expressions known to be the current value of a variable
|
||||
@ -37,7 +38,14 @@ struct Dialect;
|
||||
class CommonSubexpressionEliminator: public DataFlowAnalyzer
|
||||
{
|
||||
public:
|
||||
CommonSubexpressionEliminator(Dialect const& _dialect): DataFlowAnalyzer(_dialect) {}
|
||||
/// Runs the CSE pass. @a _ast needs to be the complete AST of the program!
|
||||
static void run(Dialect const& _dialect, Block& _ast);
|
||||
|
||||
private:
|
||||
CommonSubexpressionEliminator(
|
||||
Dialect const& _dialect,
|
||||
std::map<YulString, SideEffects> _functionSideEffects
|
||||
);
|
||||
|
||||
protected:
|
||||
using ASTModifier::visit;
|
||||
|
@ -211,7 +211,7 @@ void DataFlowAnalyzer::handleAssignment(set<YulString> const& _variables, Expres
|
||||
{
|
||||
clearValues(_variables);
|
||||
|
||||
MovableChecker movableChecker{m_dialect};
|
||||
MovableChecker movableChecker{m_dialect, &m_functionSideEffects};
|
||||
if (_value)
|
||||
movableChecker.visit(*_value);
|
||||
else
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <libyul/optimiser/KnowledgeBase.h>
|
||||
#include <libyul/YulString.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <libyul/SideEffects.h>
|
||||
|
||||
// TODO avoid
|
||||
#include <libevmasm/Instruction.h>
|
||||
@ -38,6 +39,7 @@
|
||||
namespace yul
|
||||
{
|
||||
struct Dialect;
|
||||
struct SideEffects;
|
||||
|
||||
/**
|
||||
* Base class to perform data flow analysis during AST walks.
|
||||
@ -67,8 +69,16 @@ struct Dialect;
|
||||
class DataFlowAnalyzer: public ASTModifier
|
||||
{
|
||||
public:
|
||||
explicit DataFlowAnalyzer(Dialect const& _dialect):
|
||||
/// @param _functionSideEffects
|
||||
/// Side-effects of user-defined functions. Worst-case side-effects are assumed
|
||||
/// if this is not provided or the function is not found.
|
||||
/// The parameter is mostly used to determine movability of expressions.
|
||||
explicit DataFlowAnalyzer(
|
||||
Dialect const& _dialect,
|
||||
std::map<YulString, SideEffects> _functionSideEffects = {}
|
||||
):
|
||||
m_dialect(_dialect),
|
||||
m_functionSideEffects(std::move(_functionSideEffects)),
|
||||
m_knowledgeBase(_dialect, m_value)
|
||||
{}
|
||||
|
||||
@ -124,6 +134,9 @@ protected:
|
||||
) const;
|
||||
|
||||
Dialect const& m_dialect;
|
||||
/// Side-effects of user-defined functions. Worst-case side-effects are assumed
|
||||
/// if this is not provided or the function is not found.
|
||||
std::map<YulString, SideEffects> m_functionSideEffects;
|
||||
|
||||
/// Current values of variables, always movable.
|
||||
std::map<YulString, Expression const*> m_value;
|
||||
|
@ -35,8 +35,12 @@ using namespace dev;
|
||||
using namespace yul;
|
||||
|
||||
|
||||
SideEffectsCollector::SideEffectsCollector(Dialect const& _dialect, Expression const& _expression):
|
||||
SideEffectsCollector(_dialect)
|
||||
SideEffectsCollector::SideEffectsCollector(
|
||||
Dialect const& _dialect,
|
||||
Expression const& _expression,
|
||||
map<YulString, SideEffects> const* _functionSideEffects
|
||||
):
|
||||
SideEffectsCollector(_dialect, _functionSideEffects)
|
||||
{
|
||||
visit(_expression);
|
||||
}
|
||||
@ -64,8 +68,11 @@ void SideEffectsCollector::operator()(FunctionCall const& _functionCall)
|
||||
{
|
||||
ASTWalker::operator()(_functionCall);
|
||||
|
||||
if (BuiltinFunction const* f = m_dialect.builtin(_functionCall.functionName.name))
|
||||
YulString functionName = _functionCall.functionName.name;
|
||||
if (BuiltinFunction const* f = m_dialect.builtin(functionName))
|
||||
m_sideEffects += f->sideEffects;
|
||||
else if (m_functionSideEffects && m_functionSideEffects->count(functionName))
|
||||
m_sideEffects += m_functionSideEffects->at(functionName);
|
||||
else
|
||||
m_sideEffects += SideEffects::worst();
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include <libyul/optimiser/ASTWalker.h>
|
||||
#include <libyul/SideEffects.h>
|
||||
#include <libyul/AsmData.h>
|
||||
|
||||
#include <set>
|
||||
|
||||
@ -36,8 +37,15 @@ struct Dialect;
|
||||
class SideEffectsCollector: public ASTWalker
|
||||
{
|
||||
public:
|
||||
explicit SideEffectsCollector(Dialect const& _dialect): m_dialect(_dialect) {}
|
||||
SideEffectsCollector(Dialect const& _dialect, Expression const& _expression);
|
||||
explicit SideEffectsCollector(
|
||||
Dialect const& _dialect,
|
||||
std::map<YulString, SideEffects> const* _functionSideEffects = nullptr
|
||||
): m_dialect(_dialect), m_functionSideEffects(_functionSideEffects) {}
|
||||
SideEffectsCollector(
|
||||
Dialect const& _dialect,
|
||||
Expression const& _expression,
|
||||
std::map<YulString, SideEffects> const* _functionSideEffects = nullptr
|
||||
);
|
||||
SideEffectsCollector(Dialect const& _dialect, Statement const& _statement);
|
||||
SideEffectsCollector(Dialect const& _dialect, Block const& _ast);
|
||||
|
||||
@ -59,6 +67,7 @@ public:
|
||||
|
||||
private:
|
||||
Dialect const& m_dialect;
|
||||
std::map<YulString, SideEffects> const* m_functionSideEffects = nullptr;
|
||||
SideEffects m_sideEffects;
|
||||
};
|
||||
|
||||
@ -108,7 +117,10 @@ private:
|
||||
class MovableChecker: public SideEffectsCollector
|
||||
{
|
||||
public:
|
||||
explicit MovableChecker(Dialect const& _dialect): SideEffectsCollector(_dialect) {}
|
||||
explicit MovableChecker(
|
||||
Dialect const& _dialect,
|
||||
std::map<YulString, SideEffects> const* _functionSideEffects = nullptr
|
||||
): SideEffectsCollector(_dialect, _functionSideEffects) {}
|
||||
MovableChecker(Dialect const& _dialect, Expression const& _expression);
|
||||
|
||||
void operator()(Identifier const& _identifier) override;
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <libyul/optimiser/Disambiguator.h>
|
||||
#include <libyul/optimiser/VarDeclInitializer.h>
|
||||
#include <libyul/optimiser/BlockFlattener.h>
|
||||
#include <libyul/optimiser/CallGraphGenerator.h>
|
||||
#include <libyul/optimiser/ControlFlowSimplifier.h>
|
||||
#include <libyul/optimiser/DeadCodeEliminator.h>
|
||||
#include <libyul/optimiser/FunctionGrouper.h>
|
||||
@ -37,6 +38,7 @@
|
||||
#include <libyul/optimiser/UnusedPruner.h>
|
||||
#include <libyul/optimiser/ExpressionSimplifier.h>
|
||||
#include <libyul/optimiser/CommonSubexpressionEliminator.h>
|
||||
#include <libyul/optimiser/Semantics.h>
|
||||
#include <libyul/optimiser/SSAReverser.h>
|
||||
#include <libyul/optimiser/SSATransform.h>
|
||||
#include <libyul/optimiser/StackCompressor.h>
|
||||
@ -85,7 +87,7 @@ void OptimiserSuite::run(
|
||||
DeadCodeEliminator{_dialect}(ast);
|
||||
FunctionGrouper{}(ast);
|
||||
EquivalentFunctionCombiner::run(ast);
|
||||
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers);
|
||||
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
|
||||
BlockFlattener{}(ast);
|
||||
ControlFlowSimplifier{_dialect}(ast);
|
||||
StructuralSimplifier{_dialect}(ast);
|
||||
@ -114,7 +116,8 @@ void OptimiserSuite::run(
|
||||
RedundantAssignEliminator::run(_dialect, ast);
|
||||
|
||||
ExpressionSimplifier::run(_dialect, ast);
|
||||
CommonSubexpressionEliminator{_dialect}(ast);
|
||||
|
||||
CommonSubexpressionEliminator::run(_dialect, ast);
|
||||
}
|
||||
|
||||
{
|
||||
@ -124,19 +127,20 @@ void OptimiserSuite::run(
|
||||
ControlFlowSimplifier{_dialect}(ast);
|
||||
BlockFlattener{}(ast);
|
||||
DeadCodeEliminator{_dialect}(ast);
|
||||
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers);
|
||||
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
|
||||
}
|
||||
|
||||
{
|
||||
// simplify again
|
||||
CommonSubexpressionEliminator{_dialect}(ast);
|
||||
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers);
|
||||
CommonSubexpressionEliminator::run(_dialect, ast);
|
||||
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
|
||||
}
|
||||
|
||||
{
|
||||
// reverse SSA
|
||||
SSAReverser::run(ast);
|
||||
CommonSubexpressionEliminator{_dialect}(ast);
|
||||
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers);
|
||||
CommonSubexpressionEliminator::run(_dialect, ast);
|
||||
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
|
||||
|
||||
ExpressionJoiner::run(ast);
|
||||
ExpressionJoiner::run(ast);
|
||||
@ -147,7 +151,7 @@ void OptimiserSuite::run(
|
||||
{
|
||||
// run functional expression inliner
|
||||
ExpressionInliner(_dialect, ast).run();
|
||||
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers);
|
||||
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
|
||||
}
|
||||
|
||||
{
|
||||
@ -156,7 +160,7 @@ void OptimiserSuite::run(
|
||||
SSATransform::run(ast, dispenser);
|
||||
RedundantAssignEliminator::run(_dialect, ast);
|
||||
RedundantAssignEliminator::run(_dialect, ast);
|
||||
CommonSubexpressionEliminator{_dialect}(ast);
|
||||
CommonSubexpressionEliminator::run(_dialect, ast);
|
||||
}
|
||||
|
||||
{
|
||||
@ -177,12 +181,12 @@ void OptimiserSuite::run(
|
||||
BlockFlattener{}(ast);
|
||||
DeadCodeEliminator{_dialect}(ast);
|
||||
ControlFlowSimplifier{_dialect}(ast);
|
||||
CommonSubexpressionEliminator{_dialect}(ast);
|
||||
CommonSubexpressionEliminator::run(_dialect, ast);
|
||||
SSATransform::run(ast, dispenser);
|
||||
RedundantAssignEliminator::run(_dialect, ast);
|
||||
RedundantAssignEliminator::run(_dialect, ast);
|
||||
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers);
|
||||
CommonSubexpressionEliminator{_dialect}(ast);
|
||||
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
|
||||
CommonSubexpressionEliminator::run(_dialect, ast);
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,19 +194,19 @@ void OptimiserSuite::run(
|
||||
|
||||
ExpressionJoiner::run(ast);
|
||||
Rematerialiser::run(_dialect, ast);
|
||||
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers);
|
||||
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
|
||||
ExpressionJoiner::run(ast);
|
||||
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers);
|
||||
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
|
||||
ExpressionJoiner::run(ast);
|
||||
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers);
|
||||
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
|
||||
|
||||
SSAReverser::run(ast);
|
||||
CommonSubexpressionEliminator{_dialect}(ast);
|
||||
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers);
|
||||
CommonSubexpressionEliminator::run(_dialect, ast);
|
||||
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
|
||||
|
||||
ExpressionJoiner::run(ast);
|
||||
Rematerialiser::run(_dialect, ast);
|
||||
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers);
|
||||
UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers);
|
||||
|
||||
// This is a tuning parameter, but actually just prevents infinite loops.
|
||||
size_t stackCompressorMaxIterations = 16;
|
||||
|
@ -20,12 +20,14 @@
|
||||
|
||||
#include <libyul/optimiser/UnusedPruner.h>
|
||||
|
||||
#include <libyul/optimiser/CallGraphGenerator.h>
|
||||
#include <libyul/optimiser/NameCollector.h>
|
||||
#include <libyul/optimiser/Semantics.h>
|
||||
#include <libyul/optimiser/OptimizerUtilities.h>
|
||||
#include <libyul/Exceptions.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <libyul/Dialect.h>
|
||||
#include <libyul/SideEffects.h>
|
||||
|
||||
#include <boost/algorithm/cxx11/none_of.hpp>
|
||||
|
||||
@ -37,10 +39,12 @@ UnusedPruner::UnusedPruner(
|
||||
Dialect const& _dialect,
|
||||
Block& _ast,
|
||||
bool _allowMSizeOptimization,
|
||||
map<YulString, SideEffects> const* _functionSideEffects,
|
||||
set<YulString> const& _externallyUsedFunctions
|
||||
):
|
||||
m_dialect(_dialect),
|
||||
m_allowMSizeOptimization(_allowMSizeOptimization)
|
||||
m_allowMSizeOptimization(_allowMSizeOptimization),
|
||||
m_functionSideEffects(_functionSideEffects)
|
||||
{
|
||||
m_references = ReferencesCounter::countReferences(_ast);
|
||||
for (auto const& f: _externallyUsedFunctions)
|
||||
@ -88,7 +92,10 @@ void UnusedPruner::operator()(Block& _block)
|
||||
{
|
||||
if (!varDecl.value)
|
||||
statement = Block{std::move(varDecl.location), {}};
|
||||
else if (SideEffectsCollector(m_dialect, *varDecl.value).sideEffectFree(m_allowMSizeOptimization))
|
||||
else if (
|
||||
SideEffectsCollector(m_dialect, *varDecl.value, m_functionSideEffects).
|
||||
sideEffectFree(m_allowMSizeOptimization)
|
||||
)
|
||||
{
|
||||
subtractReferences(ReferencesCounter::countReferences(*varDecl.value));
|
||||
statement = Block{std::move(varDecl.location), {}};
|
||||
@ -104,7 +111,10 @@ void UnusedPruner::operator()(Block& _block)
|
||||
else if (statement.type() == typeid(ExpressionStatement))
|
||||
{
|
||||
ExpressionStatement& exprStmt = boost::get<ExpressionStatement>(statement);
|
||||
if (SideEffectsCollector(m_dialect, exprStmt.expression).sideEffectFree(m_allowMSizeOptimization))
|
||||
if (
|
||||
SideEffectsCollector(m_dialect, exprStmt.expression, m_functionSideEffects).
|
||||
sideEffectFree(m_allowMSizeOptimization)
|
||||
)
|
||||
{
|
||||
subtractReferences(ReferencesCounter::countReferences(exprStmt.expression));
|
||||
statement = Block{std::move(exprStmt.location), {}};
|
||||
@ -120,26 +130,31 @@ void UnusedPruner::runUntilStabilised(
|
||||
Dialect const& _dialect,
|
||||
Block& _ast,
|
||||
bool _allowMSizeOptimization,
|
||||
map<YulString, SideEffects> const* _functionSideEffects,
|
||||
set<YulString> const& _externallyUsedFunctions
|
||||
)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
UnusedPruner pruner(_dialect, _ast, _allowMSizeOptimization, _externallyUsedFunctions);
|
||||
UnusedPruner pruner(
|
||||
_dialect, _ast, _allowMSizeOptimization, _functionSideEffects,
|
||||
_externallyUsedFunctions);
|
||||
pruner(_ast);
|
||||
if (!pruner.shouldRunAgain())
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void UnusedPruner::runUntilStabilised(
|
||||
void UnusedPruner::runUntilStabilisedOnFullAST(
|
||||
Dialect const& _dialect,
|
||||
Block& _ast,
|
||||
set<YulString> const& _externallyUsedFunctions
|
||||
)
|
||||
{
|
||||
map<YulString, SideEffects> functionSideEffects =
|
||||
SideEffectsPropagator::sideEffects(_dialect, CallGraphGenerator::callGraph(_ast));
|
||||
bool allowMSizeOptimization = !MSizeFinder::containsMSize(_dialect, _ast);
|
||||
runUntilStabilised(_dialect, _ast, allowMSizeOptimization, _externallyUsedFunctions);
|
||||
runUntilStabilised(_dialect, _ast, allowMSizeOptimization, &functionSideEffects, _externallyUsedFunctions);
|
||||
}
|
||||
|
||||
void UnusedPruner::runUntilStabilised(
|
||||
|
@ -29,6 +29,7 @@
|
||||
namespace yul
|
||||
{
|
||||
struct Dialect;
|
||||
struct SideEffects;
|
||||
|
||||
/**
|
||||
* Optimisation stage that removes unused variables and functions and also
|
||||
@ -50,6 +51,7 @@ public:
|
||||
Dialect const& _dialect,
|
||||
Block& _ast,
|
||||
bool _allowMSizeOptimization,
|
||||
std::map<YulString, SideEffects> const* _functionSideEffects = nullptr,
|
||||
std::set<YulString> const& _externallyUsedFunctions = {}
|
||||
);
|
||||
UnusedPruner(
|
||||
@ -70,10 +72,15 @@ public:
|
||||
Dialect const& _dialect,
|
||||
Block& _ast,
|
||||
bool _allowMSizeOptimization,
|
||||
std::map<YulString, SideEffects> const* _functionSideEffects = nullptr,
|
||||
std::set<YulString> const& _externallyUsedFunctions = {}
|
||||
);
|
||||
|
||||
static void runUntilStabilised(
|
||||
/// Run the pruner until the code does not change anymore.
|
||||
/// The provided block has to be a full AST.
|
||||
/// The pruner itself determines if msize is used and which user-defined functions
|
||||
/// are side-effect free.
|
||||
static void runUntilStabilisedOnFullAST(
|
||||
Dialect const& _dialect,
|
||||
Block& _ast,
|
||||
std::set<YulString> const& _externallyUsedFunctions = {}
|
||||
@ -97,6 +104,7 @@ private:
|
||||
|
||||
Dialect const& m_dialect;
|
||||
bool m_allowMSizeOptimization = false;
|
||||
std::map<YulString, SideEffects> const* m_functionSideEffects = nullptr;
|
||||
bool m_shouldRunAgain = false;
|
||||
std::map<YulString, size_t> m_references;
|
||||
};
|
||||
|
@ -39,5 +39,5 @@ set REPORT=%DIRECTORY%/windows.txt
|
||||
cp ../report.txt %REPORT%
|
||||
git add %REPORT%
|
||||
git commit -a -m "Added report."
|
||||
git pull --rebase
|
||||
git pull --rebase 2>&1
|
||||
git push origin 2>&1
|
||||
|
@ -641,7 +641,7 @@ Allowed options)",
|
||||
(
|
||||
g_strEVMVersion.c_str(),
|
||||
po::value<string>()->value_name("version"),
|
||||
"Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, byzantium, constantinople or petersburg (default)."
|
||||
"Select desired EVM version. Either homestead, tangerineWhistle, spuriousDragon, byzantium, constantinople, petersburg (default), istanbul or berlin."
|
||||
)
|
||||
(g_argOptimize.c_str(), "Enable bytecode optimizer.")
|
||||
(
|
||||
|
@ -75,6 +75,10 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::vm* _vm):
|
||||
m_evmVersion = EVMC_BYZANTIUM;
|
||||
else if (_evmVersion == langutil::EVMVersion::constantinople())
|
||||
m_evmVersion = EVMC_CONSTANTINOPLE;
|
||||
else if (_evmVersion == langutil::EVMVersion::istanbul())
|
||||
assertThrow(false, Exception, "Istanbul is not supported yet.");
|
||||
else if (_evmVersion == langutil::EVMVersion::berlin())
|
||||
assertThrow(false, Exception, "Berlin is not supported yet.");
|
||||
else //if (_evmVersion == langutil::EVMVersion::petersburg())
|
||||
m_evmVersion = EVMC_PETERSBURG;
|
||||
}
|
||||
|
@ -7,24 +7,16 @@
|
||||
(local $_1 i64)
|
||||
(local $pos i64)
|
||||
(local $_2 i64)
|
||||
(local $_3 i64)
|
||||
(local $hi i64)
|
||||
(local $_4 i64)
|
||||
(local $y i64)
|
||||
(local $hi_1 i64)
|
||||
(local $hi_2 i64)
|
||||
(local $hi_3 i64)
|
||||
(local $hi_4 i64)
|
||||
(set_local $_1 (i64.const 0))
|
||||
(set_local $pos (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (i64.const 64)))
|
||||
(set_local $_2 (i64.const 32))
|
||||
(set_local $_3 (i64.shr_u (get_local $_1) (i64.const 16)))
|
||||
(set_local $hi (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (get_local $_1) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (get_local $_1) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (get_local $_3))) (get_local $_2)))
|
||||
(set_local $_4 (i64.shr_u (get_local $_1) (get_local $_2)))
|
||||
(i64.store (get_local $pos) (i64.or (get_local $hi) (call $endian_swap_32 (get_local $_4)))) (set_local $hi_1 (i64.shl (call $endian_swap_16 (get_local $_1)) (i64.const 16)))
|
||||
(set_local $hi_2 (i64.shl (i64.or (get_local $hi_1) (call $endian_swap_16 (get_local $_3))) (get_local $_2)))
|
||||
(i64.store (i64.add (get_local $pos) (i64.const 8)) (i64.or (get_local $hi_2) (call $endian_swap_32 (get_local $_4)))) (set_local $hi_3 (i64.shl (call $endian_swap_32 (get_local $_1)) (get_local $_2)))
|
||||
(i64.store (i64.add (get_local $pos) (i64.const 16)) (i64.or (get_local $hi_3) (call $endian_swap_32 (get_local $_4)))) (set_local $hi_4 (i64.shl (call $endian_swap_32 (i64.const 64)) (get_local $_2)))
|
||||
(i64.store (i64.add (get_local $pos) (i64.const 24)) (i64.or (get_local $hi_4) (call $endian_swap_32 (i64.shr_u (i64.const 64) (get_local $_2))))) (call $eth.revert (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1)) (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1)))
|
||||
(set_local $_2 (i64.const 65280))
|
||||
(set_local $hi (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (get_local $_1) (i64.const 8)) (get_local $_2)) (i64.and (i64.shr_u (get_local $_1) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (i64.shr_u (get_local $_1) (i64.const 16)))) (i64.const 32)))
|
||||
(set_local $y (i64.or (get_local $hi) (call $endian_swap_32 (i64.shr_u (get_local $_1) (i64.const 32)))))
|
||||
(i64.store (get_local $pos) (get_local $y)) (i64.store (i64.add (get_local $pos) (i64.const 8)) (get_local $y)) (i64.store (i64.add (get_local $pos) (i64.const 16)) (get_local $y)) (set_local $hi_1 (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (i64.const 64) (i64.const 8)) (get_local $_2)) (i64.and (i64.shr_u (i64.const 64) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (i64.shr_u (i64.const 64) (i64.const 16)))) (i64.const 32)))
|
||||
(i64.store (i64.add (get_local $pos) (i64.const 24)) (i64.or (get_local $hi_1) (call $endian_swap_32 (i64.shr_u (i64.const 64) (i64.const 32))))) (call $eth.revert (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1)) (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1)))
|
||||
)
|
||||
|
||||
(func $u256_to_i32
|
||||
@ -74,44 +66,38 @@
|
||||
(local $_1 i64)
|
||||
(local $pos i64)
|
||||
(local $hi i64)
|
||||
(local $_2 i64)
|
||||
(local $y i64)
|
||||
(local $hi_1 i64)
|
||||
(local $_3 i64)
|
||||
(local $hi_2 i64)
|
||||
(local $hi_3 i64)
|
||||
(local $hi_4 i64)
|
||||
(local $_2 i64)
|
||||
(local $_3 i64)
|
||||
(local $_4 i64)
|
||||
(local $_5 i64)
|
||||
(local $_6 i64)
|
||||
(local $_7 i64)
|
||||
(local $_8 i64)
|
||||
(local $_9 i64)
|
||||
(local $_10 i64)
|
||||
(local $_11 i64)
|
||||
(set_local $_1 (i64.const 0))
|
||||
(set_local $pos (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (i64.const 64)))
|
||||
(set_local $hi (i64.shl (call $endian_swap_16 (get_local $_1)) (i64.const 16)))
|
||||
(set_local $_2 (i64.shr_u (get_local $_1) (i64.const 16)))
|
||||
(set_local $hi_1 (i64.shl (i64.or (get_local $hi) (call $endian_swap_16 (get_local $_2))) (i64.const 32)))
|
||||
(set_local $_3 (i64.shr_u (get_local $_1) (i64.const 32)))
|
||||
(i64.store (get_local $pos) (i64.or (get_local $hi_1) (call $endian_swap_32 (get_local $_3)))) (set_local $hi_2 (i64.shl (call $endian_swap_16 (get_local $_1)) (i64.const 16)))
|
||||
(set_local $hi_3 (i64.shl (i64.or (get_local $hi_2) (call $endian_swap_16 (get_local $_2))) (i64.const 32)))
|
||||
(i64.store (i64.add (get_local $pos) (i64.const 8)) (i64.or (get_local $hi_3) (call $endian_swap_32 (get_local $_3)))) (set_local $hi_4 (i64.shl (call $endian_swap_32 (get_local $_1)) (i64.const 32)))
|
||||
(i64.store (i64.add (get_local $pos) (i64.const 16)) (i64.or (get_local $hi_4) (call $endian_swap_32 (get_local $_3)))) (i64.store (i64.add (get_local $pos) (i64.const 24)) (call $endian_swap (i64.const 64))) (block
|
||||
(set_local $_4 (datasize \"C_2_deployed\"))
|
||||
(set_local $_5 (get_global $global_))
|
||||
(set_local $_6 (get_global $global__1))
|
||||
(set_local $_7 (get_global $global__2))
|
||||
(set_local $hi (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (get_local $_1) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (get_local $_1) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (i64.shr_u (get_local $_1) (i64.const 16)))) (i64.const 32)))
|
||||
(set_local $y (i64.or (get_local $hi) (call $endian_swap_32 (i64.shr_u (get_local $_1) (i64.const 32)))))
|
||||
(i64.store (get_local $pos) (get_local $y)) (i64.store (i64.add (get_local $pos) (i64.const 8)) (get_local $y)) (i64.store (i64.add (get_local $pos) (i64.const 16)) (get_local $y)) (set_local $hi_1 (i64.shl (call $endian_swap_16 (i64.const 64)) (i64.const 16)))
|
||||
(set_local $hi_2 (i64.shl (i64.or (get_local $hi_1) (call $endian_swap_16 (i64.shr_u (i64.const 64) (i64.const 16)))) (i64.const 32)))
|
||||
(i64.store (i64.add (get_local $pos) (i64.const 24)) (i64.or (get_local $hi_2) (call $endian_swap_32 (i64.shr_u (i64.const 64) (i64.const 32))))) (block
|
||||
(set_local $_2 (datasize \"C_2_deployed\"))
|
||||
(set_local $_3 (get_global $global_))
|
||||
(set_local $_4 (get_global $global__1))
|
||||
(set_local $_5 (get_global $global__2))
|
||||
|
||||
)
|
||||
(block
|
||||
(set_local $_8 (dataoffset \"C_2_deployed\"))
|
||||
(set_local $_9 (get_global $global_))
|
||||
(set_local $_10 (get_global $global__1))
|
||||
(set_local $_11 (get_global $global__2))
|
||||
(set_local $_6 (dataoffset \"C_2_deployed\"))
|
||||
(set_local $_7 (get_global $global_))
|
||||
(set_local $_8 (get_global $global__1))
|
||||
(set_local $_9 (get_global $global__2))
|
||||
|
||||
)
|
||||
(call $eth.codeCopy (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1)) (call $u256_to_i32 (get_local $_8) (get_local $_9) (get_local $_10) (get_local $_11)) (call $u256_to_i32 (get_local $_4) (get_local $_5) (get_local $_6) (get_local $_7))) (call $eth.finish (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1)) (call $u256_to_i32 (get_local $_4) (get_local $_5) (get_local $_6) (get_local $_7)))
|
||||
(call $eth.codeCopy (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1)) (call $u256_to_i32 (get_local $_6) (get_local $_7) (get_local $_8) (get_local $_9)) (call $u256_to_i32 (get_local $_2) (get_local $_3) (get_local $_4) (get_local $_5))) (call $eth.finish (call $u256_to_i32 (get_local $_1) (get_local $_1) (get_local $_1) (get_local $_1)) (call $u256_to_i32 (get_local $_2) (get_local $_3) (get_local $_4) (get_local $_5)))
|
||||
)
|
||||
|
||||
(func $u256_to_i32
|
||||
@ -147,16 +133,6 @@
|
||||
(get_local $y)
|
||||
)
|
||||
|
||||
(func $endian_swap
|
||||
(param $x i64)
|
||||
(result i64)
|
||||
(local $y i64)
|
||||
(local $hi i64)
|
||||
(set_local $hi (i64.shl (call $endian_swap_32 (get_local $x)) (i64.const 32)))
|
||||
(set_local $y (i64.or (get_local $hi) (call $endian_swap_32 (i64.shr_u (get_local $x) (i64.const 32)))))
|
||||
(get_local $y)
|
||||
)
|
||||
|
||||
)
|
||||
"}}}},"errors":[{"component":"general","formattedMessage":"Warning: The Yul optimiser is still experimental. Do not use it in production unless correctness of generated code is verified with extensive tests.
|
||||
","message":"The Yul optimiser is still experimental. Do not use it in production unless correctness of generated code is verified with extensive tests.","severity":"warning","type":"Warning"}],"sources":{"A":{"id":0}}}
|
||||
|
@ -9,29 +9,6 @@ contract test {
|
||||
// :test
|
||||
// [
|
||||
// {
|
||||
// "constant": false,
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "uint256",
|
||||
// "name": "a",
|
||||
// "type": "uint256"
|
||||
// }
|
||||
// ],
|
||||
// "name": "f",
|
||||
// "outputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "uint256",
|
||||
// "name": "d",
|
||||
// "type": "uint256"
|
||||
// }
|
||||
// ],
|
||||
// "payable": false,
|
||||
// "stateMutability": "nonpayable",
|
||||
// "type": "function"
|
||||
// },
|
||||
// {
|
||||
// "anonymous": false,
|
||||
// "inputs":
|
||||
// [
|
||||
@ -76,5 +53,28 @@ contract test {
|
||||
// "inputs": [],
|
||||
// "name": "e3",
|
||||
// "type": "event"
|
||||
// },
|
||||
// {
|
||||
// "constant": false,
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "uint256",
|
||||
// "name": "a",
|
||||
// "type": "uint256"
|
||||
// }
|
||||
// ],
|
||||
// "name": "f",
|
||||
// "outputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "uint256",
|
||||
// "name": "d",
|
||||
// "type": "uint256"
|
||||
// }
|
||||
// ],
|
||||
// "payable": false,
|
||||
// "stateMutability": "nonpayable",
|
||||
// "type": "function"
|
||||
// }
|
||||
// ]
|
||||
|
@ -10,6 +10,20 @@ contract Derived is Base {
|
||||
// :Base
|
||||
// [
|
||||
// {
|
||||
// "anonymous": false,
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "indexed": true,
|
||||
// "internalType": "bytes32",
|
||||
// "name": "evtArgBase",
|
||||
// "type": "bytes32"
|
||||
// }
|
||||
// ],
|
||||
// "name": "baseEvent",
|
||||
// "type": "event"
|
||||
// },
|
||||
// {
|
||||
// "constant": false,
|
||||
// "inputs":
|
||||
// [
|
||||
@ -31,7 +45,12 @@ contract Derived is Base {
|
||||
// "payable": false,
|
||||
// "stateMutability": "nonpayable",
|
||||
// "type": "function"
|
||||
// },
|
||||
// }
|
||||
// ]
|
||||
//
|
||||
//
|
||||
// :Derived
|
||||
// [
|
||||
// {
|
||||
// "anonymous": false,
|
||||
// "inputs":
|
||||
@ -45,13 +64,22 @@ contract Derived is Base {
|
||||
// ],
|
||||
// "name": "baseEvent",
|
||||
// "type": "event"
|
||||
// }
|
||||
// ]
|
||||
//
|
||||
//
|
||||
// :Derived
|
||||
// },
|
||||
// {
|
||||
// "anonymous": false,
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "indexed": true,
|
||||
// "internalType": "uint256",
|
||||
// "name": "evtArgDerived",
|
||||
// "type": "uint256"
|
||||
// }
|
||||
// ],
|
||||
// "name": "derivedEvent",
|
||||
// "type": "event"
|
||||
// },
|
||||
// {
|
||||
// "constant": false,
|
||||
// "inputs":
|
||||
// [
|
||||
@ -96,33 +124,5 @@ contract Derived is Base {
|
||||
// "payable": false,
|
||||
// "stateMutability": "nonpayable",
|
||||
// "type": "function"
|
||||
// },
|
||||
// {
|
||||
// "anonymous": false,
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "indexed": true,
|
||||
// "internalType": "uint256",
|
||||
// "name": "evtArgDerived",
|
||||
// "type": "uint256"
|
||||
// }
|
||||
// ],
|
||||
// "name": "derivedEvent",
|
||||
// "type": "event"
|
||||
// },
|
||||
// {
|
||||
// "anonymous": false,
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "indexed": true,
|
||||
// "internalType": "bytes32",
|
||||
// "name": "evtArgBase",
|
||||
// "type": "bytes32"
|
||||
// }
|
||||
// ],
|
||||
// "name": "baseEvent",
|
||||
// "type": "event"
|
||||
// }
|
||||
// ]
|
||||
|
@ -6,6 +6,29 @@ contract test {
|
||||
// :test
|
||||
// [
|
||||
// {
|
||||
// "constant": true,
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "uint32",
|
||||
// "name": "a",
|
||||
// "type": "uint32"
|
||||
// }
|
||||
// ],
|
||||
// "name": "boo",
|
||||
// "outputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "uint256",
|
||||
// "name": "b",
|
||||
// "type": "uint256"
|
||||
// }
|
||||
// ],
|
||||
// "payable": false,
|
||||
// "stateMutability": "pure",
|
||||
// "type": "function"
|
||||
// },
|
||||
// {
|
||||
// "constant": false,
|
||||
// "inputs":
|
||||
// [
|
||||
@ -32,28 +55,5 @@ contract test {
|
||||
// "payable": false,
|
||||
// "stateMutability": "nonpayable",
|
||||
// "type": "function"
|
||||
// },
|
||||
// {
|
||||
// "constant": true,
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "uint32",
|
||||
// "name": "a",
|
||||
// "type": "uint32"
|
||||
// }
|
||||
// ],
|
||||
// "name": "boo",
|
||||
// "outputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "uint256",
|
||||
// "name": "b",
|
||||
// "type": "uint256"
|
||||
// }
|
||||
// ],
|
||||
// "payable": false,
|
||||
// "stateMutability": "pure",
|
||||
// "type": "function"
|
||||
// }
|
||||
// ]
|
||||
|
@ -11,6 +11,19 @@ contract test {
|
||||
// :test
|
||||
// [
|
||||
// {
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "enum test.ActionChoices",
|
||||
// "name": "param",
|
||||
// "type": "uint8"
|
||||
// }
|
||||
// ],
|
||||
// "payable": false,
|
||||
// "stateMutability": "nonpayable",
|
||||
// "type": "constructor"
|
||||
// },
|
||||
// {
|
||||
// "constant": false,
|
||||
// "inputs": [],
|
||||
// "name": "ret",
|
||||
@ -25,18 +38,5 @@ contract test {
|
||||
// "payable": false,
|
||||
// "stateMutability": "nonpayable",
|
||||
// "type": "function"
|
||||
// },
|
||||
// {
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "enum test.ActionChoices",
|
||||
// "name": "param",
|
||||
// "type": "uint8"
|
||||
// }
|
||||
// ],
|
||||
// "payable": false,
|
||||
// "stateMutability": "nonpayable",
|
||||
// "type": "constructor"
|
||||
// }
|
||||
// ]
|
||||
|
@ -6,6 +6,29 @@ contract test {
|
||||
// :test
|
||||
// [
|
||||
// {
|
||||
// "constant": true,
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "uint32",
|
||||
// "name": "a",
|
||||
// "type": "uint32"
|
||||
// }
|
||||
// ],
|
||||
// "name": "boo",
|
||||
// "outputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "uint256",
|
||||
// "name": "b",
|
||||
// "type": "uint256"
|
||||
// }
|
||||
// ],
|
||||
// "payable": false,
|
||||
// "stateMutability": "view",
|
||||
// "type": "function"
|
||||
// },
|
||||
// {
|
||||
// "constant": false,
|
||||
// "inputs":
|
||||
// [
|
||||
@ -32,28 +55,5 @@ contract test {
|
||||
// "payable": false,
|
||||
// "stateMutability": "nonpayable",
|
||||
// "type": "function"
|
||||
// },
|
||||
// {
|
||||
// "constant": true,
|
||||
// "inputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "uint32",
|
||||
// "name": "a",
|
||||
// "type": "uint32"
|
||||
// }
|
||||
// ],
|
||||
// "name": "boo",
|
||||
// "outputs":
|
||||
// [
|
||||
// {
|
||||
// "internalType": "uint256",
|
||||
// "name": "b",
|
||||
// "type": "uint256"
|
||||
// }
|
||||
// ],
|
||||
// "payable": false,
|
||||
// "stateMutability": "view",
|
||||
// "type": "function"
|
||||
// }
|
||||
// ]
|
||||
|
@ -212,7 +212,7 @@ BOOST_AUTO_TEST_CASE(compound_assignment_division)
|
||||
uint[] array;
|
||||
function f(uint x, uint p) public {
|
||||
require(x == 2);
|
||||
require(array[p] == 10);
|
||||
array[p] = 10;
|
||||
array[p] /= array[p] / x;
|
||||
assert(array[p] == x);
|
||||
assert(array[p] == 0);
|
||||
@ -225,7 +225,7 @@ BOOST_AUTO_TEST_CASE(compound_assignment_division)
|
||||
mapping (uint => uint) map;
|
||||
function f(uint x, uint p) public {
|
||||
require(x == 2);
|
||||
require(map[p] == 10);
|
||||
map[p] = 10;
|
||||
map[p] /= map[p] / x;
|
||||
assert(map[p] == x);
|
||||
assert(map[p] == 0);
|
||||
|
@ -5,11 +5,11 @@ contract C
|
||||
uint[] array;
|
||||
function f(uint x, uint p) public {
|
||||
require(x < 100);
|
||||
require(array[p] == 100);
|
||||
array[p] = 100;
|
||||
array[p] += array[p] + x;
|
||||
assert(array[p] < 300);
|
||||
assert(array[p] < 110);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (202-224): Assertion violation happens here
|
||||
// Warning: (192-214): Assertion violation happens here
|
||||
|
@ -5,11 +5,11 @@ contract C
|
||||
mapping (uint => uint) map;
|
||||
function f(uint x, uint p) public {
|
||||
require(x < 100);
|
||||
require(map[p] == 100);
|
||||
map[p] = 100;
|
||||
map[p] += map[p] + x;
|
||||
assert(map[p] < 300);
|
||||
assert(map[p] < 110);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (208-228): Assertion violation happens here
|
||||
// Warning: (198-218): Assertion violation happens here
|
||||
|
@ -5,11 +5,11 @@ contract C
|
||||
uint[] array;
|
||||
function f(uint x, uint p) public {
|
||||
require(x < 10);
|
||||
require(array[p] == 10);
|
||||
array[p] = 10;
|
||||
array[p] *= array[p] + x;
|
||||
assert(array[p] <= 190);
|
||||
assert(array[p] < 50);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (201-222): Assertion violation happens here
|
||||
// Warning: (191-212): Assertion violation happens here
|
||||
|
@ -5,11 +5,11 @@ contract C
|
||||
mapping (uint => uint) map;
|
||||
function f(uint x, uint p) public {
|
||||
require(x < 10);
|
||||
require(map[p] == 10);
|
||||
map[p] = 10;
|
||||
map[p] *= map[p] + x;
|
||||
assert(map[p] <= 190);
|
||||
assert(map[p] < 50);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (207-226): Assertion violation happens here
|
||||
// Warning: (197-216): Assertion violation happens here
|
||||
|
@ -5,11 +5,11 @@ contract C
|
||||
uint[] array;
|
||||
function f(uint x, uint p) public {
|
||||
require(x < 100);
|
||||
require(array[p] == 200);
|
||||
array[p] = 200;
|
||||
array[p] -= array[p] - x;
|
||||
assert(array[p] >= 0);
|
||||
assert(array[p] < 90);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (201-222): Assertion violation happens here
|
||||
// Warning: (191-212): Assertion violation happens here
|
||||
|
@ -5,11 +5,11 @@ contract C
|
||||
mapping (uint => uint) map;
|
||||
function f(uint x, uint p) public {
|
||||
require(x < 100);
|
||||
require(map[p] == 200);
|
||||
map[p] = 200;
|
||||
map[p] -= map[p] - x;
|
||||
assert(map[p] >= 0);
|
||||
assert(map[p] < 90);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (207-226): Assertion violation happens here
|
||||
// Warning: (197-216): Assertion violation happens here
|
||||
|
@ -10,12 +10,9 @@ contract C
|
||||
delete a;
|
||||
else
|
||||
delete a[2];
|
||||
// Assertion fails as false positive because
|
||||
// setZeroValue for arrays needs \forall i . a[i] = 0
|
||||
// which is still unimplemented.
|
||||
assert(a[2] == 0);
|
||||
assert(a[1] == 0);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (118-119): Condition is always true.
|
||||
// Warning: (297-314): Assertion violation happens here
|
||||
|
@ -6,10 +6,6 @@ contract C
|
||||
function f() public {
|
||||
require(a[2][3] == 4);
|
||||
delete a;
|
||||
// Fails as false positive.
|
||||
// setZeroValue needs forall for arrays.
|
||||
assert(a[2][3] == 0);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (194-214): Assertion violation happens here
|
||||
|
@ -9,11 +9,7 @@ contract C
|
||||
delete a;
|
||||
else
|
||||
delete a[2];
|
||||
// Fails as false positive since
|
||||
// setZeroValue for arrays needs forall
|
||||
// which is unimplemented.
|
||||
assert(a[2][3] == 0);
|
||||
assert(a[1][1] == 0);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (266-286): Assertion violation happens here
|
||||
|
@ -16,12 +16,9 @@ contract C
|
||||
g();
|
||||
else
|
||||
h();
|
||||
// Assertion fails as false positive because
|
||||
// setZeroValue for arrays needs \forall i . a[i] = 0
|
||||
// which is still unimplemented.
|
||||
assert(a[2] == 0);
|
||||
assert(a[1] == 0);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (201-202): Condition is always true.
|
||||
// Warning: (367-384): Assertion violation happens here
|
||||
|
@ -4,10 +4,11 @@ contract C
|
||||
{
|
||||
uint[][] array;
|
||||
function f(uint x, uint y, uint z, uint t) public view {
|
||||
require(array[x][y] == 200);
|
||||
// TODO change to = 200 when 2d assignments are supported.
|
||||
require(array[x][y] < 200);
|
||||
require(x == z && y == t);
|
||||
assert(array[z][t] > 300);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (183-208): Assertion violation happens here
|
||||
// Warning: (243-268): Assertion violation happens here
|
||||
|
@ -4,10 +4,11 @@ contract C
|
||||
{
|
||||
uint[][][] array;
|
||||
function f(uint x, uint y, uint z, uint t, uint w, uint v) public view {
|
||||
require(array[x][y][z] == 200);
|
||||
// TODO change to = 200 when 3d assignments are supported.
|
||||
require(array[x][y][z] < 200);
|
||||
require(x == t && y == w && z == v);
|
||||
assert(array[t][w][v] > 300);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (214-242): Assertion violation happens here
|
||||
// Warning: (274-302): Assertion violation happens here
|
||||
|
@ -4,10 +4,10 @@ contract C
|
||||
{
|
||||
uint[10][20] array;
|
||||
function f(uint x, uint y, uint z, uint t) public view {
|
||||
require(array[x][y] == 200);
|
||||
require(array[x][y] < 200);
|
||||
require(x == z && y == t);
|
||||
assert(array[z][t] > 300);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (187-212): Assertion violation happens here
|
||||
// Warning: (186-211): Assertion violation happens here
|
||||
|
@ -4,10 +4,11 @@ contract C
|
||||
{
|
||||
uint[10][20][30] array;
|
||||
function f(uint x, uint y, uint z, uint t, uint w, uint v) public view {
|
||||
require(array[x][y][z] == 200);
|
||||
// TODO change to = 200 when 3d assignments are supported.
|
||||
require(array[x][y][z] < 200);
|
||||
require(x == t && y == w && z == v);
|
||||
assert(array[t][w][v] > 300);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (220-248): Assertion violation happens here
|
||||
// Warning: (280-308): Assertion violation happens here
|
||||
|
@ -9,4 +9,3 @@ contract C
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning: (125-144): Assertion violation happens here
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <libyul/optimiser/ControlFlowSimplifier.h>
|
||||
#include <libyul/optimiser/DeadCodeEliminator.h>
|
||||
#include <libyul/optimiser/Disambiguator.h>
|
||||
#include <libyul/optimiser/CallGraphGenerator.h>
|
||||
#include <libyul/optimiser/CommonSubexpressionEliminator.h>
|
||||
#include <libyul/optimiser/NameCollector.h>
|
||||
#include <libyul/optimiser/EquivalentFunctionCombiner.h>
|
||||
@ -45,6 +46,7 @@
|
||||
#include <libyul/optimiser/ExpressionJoiner.h>
|
||||
#include <libyul/optimiser/SSAReverser.h>
|
||||
#include <libyul/optimiser/SSATransform.h>
|
||||
#include <libyul/optimiser/Semantics.h>
|
||||
#include <libyul/optimiser/RedundantAssignEliminator.h>
|
||||
#include <libyul/optimiser/StructuralSimplifier.h>
|
||||
#include <libyul/optimiser/StackCompressor.h>
|
||||
@ -149,7 +151,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
|
||||
else if (m_optimizerStep == "commonSubexpressionEliminator")
|
||||
{
|
||||
disambiguate();
|
||||
(CommonSubexpressionEliminator{*m_dialect})(*m_ast);
|
||||
CommonSubexpressionEliminator::run(*m_dialect, *m_ast);
|
||||
}
|
||||
else if (m_optimizerStep == "expressionSplitter")
|
||||
{
|
||||
@ -218,9 +220,9 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
|
||||
NameDispenser nameDispenser{*m_dialect, *m_ast};
|
||||
ExpressionSplitter{*m_dialect, nameDispenser}(*m_ast);
|
||||
ForLoopInitRewriter{}(*m_ast);
|
||||
CommonSubexpressionEliminator{*m_dialect}(*m_ast);
|
||||
CommonSubexpressionEliminator::run(*m_dialect, *m_ast);
|
||||
ExpressionSimplifier::run(*m_dialect, *m_ast);
|
||||
UnusedPruner::runUntilStabilised(*m_dialect, *m_ast);
|
||||
UnusedPruner::runUntilStabilisedOnFullAST(*m_dialect, *m_ast);
|
||||
DeadCodeEliminator{*m_dialect}(*m_ast);
|
||||
ExpressionJoiner::run(*m_ast);
|
||||
ExpressionJoiner::run(*m_ast);
|
||||
@ -228,7 +230,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
|
||||
else if (m_optimizerStep == "unusedPruner")
|
||||
{
|
||||
disambiguate();
|
||||
UnusedPruner::runUntilStabilised(*m_dialect, *m_ast);
|
||||
UnusedPruner::runUntilStabilisedOnFullAST(*m_dialect, *m_ast);
|
||||
}
|
||||
else if (m_optimizerStep == "deadCodeEliminator")
|
||||
{
|
||||
@ -260,12 +262,12 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
|
||||
ForLoopInitRewriter{}(*m_ast);
|
||||
NameDispenser nameDispenser{*m_dialect, *m_ast};
|
||||
ExpressionSplitter{*m_dialect, nameDispenser}(*m_ast);
|
||||
CommonSubexpressionEliminator{*m_dialect}(*m_ast);
|
||||
CommonSubexpressionEliminator::run(*m_dialect, *m_ast);
|
||||
ExpressionSimplifier::run(*m_dialect, *m_ast);
|
||||
|
||||
LoadResolver::run(*m_dialect, *m_ast);
|
||||
|
||||
UnusedPruner::runUntilStabilised(*m_dialect, *m_ast);
|
||||
UnusedPruner::runUntilStabilisedOnFullAST(*m_dialect, *m_ast);
|
||||
ExpressionJoiner::run(*m_ast);
|
||||
ExpressionJoiner::run(*m_ast);
|
||||
}
|
||||
@ -298,8 +300,8 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
|
||||
RedundantAssignEliminator::run(*m_dialect, *m_ast);
|
||||
// reverse SSA
|
||||
SSAReverser::run(*m_ast);
|
||||
CommonSubexpressionEliminator{*m_dialect}(*m_ast);
|
||||
UnusedPruner::runUntilStabilised(*m_dialect, *m_ast);
|
||||
CommonSubexpressionEliminator::run(*m_dialect, *m_ast);
|
||||
UnusedPruner::runUntilStabilisedOnFullAST(*m_dialect, *m_ast);
|
||||
}
|
||||
else if (m_optimizerStep == "stackCompressor")
|
||||
{
|
||||
|
@ -0,0 +1,26 @@
|
||||
{
|
||||
function double(x) -> y { y := add(x, x) }
|
||||
function double_with_se(x) -> y { y := add(x, x) mstore(40, 4) }
|
||||
let i := mload(3)
|
||||
let a := double(i)
|
||||
let b := double(i)
|
||||
let c := double_with_se(i)
|
||||
let d := double_with_se(i)
|
||||
}
|
||||
// ====
|
||||
// step: commonSubexpressionEliminator
|
||||
// ----
|
||||
// {
|
||||
// function double(x) -> y
|
||||
// { y := add(x, x) }
|
||||
// function double_with_se(x_1) -> y_2
|
||||
// {
|
||||
// y_2 := add(x_1, x_1)
|
||||
// mstore(40, 4)
|
||||
// }
|
||||
// let i := mload(3)
|
||||
// let a := double(i)
|
||||
// let b := a
|
||||
// let c := double_with_se(i)
|
||||
// let d := double_with_se(i)
|
||||
// }
|
@ -1,6 +1,6 @@
|
||||
// Even if the functions pass the equality check, they are not movable.
|
||||
{
|
||||
function f() -> a { }
|
||||
function f() -> a { mstore(1, 2) }
|
||||
let b := sub(f(), f())
|
||||
mstore(0, b)
|
||||
}
|
||||
@ -9,6 +9,6 @@
|
||||
// ----
|
||||
// {
|
||||
// function f() -> a
|
||||
// { }
|
||||
// { mstore(1, 2) }
|
||||
// mstore(0, sub(f(), f()))
|
||||
// }
|
||||
|
@ -31,8 +31,5 @@
|
||||
// mstore8(calldataload(_5), 4)
|
||||
// sstore(_5, mload(_2))
|
||||
// mstore(_2, _17)
|
||||
// g()
|
||||
// sstore(_5, mload(_2))
|
||||
// function g()
|
||||
// { }
|
||||
// }
|
||||
|
@ -0,0 +1,13 @@
|
||||
{
|
||||
function f(x) -> t {
|
||||
let b := 7
|
||||
}
|
||||
function g(x) -> t {
|
||||
t := x
|
||||
}
|
||||
let x := f(g(2))
|
||||
}
|
||||
// ====
|
||||
// step: unusedPruner
|
||||
// ----
|
||||
// { }
|
@ -5,8 +5,4 @@
|
||||
// ====
|
||||
// step: unusedPruner
|
||||
// ----
|
||||
// {
|
||||
// function f() -> x, y
|
||||
// { }
|
||||
// let a, b := f()
|
||||
// }
|
||||
|
@ -22,8 +22,10 @@
|
||||
|
||||
#include <libdevcore/StringUtils.h>
|
||||
|
||||
#include <boost/range/algorithm_ext/erase.hpp>
|
||||
#include <boost/algorithm/cxx11/all_of.hpp>
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/range/algorithm_ext/erase.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace yul::test::yul_fuzzer;
|
||||
@ -84,11 +86,34 @@ string ProtoConverter::visit(Literal const& _x)
|
||||
}
|
||||
}
|
||||
|
||||
// Reference any index in [0, m_numLiveVars-1]
|
||||
bool ProtoConverter::varDeclAvailable()
|
||||
{
|
||||
if (m_inFunctionDef)
|
||||
return m_scopeVars.top().size() > 0;
|
||||
else
|
||||
return m_variables.size() > 0;
|
||||
}
|
||||
|
||||
bool ProtoConverter::functionCallNotPossible(FunctionCall_Returns _type)
|
||||
{
|
||||
return _type == FunctionCall::SINGLE ||
|
||||
(_type == FunctionCall::MULTIASSIGN && !varDeclAvailable());
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(VarRef const& _x)
|
||||
{
|
||||
yulAssert(m_numLiveVars > 0, "Proto fuzzer: No variables to reference.");
|
||||
m_output << "x_" << (static_cast<uint32_t>(_x.varnum()) % m_numLiveVars);
|
||||
if (m_inFunctionDef)
|
||||
{
|
||||
// Ensure that there is at least one variable declaration to reference in function scope.
|
||||
yulAssert(m_scopeVars.top().size() > 0, "Proto fuzzer: No variables to reference.");
|
||||
m_output << m_scopeVars.top()[_x.varnum() % m_scopeVars.top().size()];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ensure that there is at least one variable declaration to reference in nested scopes.
|
||||
yulAssert(m_variables.size() > 0, "Proto fuzzer: No variables to reference.");
|
||||
m_output << m_variables[_x.varnum() % m_variables.size()];
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(Expression const& _x)
|
||||
@ -96,6 +121,12 @@ void ProtoConverter::visit(Expression const& _x)
|
||||
switch (_x.expr_oneof_case())
|
||||
{
|
||||
case Expression::kVarref:
|
||||
// If the expression requires a variable reference that we cannot provide
|
||||
// (because there are no variables in scope), we silently output a literal
|
||||
// expression from the optimizer dictionary.
|
||||
if (!varDeclAvailable())
|
||||
m_output << dictionaryToken();
|
||||
else
|
||||
visit(_x.varref());
|
||||
break;
|
||||
case Expression::kCons:
|
||||
@ -114,7 +145,18 @@ void ProtoConverter::visit(Expression const& _x)
|
||||
visit(_x.nop());
|
||||
break;
|
||||
case Expression::kFuncExpr:
|
||||
// FunctionCall must return a single value, otherwise
|
||||
// we output a trivial expression "1".
|
||||
if (_x.func_expr().ret() == FunctionCall::SINGLE)
|
||||
visit(_x.func_expr());
|
||||
else
|
||||
m_output << dictionaryToken();
|
||||
break;
|
||||
case Expression::kLowcall:
|
||||
visit(_x.lowcall());
|
||||
break;
|
||||
case Expression::kCreate:
|
||||
visit(_x.create());
|
||||
break;
|
||||
case Expression::EXPR_ONEOF_NOT_SET:
|
||||
m_output << dictionaryToken();
|
||||
@ -202,54 +244,18 @@ void ProtoConverter::visit(BinaryOp const& _x)
|
||||
|
||||
void ProtoConverter::visit(VarDecl const& _x)
|
||||
{
|
||||
m_output << "let x_" << m_numLiveVars << " := ";
|
||||
string varName = newVarName();
|
||||
m_output << "let " << varName << " := ";
|
||||
visit(_x.expr());
|
||||
m_numVarsPerScope.top()++;
|
||||
m_numLiveVars++;
|
||||
m_output << "\n";
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(EmptyVarDecl const&)
|
||||
{
|
||||
m_output << "let x_" << m_numLiveVars++ << "\n";
|
||||
m_numVarsPerScope.top()++;
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(MultiVarDecl const& _x)
|
||||
{
|
||||
size_t funcId = (static_cast<size_t>(_x.func_index()) % m_functionVecMultiReturnValue.size());
|
||||
|
||||
int numInParams = m_functionVecMultiReturnValue.at(funcId).first;
|
||||
int numOutParams = m_functionVecMultiReturnValue.at(funcId).second;
|
||||
|
||||
// Ensure that the chosen function returns at least 2 and at most 4 values
|
||||
yulAssert(
|
||||
((numOutParams >= 2) && (numOutParams <= 4)),
|
||||
"Proto fuzzer: Multi variable declaration calls a function with either too few or too many output params."
|
||||
);
|
||||
|
||||
// We must start variable numbering past the number of live variables at this point in time.
|
||||
// This creates let x_p,..., x_k :=
|
||||
// (k-p)+1 = numOutParams
|
||||
m_output <<
|
||||
"let " <<
|
||||
dev::suffixedVariableNameList("x_", m_numLiveVars, m_numLiveVars + numOutParams) <<
|
||||
" := ";
|
||||
|
||||
// Create RHS of multi var decl
|
||||
m_output << "foo_" << functionTypeToString(NumFunctionReturns::Multiple) << "_" << funcId;
|
||||
m_output << "(";
|
||||
visitFunctionInputParams(_x, numInParams);
|
||||
m_output << ")\n";
|
||||
// Update live variables in scope and in total to account for the variables created by this
|
||||
// multi variable declaration.
|
||||
m_numVarsPerScope.top() += numOutParams;
|
||||
m_numLiveVars += numOutParams;
|
||||
m_scopeVars.top().push_back(varName);
|
||||
m_variables.push_back(varName);
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(TypedVarDecl const& _x)
|
||||
{
|
||||
m_output << "let x_" << m_numLiveVars;
|
||||
string varName = newVarName();
|
||||
m_output << "let " << varName;
|
||||
switch (_x.type())
|
||||
{
|
||||
case TypedVarDecl::BOOL:
|
||||
@ -308,8 +314,8 @@ void ProtoConverter::visit(TypedVarDecl const& _x)
|
||||
m_output << " : u256\n";
|
||||
break;
|
||||
}
|
||||
m_numVarsPerScope.top()++;
|
||||
m_numLiveVars++;
|
||||
m_scopeVars.top().push_back(varName);
|
||||
m_variables.push_back(varName);
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(UnaryOp const& _x)
|
||||
@ -337,6 +343,12 @@ void ProtoConverter::visit(UnaryOp const& _x)
|
||||
case UnaryOp::EXTCODEHASH:
|
||||
m_output << "extcodehash";
|
||||
break;
|
||||
case UnaryOp::BALANCE:
|
||||
m_output << "balance";
|
||||
break;
|
||||
case UnaryOp::BLOCKHASH:
|
||||
m_output << "blockhash";
|
||||
break;
|
||||
}
|
||||
m_output << "(";
|
||||
visit(_x.operand());
|
||||
@ -385,6 +397,36 @@ void ProtoConverter::visit(NullaryOp const& _x)
|
||||
case NullaryOp::RETURNDATASIZE:
|
||||
m_output << "returndatasize()";
|
||||
break;
|
||||
case NullaryOp::ADDRESS:
|
||||
m_output << "address()";
|
||||
break;
|
||||
case NullaryOp::ORIGIN:
|
||||
m_output << "origin()";
|
||||
break;
|
||||
case NullaryOp::CALLER:
|
||||
m_output << "caller()";
|
||||
break;
|
||||
case NullaryOp::CALLVALUE:
|
||||
m_output << "callvalue()";
|
||||
break;
|
||||
case NullaryOp::GASPRICE:
|
||||
m_output << "gasprice()";
|
||||
break;
|
||||
case NullaryOp::COINBASE:
|
||||
m_output << "coinbase()";
|
||||
break;
|
||||
case NullaryOp::TIMESTAMP:
|
||||
m_output << "timestamp()";
|
||||
break;
|
||||
case NullaryOp::NUMBER:
|
||||
m_output << "number()";
|
||||
break;
|
||||
case NullaryOp::DIFFICULTY:
|
||||
m_output << "difficulty()";
|
||||
break;
|
||||
case NullaryOp::GASLIMIT:
|
||||
m_output << "gaslimit()";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -500,9 +542,7 @@ void ProtoConverter::visit(AssignmentStatement const& _x)
|
||||
m_output << "\n";
|
||||
}
|
||||
|
||||
// Called at the time function call is being made
|
||||
template <class T>
|
||||
void ProtoConverter::visitFunctionInputParams(T const& _x, unsigned _numInputParams)
|
||||
void ProtoConverter::visitFunctionInputParams(FunctionCall const& _x, unsigned _numInputParams)
|
||||
{
|
||||
// We reverse the order of function input visits since it helps keep this switch case concise.
|
||||
switch (_numInputParams)
|
||||
@ -530,19 +570,128 @@ void ProtoConverter::visitFunctionInputParams(T const& _x, unsigned _numInputPar
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(MultiAssignment const& _x)
|
||||
bool ProtoConverter::functionValid(FunctionCall_Returns _type, unsigned _numOutParams)
|
||||
{
|
||||
size_t funcId = (static_cast<size_t>(_x.func_index()) % m_functionVecMultiReturnValue.size());
|
||||
unsigned numInParams = m_functionVecMultiReturnValue.at(funcId).first;
|
||||
unsigned numOutParams = m_functionVecMultiReturnValue.at(funcId).second;
|
||||
switch (_type)
|
||||
{
|
||||
case FunctionCall::ZERO:
|
||||
return _numOutParams == 0;
|
||||
case FunctionCall::SINGLE:
|
||||
return _numOutParams == 1;
|
||||
case FunctionCall::MULTIDECL:
|
||||
case FunctionCall::MULTIASSIGN:
|
||||
return _numOutParams > 1;
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoConverter::convertFunctionCall(
|
||||
FunctionCall const& _x,
|
||||
std::string _name,
|
||||
unsigned _numInParams,
|
||||
bool _newLine
|
||||
)
|
||||
{
|
||||
m_output << _name << "(";
|
||||
visitFunctionInputParams(_x, _numInParams);
|
||||
m_output << ")";
|
||||
if (_newLine)
|
||||
m_output << "\n";
|
||||
}
|
||||
|
||||
vector<string> ProtoConverter::createVarDecls(unsigned _start, unsigned _end, bool _isAssignment)
|
||||
{
|
||||
m_output << "let ";
|
||||
vector<string> varsVec = createVars(_start, _end);
|
||||
if (_isAssignment)
|
||||
m_output << " := ";
|
||||
else
|
||||
m_output << "\n";
|
||||
return varsVec;
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(FunctionCall const& _x)
|
||||
{
|
||||
bool functionAvailable = m_functionSigMap.size() > 0;
|
||||
unsigned numInParams, numOutParams;
|
||||
string funcName;
|
||||
FunctionCall_Returns funcType = _x.ret();
|
||||
if (functionAvailable)
|
||||
{
|
||||
yulAssert(m_functions.size() > 0, "Proto fuzzer: No function in scope");
|
||||
funcName = m_functions[_x.func_index() % m_functions.size()];
|
||||
auto ret = m_functionSigMap.at(funcName);
|
||||
numInParams = ret.first;
|
||||
numOutParams = ret.second;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there are no functions available, calls to functions that
|
||||
// return a single value may be replaced by a dictionary token.
|
||||
if (funcType == FunctionCall::SINGLE)
|
||||
m_output << dictionaryToken();
|
||||
return;
|
||||
}
|
||||
|
||||
// If function selected for function call does not meet interface
|
||||
// requirements (num output values) for the function type
|
||||
// specified, then we return early unless it is a function call
|
||||
// that returns a single value (which may be replaced by a
|
||||
// dictionary token.
|
||||
if (!functionValid(funcType, numOutParams))
|
||||
{
|
||||
if (funcType == FunctionCall::SINGLE)
|
||||
m_output << dictionaryToken();
|
||||
return;
|
||||
}
|
||||
|
||||
// If we are here, it means that we have at least one valid
|
||||
// function for making the function call
|
||||
switch (funcType)
|
||||
{
|
||||
case FunctionCall::ZERO:
|
||||
convertFunctionCall(_x, funcName, numInParams);
|
||||
break;
|
||||
case FunctionCall::SINGLE:
|
||||
// Since functions that return a single value are used as expressions
|
||||
// we do not print a newline because it is done by the expression
|
||||
// visitor.
|
||||
convertFunctionCall(_x, funcName, numInParams, /*newLine=*/false);
|
||||
break;
|
||||
case FunctionCall::MULTIDECL:
|
||||
// Hack: Disallow (multi) variable declarations until scope extension
|
||||
// is implemented for "for-init"
|
||||
if (!m_inForInitScope)
|
||||
{
|
||||
// Ensure that the chosen function returns at most 4 values
|
||||
yulAssert(
|
||||
((numOutParams >= 2) && (numOutParams <= 4)),
|
||||
"Proto fuzzer: Multi assignment calls a function that has either too many or too few output parameters."
|
||||
numOutParams <= 4,
|
||||
"Proto fuzzer: Function call with too many output params encountered."
|
||||
);
|
||||
|
||||
// Obtain variable name suffix
|
||||
unsigned startIdx = counter();
|
||||
vector<string> varsVec = createVarDecls(
|
||||
startIdx,
|
||||
startIdx + numOutParams,
|
||||
/*isAssignment=*/true
|
||||
);
|
||||
|
||||
// Create RHS of multi var decl
|
||||
convertFunctionCall(_x, funcName, numInParams);
|
||||
// Add newly minted vars in the multidecl statement to current scope
|
||||
addVarsToScope(varsVec);
|
||||
}
|
||||
break;
|
||||
case FunctionCall::MULTIASSIGN:
|
||||
// Ensure that the chosen function returns at most 4 values
|
||||
yulAssert(
|
||||
numOutParams <= 4,
|
||||
"Proto fuzzer: Function call with too many output params encountered."
|
||||
);
|
||||
|
||||
// Convert LHS of multi assignment
|
||||
// We reverse the order of out param visits since the order does not matter. This helps reduce the size of this
|
||||
// switch statement.
|
||||
// We reverse the order of out param visits since the order does not matter.
|
||||
// This helps reduce the size of this switch statement.
|
||||
switch (numOutParams)
|
||||
{
|
||||
case 4:
|
||||
@ -559,56 +708,77 @@ void ProtoConverter::visit(MultiAssignment const& _x)
|
||||
visit(_x.out_param1());
|
||||
break;
|
||||
default:
|
||||
yulAssert(false, "Proto fuzzer: Function call with too many input parameters.");
|
||||
yulAssert(false, "Proto fuzzer: Function call with too many or too few input parameters.");
|
||||
break;
|
||||
}
|
||||
m_output << " := ";
|
||||
|
||||
// Convert RHS of multi assignment
|
||||
m_output << "foo_" << functionTypeToString(NumFunctionReturns::Multiple) << "_" << funcId;
|
||||
m_output << "(";
|
||||
visitFunctionInputParams(_x, numInParams);
|
||||
m_output << ")\n";
|
||||
convertFunctionCall(_x, funcName, numInParams);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(FunctionCallNoReturnVal const& _x)
|
||||
void ProtoConverter::visit(LowLevelCall const& _x)
|
||||
{
|
||||
size_t funcId = (static_cast<size_t>(_x.func_index()) % m_functionVecNoReturnValue.size());
|
||||
unsigned numInParams = m_functionVecNoReturnValue.at(funcId);
|
||||
m_output << "foo_" << functionTypeToString(NumFunctionReturns::None) << "_" << funcId;
|
||||
m_output << "(";
|
||||
visitFunctionInputParams(_x, numInParams);
|
||||
m_output << ")\n";
|
||||
LowLevelCall_Type type = _x.callty();
|
||||
switch (type)
|
||||
{
|
||||
case LowLevelCall::CALL:
|
||||
m_output << "call(";
|
||||
break;
|
||||
case LowLevelCall::CALLCODE:
|
||||
m_output << "callcode(";
|
||||
break;
|
||||
case LowLevelCall::DELEGATECALL:
|
||||
m_output << "delegatecall(";
|
||||
break;
|
||||
case LowLevelCall::STATICCALL:
|
||||
m_output << "staticcall(";
|
||||
break;
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(FunctionCallSingleReturnVal const& _x)
|
||||
visit(_x.gas());
|
||||
m_output << ", ";
|
||||
visit(_x.addr());
|
||||
m_output << ", ";
|
||||
if (type == LowLevelCall::CALL || LowLevelCall::CALLCODE)
|
||||
{
|
||||
size_t funcId = (static_cast<size_t>(_x.func_index()) % m_functionVecSingleReturnValue.size());
|
||||
unsigned numInParams = m_functionVecSingleReturnValue.at(funcId);
|
||||
m_output << "foo_" << functionTypeToString(NumFunctionReturns::Single) << "_" << funcId;
|
||||
m_output << "(";
|
||||
visitFunctionInputParams(_x, numInParams);
|
||||
visit(_x.wei());
|
||||
m_output << ", ";
|
||||
}
|
||||
visit(_x.in());
|
||||
m_output << ", ";
|
||||
visit(_x.insize());
|
||||
m_output << ", ";
|
||||
visit(_x.out());
|
||||
m_output << ", ";
|
||||
visit(_x.outsize());
|
||||
m_output << ")";
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(FunctionCall const& _x)
|
||||
void ProtoConverter::visit(Create const& _x)
|
||||
{
|
||||
switch (_x.functioncall_oneof_case())
|
||||
Create_Type type = _x.createty();
|
||||
switch (type)
|
||||
{
|
||||
case FunctionCall::kCallZero:
|
||||
visit(_x.call_zero());
|
||||
case Create::CREATE:
|
||||
m_output << "create(";
|
||||
break;
|
||||
case FunctionCall::kCallMultidecl:
|
||||
// Hack: Disallow (multi) variable declarations until scope extension is implemented for "for-init"
|
||||
if (!m_inForInitScope)
|
||||
visit(_x.call_multidecl());
|
||||
break;
|
||||
case FunctionCall::kCallMultiassign:
|
||||
visit(_x.call_multiassign());
|
||||
break;
|
||||
case FunctionCall::FUNCTIONCALL_ONEOF_NOT_SET:
|
||||
case Create::CREATE2:
|
||||
m_output << "create2(";
|
||||
break;
|
||||
}
|
||||
visit(_x.wei());
|
||||
m_output << ", ";
|
||||
visit(_x.position());
|
||||
m_output << ", ";
|
||||
visit(_x.size());
|
||||
if (type == Create::CREATE2)
|
||||
{
|
||||
m_output << ", ";
|
||||
visit(_x.value());
|
||||
}
|
||||
m_output << ")";
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(IfStmt const& _x)
|
||||
@ -693,7 +863,7 @@ void ProtoConverter::visit(CaseStmt const& _x)
|
||||
// a case statement containing a case literal that has already been used in a
|
||||
// previous case statement. If the hash (u256 value) matches a previous hash,
|
||||
// then we simply don't create a new case statement.
|
||||
string noDoubleQuoteStr = "";
|
||||
string noDoubleQuoteStr{""};
|
||||
if (literal.size() > 2)
|
||||
{
|
||||
// Ensure that all characters in the string literal except the first
|
||||
@ -742,7 +912,7 @@ void ProtoConverter::visit(SwitchStmt const& _x)
|
||||
{
|
||||
if (_x.case_stmt_size() > 0 || _x.has_default_block())
|
||||
{
|
||||
std::set<dev::u256> s;
|
||||
std::set<u256> s;
|
||||
m_switchLiteralSetPerScope.push(s);
|
||||
m_output << "switch ";
|
||||
visit(_x.switch_expr());
|
||||
@ -828,6 +998,9 @@ void ProtoConverter::visit(Statement const& _x)
|
||||
visit(_x.decl());
|
||||
break;
|
||||
case Statement::kAssignment:
|
||||
// Create an assignment statement only if there is at least one variable
|
||||
// declaration that is in scope.
|
||||
if (varDeclAvailable())
|
||||
visit(_x.assignment());
|
||||
break;
|
||||
case Statement::kIfstmt:
|
||||
@ -869,133 +1042,309 @@ void ProtoConverter::visit(Statement const& _x)
|
||||
visit(_x.terminatestmt());
|
||||
break;
|
||||
case Statement::kFunctioncall:
|
||||
// Return early if a function call cannot be created
|
||||
if (functionCallNotPossible(_x.functioncall().ret()))
|
||||
return;
|
||||
visit(_x.functioncall());
|
||||
break;
|
||||
case Statement::kFuncdef:
|
||||
if (!m_inForInitScope)
|
||||
visit(_x.funcdef());
|
||||
break;
|
||||
case Statement::kPop:
|
||||
visit(_x.pop());
|
||||
break;
|
||||
case Statement::STMT_ONEOF_NOT_SET:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(Block const& _x)
|
||||
void ProtoConverter::openScope(vector<string> const& _funcParams)
|
||||
{
|
||||
m_scopeVars.push({});
|
||||
m_scopeFuncs.push({});
|
||||
if (!_funcParams.empty())
|
||||
addVarsToScope(_funcParams);
|
||||
}
|
||||
|
||||
void ProtoConverter::updateFunctionMaps(string const& _var)
|
||||
{
|
||||
unsigned erased = m_functionSigMap.erase(_var);
|
||||
|
||||
for (auto const& i: m_functionDefMap)
|
||||
if (i.second == _var)
|
||||
{
|
||||
erased += m_functionDefMap.erase(i.first);
|
||||
break;
|
||||
}
|
||||
|
||||
yulAssert(erased == 2, "Proto fuzzer: Function maps not updated");
|
||||
}
|
||||
|
||||
void ProtoConverter::closeScope()
|
||||
{
|
||||
for (auto const& var: m_scopeVars.top())
|
||||
{
|
||||
unsigned numVarsRemoved = m_variables.size();
|
||||
m_variables.erase(remove(m_variables.begin(), m_variables.end(), var), m_variables.end());
|
||||
numVarsRemoved -= m_variables.size();
|
||||
yulAssert(
|
||||
numVarsRemoved == 1,
|
||||
"Proto fuzzer: Nothing or too much went out of scope"
|
||||
);
|
||||
}
|
||||
m_scopeVars.pop();
|
||||
|
||||
for (auto const& f: m_scopeFuncs.top())
|
||||
{
|
||||
unsigned numFuncsRemoved = m_functions.size();
|
||||
m_functions.erase(remove(m_functions.begin(), m_functions.end(), f), m_functions.end());
|
||||
numFuncsRemoved -= m_functions.size();
|
||||
yulAssert(
|
||||
numFuncsRemoved == 1,
|
||||
"Proto fuzzer: Nothing or too much went out of scope"
|
||||
);
|
||||
updateFunctionMaps(f);
|
||||
}
|
||||
m_scopeFuncs.pop();
|
||||
}
|
||||
|
||||
void ProtoConverter::addVarsToScope(vector<string> const& _vars)
|
||||
{
|
||||
for (string const& i: _vars)
|
||||
{
|
||||
m_variables.push_back(i);
|
||||
m_scopeVars.top().push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(Block const& _x, vector<string> _funcParams)
|
||||
{
|
||||
openScope(_funcParams);
|
||||
|
||||
// Register function declarations in this scope unless this
|
||||
// scope belongs to for-init (in which function declarations
|
||||
// are forbidden).
|
||||
for (auto const& statement: _x.statements())
|
||||
if (statement.has_funcdef() && !m_inForInitScope)
|
||||
registerFunction(&statement.funcdef());
|
||||
|
||||
if (_x.statements_size() > 0)
|
||||
{
|
||||
m_numVarsPerScope.push(0);
|
||||
m_output << "{\n";
|
||||
for (auto const& st: _x.statements())
|
||||
visit(st);
|
||||
m_output << "}\n";
|
||||
m_numLiveVars -= m_numVarsPerScope.top();
|
||||
m_numVarsPerScope.pop();
|
||||
}
|
||||
else
|
||||
m_output << "{}\n";
|
||||
closeScope();
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(SpecialBlock const& _x)
|
||||
vector<string> ProtoConverter::createVars(unsigned _startIdx, unsigned _endIdx)
|
||||
{
|
||||
m_numVarsPerScope.push(0);
|
||||
m_output << "{\n";
|
||||
visit(_x.var());
|
||||
if (_x.statements_size() > 0)
|
||||
for (auto const& st: _x.statements())
|
||||
visit(st);
|
||||
m_numLiveVars -= m_numVarsPerScope.top();
|
||||
m_numVarsPerScope.pop();
|
||||
m_output << "}\n";
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void ProtoConverter::createFunctionDefAndCall(T const& _x, unsigned _numInParams, unsigned _numOutParams, NumFunctionReturns _type)
|
||||
{
|
||||
yulAssert(
|
||||
((_numInParams <= modInputParams - 1) && (_numOutParams <= modOutputParams - 1)),
|
||||
"Proto fuzzer: Too many function I/O parameters requested."
|
||||
yulAssert(_endIdx > _startIdx, "Proto fuzzer: Variable indices not in range");
|
||||
string varsStr = dev::suffixedVariableNameList("x_", _startIdx, _endIdx);
|
||||
m_output << varsStr;
|
||||
vector<string> varsVec;
|
||||
boost::split(
|
||||
varsVec,
|
||||
varsStr,
|
||||
boost::algorithm::is_any_of(", "),
|
||||
boost::algorithm::token_compress_on
|
||||
);
|
||||
|
||||
// At the time of function definition creation, the number of live variables must be 0.
|
||||
// This is because we always create only as many variables as we need within function scope.
|
||||
yulAssert(m_numLiveVars == 0, "Proto fuzzer: Unused live variable found.");
|
||||
|
||||
// Signature
|
||||
// This creates function foo_<noreturn|singlereturn|multireturn>_<m_numFunctionSets>(x_0,...,x_n)
|
||||
m_output << "function foo_" << functionTypeToString(_type) << "_" << m_numFunctionSets;
|
||||
m_output << "(";
|
||||
if (_numInParams > 0)
|
||||
m_output << dev::suffixedVariableNameList("x_", 0, _numInParams);
|
||||
m_output << ")";
|
||||
|
||||
// Book keeping for variables in function scope and in nested scopes
|
||||
m_numVarsPerScope.push(_numInParams);
|
||||
m_numLiveVars += _numInParams;
|
||||
|
||||
// This creates -> x_n+1,...,x_r
|
||||
if (_numOutParams > 0)
|
||||
{
|
||||
m_output << " -> " << dev::suffixedVariableNameList("x_", _numInParams, _numInParams + _numOutParams);
|
||||
// More bookkeeping
|
||||
m_numVarsPerScope.top() += _numOutParams;
|
||||
m_numLiveVars += _numOutParams;
|
||||
yulAssert(
|
||||
varsVec.size() == (_endIdx - _startIdx),
|
||||
"Proto fuzzer: Variable count mismatch during function definition"
|
||||
);
|
||||
m_counter += varsVec.size();
|
||||
return varsVec;
|
||||
}
|
||||
m_output << "\n";
|
||||
|
||||
// Body
|
||||
visit(_x.statements());
|
||||
void ProtoConverter::registerFunction(FunctionDef const* _x)
|
||||
{
|
||||
unsigned numInParams = _x->num_input_params() % s_modInputParams;
|
||||
unsigned numOutParams = _x->num_output_params() % s_modOutputParams;
|
||||
NumFunctionReturns numReturns;
|
||||
if (numOutParams == 0)
|
||||
numReturns = NumFunctionReturns::None;
|
||||
else if (numOutParams == 1)
|
||||
numReturns = NumFunctionReturns::Single;
|
||||
else
|
||||
numReturns = NumFunctionReturns::Multiple;
|
||||
|
||||
// Ensure that variable stack is balanced
|
||||
m_numLiveVars -= m_numVarsPerScope.top();
|
||||
m_numVarsPerScope.pop();
|
||||
yulAssert(m_numLiveVars == 0, "Proto fuzzer: Variable stack after function definition is unbalanced.");
|
||||
// Generate function name
|
||||
string funcName = functionName(numReturns);
|
||||
|
||||
// Manually create a multi assignment using global variables
|
||||
// This prints a_0, ..., a_k-1 for this function that returns "k" values
|
||||
if (_numOutParams > 0)
|
||||
m_output << dev::suffixedVariableNameList("a_", 0, _numOutParams) << " := ";
|
||||
// Register function
|
||||
auto ret = m_functionSigMap.emplace(make_pair(funcName, make_pair(numInParams, numOutParams)));
|
||||
yulAssert(ret.second, "Proto fuzzer: Function already exists.");
|
||||
m_functions.push_back(funcName);
|
||||
m_scopeFuncs.top().push_back(funcName);
|
||||
m_functionDefMap.emplace(make_pair(_x, funcName));
|
||||
}
|
||||
|
||||
// Call the function with the correct number of input parameters via calls to calldataload with
|
||||
// incremental addresses.
|
||||
m_output << "foo_" << functionTypeToString(_type) << "_" << std::to_string(m_numFunctionSets);
|
||||
m_output << "(";
|
||||
void ProtoConverter::fillFunctionCallInput(unsigned _numInParams)
|
||||
{
|
||||
for (unsigned i = 0; i < _numInParams; i++)
|
||||
{
|
||||
m_output << "calldataload(" << std::to_string(i*32) << ")";
|
||||
// Throw a 4-sided dice to choose whether to populate function input
|
||||
// argument from a pseudo-randomly chosen slot in one of the following
|
||||
// locations: calldata, memory, storage, or yul optimizer dictionary.
|
||||
unsigned diceValue = counter() % 4;
|
||||
// Pseudo-randomly choose one of the first ten 32-byte
|
||||
// aligned slots.
|
||||
string slot = to_string((counter() % 10) * 32);
|
||||
switch (diceValue)
|
||||
{
|
||||
case 0:
|
||||
m_output << "calldataload(" << slot << ")";
|
||||
break;
|
||||
case 1:
|
||||
m_output << "mload(" << slot << ")";
|
||||
break;
|
||||
case 2:
|
||||
m_output << "sload(" << slot << ")";
|
||||
break;
|
||||
case 3:
|
||||
// Call to dictionaryToken() automatically picks a token
|
||||
// at a pseudo-random location.
|
||||
m_output << dictionaryToken();
|
||||
break;
|
||||
}
|
||||
if (i < _numInParams - 1)
|
||||
m_output << ",";
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoConverter::saveFunctionCallOutput(vector<string> const& _varsVec)
|
||||
{
|
||||
for (auto const& var: _varsVec)
|
||||
{
|
||||
// Flip a dice to choose whether to save output values
|
||||
// in storage or memory.
|
||||
bool coinFlip = counter() % 2 == 0;
|
||||
// Pseudo-randomly choose one of the first ten 32-byte
|
||||
// aligned slots.
|
||||
string slot = to_string((counter() % 10) * 32);
|
||||
if (coinFlip)
|
||||
m_output << "sstore(" << slot << ", " << var << ")\n";
|
||||
else
|
||||
m_output << "mstore(" << slot << ", " << var << ")\n";
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoConverter::createFunctionCall(
|
||||
string _funcName,
|
||||
unsigned _numInParams,
|
||||
unsigned _numOutParams
|
||||
)
|
||||
{
|
||||
vector<string> varsVec{};
|
||||
if (_numOutParams > 0)
|
||||
{
|
||||
unsigned startIdx = counter();
|
||||
// Prints the following to output stream "let x_i,...,x_n := "
|
||||
varsVec = createVarDecls(
|
||||
startIdx,
|
||||
startIdx + _numOutParams,
|
||||
/*isAssignment=*/true
|
||||
);
|
||||
}
|
||||
|
||||
// Call the function with the correct number of input parameters
|
||||
m_output << _funcName << "(";
|
||||
if (_numInParams > 0)
|
||||
fillFunctionCallInput(_numInParams);
|
||||
m_output << ")\n";
|
||||
|
||||
for (unsigned i = 0; i < _numOutParams; i++)
|
||||
m_output << "sstore(" << std::to_string(i*32) << ", a_" << std::to_string(i) << ")\n";
|
||||
if (!varsVec.empty())
|
||||
{
|
||||
// Save values returned by function so that they are reflected
|
||||
// in the interpreter trace.
|
||||
saveFunctionCallOutput(varsVec);
|
||||
// Add newly minted vars to current scope
|
||||
addVarsToScope(varsVec);
|
||||
}
|
||||
else
|
||||
yulAssert(_numOutParams == 0, "Proto fuzzer: Function return value not saved");
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(FunctionDefinitionNoReturnVal const& _x)
|
||||
void ProtoConverter::createFunctionDefAndCall(
|
||||
FunctionDef const& _x,
|
||||
unsigned _numInParams,
|
||||
unsigned _numOutParams
|
||||
)
|
||||
{
|
||||
unsigned numInParams = _x.num_input_params() % modInputParams;
|
||||
unsigned numOutParams = 0;
|
||||
createFunctionDefAndCall(_x, numInParams, numOutParams, NumFunctionReturns::None);
|
||||
yulAssert(
|
||||
((_numInParams <= s_modInputParams - 1) && (_numOutParams <= s_modOutputParams - 1)),
|
||||
"Proto fuzzer: Too many function I/O parameters requested."
|
||||
);
|
||||
|
||||
// Obtain function name
|
||||
yulAssert(m_functionDefMap.count(&_x), "Proto fuzzer: Unregistered function");
|
||||
string funcName = m_functionDefMap.at(&_x);
|
||||
|
||||
vector<string> varsVec = {};
|
||||
m_output << "function " << funcName << "(";
|
||||
unsigned startIdx = counter();
|
||||
if (_numInParams > 0)
|
||||
varsVec = createVars(startIdx, startIdx + _numInParams);
|
||||
m_output << ")";
|
||||
|
||||
vector<string> outVarsVec = {};
|
||||
// This creates -> x_n+1,...,x_r
|
||||
if (_numOutParams > 0)
|
||||
{
|
||||
m_output << " -> ";
|
||||
if (varsVec.empty())
|
||||
{
|
||||
yulAssert(_numInParams == 0, "Proto fuzzer: Input parameters not processed correctly");
|
||||
varsVec = createVars(startIdx, startIdx + _numOutParams);
|
||||
}
|
||||
else
|
||||
{
|
||||
outVarsVec = createVars(startIdx + _numInParams, startIdx + _numInParams + _numOutParams);
|
||||
varsVec.insert(varsVec.end(), outVarsVec.begin(), outVarsVec.end());
|
||||
}
|
||||
}
|
||||
yulAssert(varsVec.size() == _numInParams + _numOutParams, "Proto fuzzer: Function parameters not processed correctly");
|
||||
|
||||
m_output << "\n";
|
||||
|
||||
// If function definition is in for-loop body, update
|
||||
bool wasInForBody = m_inForBodyScope;
|
||||
m_inForBodyScope = false;
|
||||
|
||||
bool wasInFunctionDef = m_inFunctionDef;
|
||||
m_inFunctionDef = true;
|
||||
|
||||
// Body
|
||||
visit(_x.block(), varsVec);
|
||||
|
||||
m_inForBodyScope = wasInForBody;
|
||||
m_inFunctionDef = wasInFunctionDef;
|
||||
|
||||
yulAssert(
|
||||
!m_inForInitScope,
|
||||
"Proto fuzzer: Trying to create function call inside for-init block"
|
||||
);
|
||||
createFunctionCall(funcName, _numInParams, _numOutParams);
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(FunctionDefinitionSingleReturnVal const& _x)
|
||||
void ProtoConverter::visit(FunctionDef const& _x)
|
||||
{
|
||||
unsigned numInParams = _x.num_input_params() % modInputParams;
|
||||
unsigned numOutParams = 1;
|
||||
createFunctionDefAndCall(_x, numInParams, numOutParams, NumFunctionReturns::Single);
|
||||
unsigned numInParams = _x.num_input_params() % s_modInputParams;
|
||||
unsigned numOutParams = _x.num_output_params() % s_modOutputParams;
|
||||
createFunctionDefAndCall(_x, numInParams, numOutParams);
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(FunctionDefinitionMultiReturnVal const& _x)
|
||||
void ProtoConverter::visit(PopStmt const& _x)
|
||||
{
|
||||
unsigned numInParams = _x.num_input_params() % modInputParams;
|
||||
// Synthesize at least 2 return parameters and at most (modOutputParams - 1)
|
||||
unsigned numOutParams = std::max<unsigned>(2, _x.num_output_params() % modOutputParams);
|
||||
createFunctionDefAndCall(_x, numInParams, numOutParams, NumFunctionReturns::Multiple);
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(FunctionDefinition const& _x)
|
||||
{
|
||||
visit(_x.fd_zero());
|
||||
visit(_x.fd_one());
|
||||
visit(_x.fd_multi());
|
||||
m_numFunctionSets++;
|
||||
m_output << "pop(";
|
||||
visit(_x.expr());
|
||||
m_output << ")\n";
|
||||
}
|
||||
|
||||
void ProtoConverter::visit(Program const& _x)
|
||||
@ -1004,25 +1353,14 @@ void ProtoConverter::visit(Program const& _x)
|
||||
m_inputSize = _x.ByteSizeLong();
|
||||
|
||||
/* Program template is as follows
|
||||
* Four Globals a_0, a_1, a_2, and a_3 to hold up to four function return values
|
||||
*
|
||||
* Repeated function definitions followed by function calls of the respective function
|
||||
* Zero or more statements. If function definition is present, it is
|
||||
* called post definition.
|
||||
* Example: function foo(x_0) -> x_1 {}
|
||||
* a_0 := foo(calldataload(0))
|
||||
* sstore(0, a_0)
|
||||
* x_2 := foo(calldataload(0))
|
||||
* sstore(0, x_2)
|
||||
*/
|
||||
m_output << "{\n";
|
||||
// Create globals at the beginning
|
||||
// This creates let a_0, a_1, a_2, a_3 (followed by a new line)
|
||||
m_output << "let " << dev::suffixedVariableNameList("a_", 0, modOutputParams - 1) << "\n";
|
||||
// Register function interface. Useful while visiting multi var decl/assignment statements.
|
||||
for (auto const& f: _x.funcs())
|
||||
registerFunction(f);
|
||||
|
||||
for (auto const& f: _x.funcs())
|
||||
visit(f);
|
||||
|
||||
yulAssert((unsigned)_x.funcs_size() == m_numFunctionSets, "Proto fuzzer: Functions not correctly registered.");
|
||||
visit(_x.block());
|
||||
m_output << "}\n";
|
||||
}
|
||||
|
||||
@ -1032,25 +1370,15 @@ string ProtoConverter::programToString(Program const& _input)
|
||||
return m_output.str();
|
||||
}
|
||||
|
||||
void ProtoConverter::registerFunction(FunctionDefinition const& _x)
|
||||
{
|
||||
// No return and single return functions explicitly state the number of values returned
|
||||
registerFunction(_x.fd_zero(), NumFunctionReturns::None);
|
||||
registerFunction(_x.fd_one(), NumFunctionReturns::Single);
|
||||
// A multi return function can have between two and (modOutputParams - 1) parameters
|
||||
unsigned numOutParams = std::max<unsigned>(2, _x.fd_multi().num_output_params() % modOutputParams);
|
||||
registerFunction(_x.fd_multi(), NumFunctionReturns::Multiple, numOutParams);
|
||||
}
|
||||
|
||||
std::string ProtoConverter::functionTypeToString(NumFunctionReturns _type)
|
||||
{
|
||||
switch (_type)
|
||||
{
|
||||
case NumFunctionReturns::None:
|
||||
return "noreturn";
|
||||
return "n";
|
||||
case NumFunctionReturns::Single:
|
||||
return "singlereturn";
|
||||
return "s";
|
||||
case NumFunctionReturns::Multiple:
|
||||
return "multireturn";
|
||||
return "m";
|
||||
}
|
||||
}
|
@ -40,14 +40,12 @@ class ProtoConverter
|
||||
public:
|
||||
ProtoConverter()
|
||||
{
|
||||
m_numLiveVars = 0;
|
||||
m_numVarsPerScope.push(m_numLiveVars);
|
||||
m_numFunctionSets = 0;
|
||||
m_inForBodyScope = false;
|
||||
m_inForInitScope = false;
|
||||
m_numNestedForLoops = 0;
|
||||
m_counter = 0;
|
||||
m_inputSize = 0;
|
||||
m_inFunctionDef = false;
|
||||
}
|
||||
ProtoConverter(ProtoConverter const&) = delete;
|
||||
ProtoConverter(ProtoConverter&&) = delete;
|
||||
@ -55,22 +53,23 @@ public:
|
||||
|
||||
private:
|
||||
void visit(BinaryOp const&);
|
||||
void visit(Block const&);
|
||||
void visit(SpecialBlock const&);
|
||||
|
||||
/// Visits a basic block optionally adding @a _funcParams to scope.
|
||||
/// @param _block Reference to a basic block of yul statements.
|
||||
/// @param _funcParams List of function parameter names, defaults to
|
||||
/// an empty vector.
|
||||
void visit(Block const& _block, std::vector<std::string> _funcParams = {});
|
||||
|
||||
std::string visit(Literal const&);
|
||||
void visit(VarRef const&);
|
||||
void visit(Expression const&);
|
||||
void visit(VarDecl const&);
|
||||
void visit(EmptyVarDecl const&);
|
||||
void visit(MultiVarDecl const&);
|
||||
void visit(TypedVarDecl const&);
|
||||
void visit(UnaryOp const&);
|
||||
void visit(AssignmentStatement const&);
|
||||
void visit(MultiAssignment const&);
|
||||
void visit(IfStmt const&);
|
||||
void visit(StoreFunc const&);
|
||||
void visit(Statement const&);
|
||||
void visit(FunctionDefinition const&);
|
||||
void visit(ForStmt const&);
|
||||
void visit(BoundedForStmt const&);
|
||||
void visit(CaseStmt const&);
|
||||
@ -84,17 +83,29 @@ private:
|
||||
void visit(RetRevStmt const&);
|
||||
void visit(SelfDestructStmt const&);
|
||||
void visit(TerminatingStmt const&);
|
||||
void visit(FunctionCallNoReturnVal const&);
|
||||
void visit(FunctionCallSingleReturnVal const&);
|
||||
void visit(FunctionCall const&);
|
||||
void visit(FunctionDefinitionNoReturnVal const&);
|
||||
void visit(FunctionDefinitionSingleReturnVal const&);
|
||||
void visit(FunctionDefinitionMultiReturnVal const&);
|
||||
void visit(FunctionDef const&);
|
||||
void visit(PopStmt const&);
|
||||
void visit(LowLevelCall const&);
|
||||
void visit(Create const&);
|
||||
void visit(Program const&);
|
||||
void registerFunction(FunctionDefinition const&);
|
||||
|
||||
/// Creates a new scope, and adds @a _funcParams to it if it
|
||||
/// is non-empty.
|
||||
void openScope(std::vector<std::string> const& _funcParams);
|
||||
/// Closes current scope
|
||||
void closeScope();
|
||||
/// Adds @a _vars to current scope
|
||||
void addVarsToScope(std::vector<std::string> const& _vars);
|
||||
|
||||
std::string createHex(std::string const& _hexBytes);
|
||||
|
||||
/// Returns a new variable name.
|
||||
std::string newVarName()
|
||||
{
|
||||
return "x_" + std::to_string(counter());
|
||||
}
|
||||
|
||||
/// Accepts an arbitrary string, removes all characters that are neither
|
||||
/// alphabets nor digits from it and returns the said string.
|
||||
std::string createAlphaNum(std::string const& _strBytes);
|
||||
@ -105,30 +116,123 @@ private:
|
||||
Multiple
|
||||
};
|
||||
|
||||
template<class T>
|
||||
void visitFunctionInputParams(T const&, unsigned);
|
||||
void visitFunctionInputParams(FunctionCall const&, unsigned);
|
||||
void createFunctionDefAndCall(FunctionDef const&, unsigned, unsigned);
|
||||
|
||||
template<class T>
|
||||
void createFunctionDefAndCall(T const&, unsigned, unsigned, NumFunctionReturns);
|
||||
/// Convert function type to a string to be used while naming a
|
||||
/// function that is created by a function declaration statement.
|
||||
/// @param _type Type classified according to the number of
|
||||
/// values returned by function.
|
||||
/// @return A string as follows. If _type is
|
||||
/// None -> "n"
|
||||
/// Single -> "s"
|
||||
/// Multiple -> "m"
|
||||
std::string functionTypeToString(NumFunctionReturns _type);
|
||||
|
||||
template <class T>
|
||||
void registerFunction(T const& _x, NumFunctionReturns _type, unsigned _numOutputParams = 0)
|
||||
{
|
||||
unsigned numInputParams = _x.num_input_params() % modInputParams;
|
||||
switch (_type)
|
||||
{
|
||||
case NumFunctionReturns::None:
|
||||
m_functionVecNoReturnValue.push_back(numInputParams);
|
||||
break;
|
||||
case NumFunctionReturns::Single:
|
||||
m_functionVecSingleReturnValue.push_back(numInputParams);
|
||||
break;
|
||||
case NumFunctionReturns::Multiple:
|
||||
m_functionVecMultiReturnValue.push_back(std::make_pair(numInputParams, _numOutputParams));
|
||||
break;
|
||||
}
|
||||
}
|
||||
/// Return true if at least one variable declaration is in scope,
|
||||
/// false otherwise.
|
||||
/// @return True in the following cases:
|
||||
/// - If we are inside a function that has already declared a variable
|
||||
/// - If there is at least one variable declaration that is
|
||||
/// in scope
|
||||
bool varDeclAvailable();
|
||||
|
||||
/// Return true if a function call cannot be made, false otherwise.
|
||||
/// @param _type is an enum denoting the type of function call. It
|
||||
/// can be one of NONE, SINGLE, MULTIDECL, MULTIASSIGN.
|
||||
/// NONE -> Function call does not return a value
|
||||
/// SINGLE -> Function call returns a single value
|
||||
/// MULTIDECL -> Function call returns more than one value
|
||||
/// and it is used to create a multi declaration
|
||||
/// statement
|
||||
/// MULTIASSIGN -> Function call returns more than one value
|
||||
/// and it is used to create a multi assignment
|
||||
/// statement
|
||||
/// @return True if the function call cannot be created for one of the
|
||||
/// following reasons
|
||||
// - It is a SINGLE function call (we reserve SINGLE functions for
|
||||
// expressions)
|
||||
// - It is a MULTIASSIGN function call and we do not have any
|
||||
// variables available for assignment.
|
||||
bool functionCallNotPossible(FunctionCall_Returns _type);
|
||||
|
||||
/// Checks if function call of type @a _type returns the correct number
|
||||
/// of values.
|
||||
/// @param _type Function call type of the function being checked
|
||||
/// @param _numOutParams Number of values returned by the function
|
||||
/// being checked
|
||||
/// @return true if the function returns the correct number of values,
|
||||
/// false otherwise
|
||||
bool functionValid(FunctionCall_Returns _type, unsigned _numOutParams);
|
||||
|
||||
/// Converts protobuf function call to a yul function call and appends
|
||||
/// it to output stream.
|
||||
/// @param _x Protobuf function call
|
||||
/// @param _name Function name
|
||||
/// @param _numInParams Number of input arguments accepted by function
|
||||
/// @param _newLine Flag that prints a new line to the output stream if
|
||||
/// true. Default value for the flag is true.
|
||||
void convertFunctionCall(
|
||||
FunctionCall const& _x,
|
||||
std::string _name,
|
||||
unsigned _numInParams,
|
||||
bool _newLine = true
|
||||
);
|
||||
|
||||
/// Prints a yul formatted variable declaration statement to the output
|
||||
/// stream.
|
||||
/// Example 1: createVarDecls(0, 1, true) returns {"x_0"} and prints
|
||||
/// let x_0 :=
|
||||
/// Example 2: createVarDecls(0, 2, false) returns {"x_0", "x_1"} and prints
|
||||
/// let x_0, x_1
|
||||
/// @param _start Start index of variable (inclusive)
|
||||
/// @param _end End index of variable (exclusive)
|
||||
/// @param _isAssignment Flag indicating if variable declaration is also
|
||||
/// an assignment. If true, the string " := " follows the variable
|
||||
/// declaration. Otherwise, a new line is follows the variable
|
||||
/// declaration.
|
||||
/// @return A vector of strings containing the variable names used in
|
||||
/// the declaration statement.
|
||||
std::vector<std::string> createVarDecls(unsigned _start, unsigned _end, bool _isAssignment);
|
||||
|
||||
/// Prints comma separated variable names to output stream and
|
||||
/// returns a vector containing the printed variable names.
|
||||
/// Example: createVars(0, 2) returns {"x_0", "x_1"} and prints
|
||||
/// x_0, x_1
|
||||
/// @param _startIdx Start index of variable (inclusive)
|
||||
/// @param _endIdx End index of variable (exclusive)
|
||||
/// @return A vector of strings containing the printed variable names.
|
||||
std::vector<std::string> createVars(unsigned _startIdx, unsigned _endIdx);
|
||||
|
||||
/// Print the yul syntax to make a call to a function named @a _funcName to
|
||||
/// the output stream.
|
||||
/// @param _funcName Name of the function to be called
|
||||
/// @param _numInParams Number of input parameters in function signature
|
||||
/// @param _numOutParams Number of output parameters in function signature
|
||||
void createFunctionCall(std::string _funcName, unsigned _numInParams, unsigned _numOutParams);
|
||||
|
||||
/// Print the yul syntax to pass input arguments to a function that has
|
||||
/// @a _numInParams number of input parameters to the output stream.
|
||||
/// The input arguments are pseudo-randomly chosen from calldata, memory,
|
||||
/// storage, or the yul optimizer hex dictionary.
|
||||
/// @param _numInParams Number of input arguments to fill
|
||||
void fillFunctionCallInput(unsigned _numInParams);
|
||||
|
||||
/// Print the yul syntax to save values returned by a function call
|
||||
/// to the output stream. The values are either stored to memory or
|
||||
/// storage based on a simulated coin flip. The saved location is
|
||||
/// decided pseudo-randomly.
|
||||
/// @param _varsVec A vector of strings that reference variables
|
||||
/// holding the return values of a function call.
|
||||
void saveFunctionCallOutput(std::vector<std::string> const& _varsVec);
|
||||
|
||||
/// Register a function declaration
|
||||
/// @param _f Pointer to a FunctionDef object
|
||||
void registerFunction(FunctionDef const* _f);
|
||||
|
||||
/// Removes entry from m_functionMap and m_functionName
|
||||
void updateFunctionMaps(std::string const& _x);
|
||||
|
||||
/// Returns a pseudo-random dictionary token.
|
||||
/// @param _p Enum that decides if the returned token is hex prefixed ("0x") or not
|
||||
/// @return Dictionary token at the index computed using a
|
||||
@ -144,33 +248,46 @@ private:
|
||||
return m_counter++;
|
||||
}
|
||||
|
||||
/// Generate function name of the form "foo_<typeSuffix>_<counter>".
|
||||
/// @param _type Type classified according to the number of
|
||||
/// values returned by function.
|
||||
std::string functionName(NumFunctionReturns _type)
|
||||
{
|
||||
return "foo_" + functionTypeToString(_type) + "_" + std::to_string(counter());
|
||||
}
|
||||
|
||||
std::ostringstream m_output;
|
||||
// Number of live variables in inner scope of a function
|
||||
std::stack<unsigned> m_numVarsPerScope;
|
||||
// Number of live variables in function scope
|
||||
unsigned m_numLiveVars;
|
||||
/// Variables in current scope
|
||||
std::stack<std::vector<std::string>> m_scopeVars;
|
||||
/// Functions in current scope
|
||||
std::stack<std::vector<std::string>> m_scopeFuncs;
|
||||
/// Variables
|
||||
std::vector<std::string> m_variables;
|
||||
/// Functions
|
||||
std::vector<std::string> m_functions;
|
||||
/// Maps FunctionDef object to its name
|
||||
std::map<FunctionDef const*, std::string> m_functionDefMap;
|
||||
// Set that is used for deduplicating switch case literals
|
||||
std::stack<std::set<dev::u256>> m_switchLiteralSetPerScope;
|
||||
// Total number of function sets. A function set contains one function of each type defined by
|
||||
// NumFunctionReturns
|
||||
unsigned m_numFunctionSets;
|
||||
// Look-up table per function type that holds the number of input (output) function parameters
|
||||
std::vector<unsigned> m_functionVecNoReturnValue;
|
||||
std::vector<unsigned> m_functionVecSingleReturnValue;
|
||||
std::vector<std::pair<unsigned, unsigned>> m_functionVecMultiReturnValue;
|
||||
std::map<std::string, std::pair<unsigned, unsigned>> m_functionSigMap;
|
||||
// mod input/output parameters impose an upper bound on the number of input/output parameters a function may have.
|
||||
static unsigned constexpr modInputParams = 5;
|
||||
static unsigned constexpr modOutputParams = 5;
|
||||
// predicate to keep track of for body scope
|
||||
static unsigned constexpr s_modInputParams = 5;
|
||||
static unsigned constexpr s_modOutputParams = 5;
|
||||
/// Predicate to keep track of for body scope. If true, break/continue
|
||||
/// statements can not be created.
|
||||
bool m_inForBodyScope;
|
||||
// Index used for naming loop variable of bounded for loops
|
||||
unsigned m_numNestedForLoops;
|
||||
// predicate to keep track of for loop init scope
|
||||
/// Predicate to keep track of for loop init scope. If true, variable
|
||||
/// or function declarations can not be created.
|
||||
bool m_inForInitScope;
|
||||
/// Monotonically increasing counter
|
||||
unsigned m_counter;
|
||||
/// Size of protobuf input
|
||||
unsigned m_inputSize;
|
||||
/// Predicate that is true if inside function definition, false otherwise
|
||||
bool m_inFunctionDef;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -21,54 +21,55 @@ message VarDecl {
|
||||
required Expression expr = 1;
|
||||
}
|
||||
|
||||
message FunctionCallNoReturnVal {
|
||||
// Indexes a function that does not return anything
|
||||
required uint32 func_index = 1;
|
||||
required Expression in_param1 = 2;
|
||||
required Expression in_param2 = 3;
|
||||
required Expression in_param3 = 4;
|
||||
required Expression in_param4 = 5;
|
||||
message LowLevelCall {
|
||||
enum Type {
|
||||
CALL = 0;
|
||||
CALLCODE = 1;
|
||||
DELEGATECALL = 2;
|
||||
STATICCALL = 3;
|
||||
}
|
||||
required Type callty = 1;
|
||||
required Expression gas = 2;
|
||||
required Expression addr = 3;
|
||||
// Valid for call and callcode only
|
||||
required Expression wei = 4;
|
||||
required Expression in = 5;
|
||||
required Expression insize = 6;
|
||||
required Expression out = 7;
|
||||
required Expression outsize = 8;
|
||||
}
|
||||
|
||||
// Used by Expression
|
||||
message FunctionCallSingleReturnVal {
|
||||
// Indexes a function that returns exactly one value
|
||||
required uint32 func_index = 1;
|
||||
required Expression in_param1 = 2;
|
||||
required Expression in_param2 = 3;
|
||||
required Expression in_param3 = 4;
|
||||
required Expression in_param4 = 5;
|
||||
message Create {
|
||||
enum Type {
|
||||
CREATE = 0;
|
||||
CREATE2 = 1;
|
||||
}
|
||||
required Type createty = 1;
|
||||
required Expression wei = 2;
|
||||
required Expression position = 3;
|
||||
required Expression size = 4;
|
||||
// Valid for create2 only
|
||||
required Expression value = 5;
|
||||
}
|
||||
|
||||
message MultiVarDecl {
|
||||
// Indexes a function that returns more than one value
|
||||
required uint32 func_index = 1;
|
||||
required Expression in_param1 = 2;
|
||||
required Expression in_param2 = 3;
|
||||
required Expression in_param3 = 4;
|
||||
required Expression in_param4 = 5;
|
||||
}
|
||||
|
||||
message MultiAssignment {
|
||||
// Indexes a function that returns more than one value
|
||||
required uint32 func_index = 1;
|
||||
required Expression in_param1 = 2;
|
||||
required Expression in_param2 = 3;
|
||||
required Expression in_param3 = 4;
|
||||
required Expression in_param4 = 5;
|
||||
required VarRef out_param1 = 6;
|
||||
required VarRef out_param2 = 7;
|
||||
required VarRef out_param3 = 8;
|
||||
required VarRef out_param4 = 9;
|
||||
}
|
||||
|
||||
// We exclude function calls with single return value here and use them as expressions
|
||||
message FunctionCall {
|
||||
oneof functioncall_oneof {
|
||||
FunctionCallNoReturnVal call_zero = 1;
|
||||
MultiVarDecl call_multidecl = 2;
|
||||
MultiAssignment call_multiassign = 3;
|
||||
enum Returns {
|
||||
ZERO = 1;
|
||||
SINGLE = 2;
|
||||
MULTIDECL = 3;
|
||||
MULTIASSIGN = 4;
|
||||
}
|
||||
required Returns ret = 1;
|
||||
// Indexes an existing function
|
||||
required uint32 func_index = 2;
|
||||
required Expression in_param1 = 3;
|
||||
required Expression in_param2 = 4;
|
||||
required Expression in_param3 = 5;
|
||||
required Expression in_param4 = 6;
|
||||
required VarRef out_param1 = 7;
|
||||
required VarRef out_param2 = 8;
|
||||
required VarRef out_param3 = 9;
|
||||
required VarRef out_param4 = 10;
|
||||
}
|
||||
|
||||
message TypedVarDecl {
|
||||
@ -159,6 +160,8 @@ message UnaryOp {
|
||||
CALLDATALOAD = 4;
|
||||
EXTCODESIZE = 5;
|
||||
EXTCODEHASH = 6;
|
||||
BALANCE = 7;
|
||||
BLOCKHASH = 8;
|
||||
}
|
||||
required UOp op = 1;
|
||||
required Expression operand = 2;
|
||||
@ -202,6 +205,16 @@ message NullaryOp {
|
||||
CALLDATASIZE = 4;
|
||||
CODESIZE = 5;
|
||||
RETURNDATASIZE = 6;
|
||||
ADDRESS = 7;
|
||||
ORIGIN = 8;
|
||||
CALLER = 9;
|
||||
CALLVALUE = 10;
|
||||
GASPRICE = 11;
|
||||
COINBASE = 12;
|
||||
TIMESTAMP = 13;
|
||||
NUMBER = 14;
|
||||
DIFFICULTY = 15;
|
||||
GASLIMIT = 16;
|
||||
}
|
||||
required NOp op = 1;
|
||||
}
|
||||
@ -242,7 +255,9 @@ message Expression {
|
||||
UnaryOp unop = 4;
|
||||
TernaryOp top = 5;
|
||||
NullaryOp nop = 6;
|
||||
FunctionCallSingleReturnVal func_expr = 7;
|
||||
FunctionCall func_expr = 7;
|
||||
LowLevelCall lowcall = 8;
|
||||
Create create = 9;
|
||||
}
|
||||
}
|
||||
|
||||
@ -311,10 +326,16 @@ message TerminatingStmt {
|
||||
}
|
||||
}
|
||||
|
||||
// Stub for a VarDecl without an Expression on the RHS
|
||||
message EmptyVarDecl {}
|
||||
message FunctionDef {
|
||||
required uint32 num_input_params = 1;
|
||||
required uint32 num_output_params = 2;
|
||||
required Block block = 3;
|
||||
}
|
||||
|
||||
message PopStmt {
|
||||
required Expression expr = 1;
|
||||
}
|
||||
|
||||
// TODO: Make Function definition a Statement
|
||||
message Statement {
|
||||
oneof stmt_oneof {
|
||||
VarDecl decl = 1;
|
||||
@ -332,6 +353,8 @@ message Statement {
|
||||
TerminatingStmt terminatestmt = 13;
|
||||
FunctionCall functioncall = 14;
|
||||
BoundedForStmt boundedforstmt = 15;
|
||||
FunctionDef funcdef = 16;
|
||||
PopStmt pop = 17;
|
||||
}
|
||||
}
|
||||
|
||||
@ -339,39 +362,8 @@ message Block {
|
||||
repeated Statement statements = 1;
|
||||
}
|
||||
|
||||
// Identical to Block with the addition of an empty var right at the top
|
||||
// Used by FunctionDefinitionNoReturnVal only.
|
||||
message SpecialBlock {
|
||||
required EmptyVarDecl var = 1;
|
||||
repeated Statement statements = 2;
|
||||
}
|
||||
|
||||
// This ensures that proto mutator generates at least one of each type if it creates at least 1 functiondef message.
|
||||
message FunctionDefinition {
|
||||
required FunctionDefinitionNoReturnVal fd_zero = 1;
|
||||
required FunctionDefinitionSingleReturnVal fd_one = 2;
|
||||
required FunctionDefinitionMultiReturnVal fd_multi = 3;
|
||||
}
|
||||
|
||||
// Since this function can have 0 parameters, we hoist an empty var decl at the top via SpecialBlock.
|
||||
message FunctionDefinitionNoReturnVal {
|
||||
required uint32 num_input_params = 1;
|
||||
required SpecialBlock statements = 2;
|
||||
}
|
||||
|
||||
message FunctionDefinitionSingleReturnVal {
|
||||
required uint32 num_input_params = 1;
|
||||
required Block statements = 2;
|
||||
}
|
||||
|
||||
message FunctionDefinitionMultiReturnVal {
|
||||
required uint32 num_input_params = 1;
|
||||
required uint32 num_output_params = 2;
|
||||
required Block statements = 3;
|
||||
}
|
||||
|
||||
message Program {
|
||||
repeated FunctionDefinition funcs = 1;
|
||||
required Block block = 1;
|
||||
}
|
||||
|
||||
package yul.test.yul_fuzzer;
|
||||
|
@ -250,8 +250,7 @@ u256 EVMInstructionInterpreter::eval(
|
||||
case Instruction::MLOAD:
|
||||
if (accessMemory(arg[0], 0x20))
|
||||
return readMemoryWord(arg[0]);
|
||||
else
|
||||
return 0x1234 + arg[0];
|
||||
return 0;
|
||||
case Instruction::MSTORE:
|
||||
if (accessMemory(arg[0], 0x20))
|
||||
writeMemoryWord(arg[0], arg[1]);
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
#include <libyul/optimiser/BlockFlattener.h>
|
||||
#include <libyul/optimiser/Disambiguator.h>
|
||||
#include <libyul/optimiser/CallGraphGenerator.h>
|
||||
#include <libyul/optimiser/CommonSubexpressionEliminator.h>
|
||||
#include <libyul/optimiser/ControlFlowSimplifier.h>
|
||||
#include <libyul/optimiser/NameCollector.h>
|
||||
@ -54,8 +55,10 @@
|
||||
#include <libyul/optimiser/SSATransform.h>
|
||||
#include <libyul/optimiser/StackCompressor.h>
|
||||
#include <libyul/optimiser/StructuralSimplifier.h>
|
||||
#include <libyul/optimiser/Semantics.h>
|
||||
#include <libyul/optimiser/VarDeclInitializer.h>
|
||||
#include <libyul/optimiser/VarNameCleaner.h>
|
||||
#include <libyul/optimiser/LoadResolver.h>
|
||||
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
|
||||
@ -133,7 +136,7 @@ public:
|
||||
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 << " co(n)trol flow simplifier/stack com(p)ressor/(D)ead code eliminator/? " << endl;
|
||||
cout << " co(n)trol flow simplifier/stack com(p)ressor/(D)ead code eliminator/(L)oad resolver/? " << endl;
|
||||
cout.flush();
|
||||
int option = readStandardInputChar();
|
||||
cout << ' ' << char(option) << endl;
|
||||
@ -151,7 +154,7 @@ public:
|
||||
ForLoopConditionIntoBody{}(*m_ast);
|
||||
break;
|
||||
case 'c':
|
||||
(CommonSubexpressionEliminator{m_dialect})(*m_ast);
|
||||
CommonSubexpressionEliminator::run(m_dialect, *m_ast);
|
||||
break;
|
||||
case 'd':
|
||||
(VarDeclInitializer{})(*m_ast);
|
||||
@ -187,7 +190,7 @@ public:
|
||||
(ControlFlowSimplifier{m_dialect})(*m_ast);
|
||||
break;
|
||||
case 'u':
|
||||
UnusedPruner::runUntilStabilised(m_dialect, *m_ast);
|
||||
UnusedPruner::runUntilStabilisedOnFullAST(m_dialect, *m_ast);
|
||||
break;
|
||||
case 'D':
|
||||
DeadCodeEliminator{m_dialect}(*m_ast);
|
||||
@ -214,6 +217,9 @@ public:
|
||||
StackCompressor::run(m_dialect, obj, true, 16);
|
||||
break;
|
||||
}
|
||||
case 'L':
|
||||
LoadResolver::run(m_dialect, *m_ast);
|
||||
break;
|
||||
default:
|
||||
cout << "Unknown option." << endl;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user