Merge pull request #6746 from ethereum/splitMovable

Split movable property into movable and side-effect-free
This commit is contained in:
chriseth 2019-05-16 13:25:15 +02:00 committed by GitHub
commit 38d92a1163
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 66 additions and 47 deletions

View File

@ -208,6 +208,27 @@ bool SemanticInformation::movable(Instruction _instruction)
return true; return true;
} }
bool SemanticInformation::sideEffectFree(Instruction _instruction)
{
// These are not really functional.
assertThrow(!isDupInstruction(_instruction) && !isSwapInstruction(_instruction), AssemblyException, "");
InstructionInfo info = instructionInfo(_instruction);
switch (_instruction)
{
// All the instructions that merely read memory are fine
// even though they are marked "sideEffects" in Instructions.cpp
case Instruction::KECCAK256:
case Instruction::MLOAD:
return true;
default:
break;
}
if (info.sideEffects)
return false;
return true;
}
bool SemanticInformation::invalidatesMemory(Instruction _instruction) bool SemanticInformation::invalidatesMemory(Instruction _instruction)
{ {
switch (_instruction) switch (_instruction)

View File

@ -56,6 +56,10 @@ struct SemanticInformation
/// without altering the semantics. This means it cannot depend on storage or memory, /// without altering the semantics. This means it cannot depend on storage or memory,
/// cannot have any side-effects, but it can depend on a call-constant state of the blockchain. /// cannot have any side-effects, but it can depend on a call-constant state of the blockchain.
static bool movable(Instruction _instruction); static bool movable(Instruction _instruction);
/// @returns true if the instruction can be removed without changing the semantics.
/// This does not mean that it has to be deterministic or retrieve information from
/// somewhere else than purely the values of its arguments.
static bool sideEffectFree(Instruction _instruction);
/// @returns true if the given instruction modifies memory. /// @returns true if the given instruction modifies memory.
static bool invalidatesMemory(Instruction _instruction); static bool invalidatesMemory(Instruction _instruction);
/// @returns true if the given instruction modifies storage (even indirectly). /// @returns true if the given instruction modifies storage (even indirectly).

View File

@ -49,6 +49,8 @@ struct BuiltinFunction
/// This means the function cannot depend on storage or memory, cannot have any side-effects, /// This means the function cannot depend on storage or memory, cannot have any side-effects,
/// but it can depend on state that is constant across an EVM-call. /// but it can depend on state that is constant across an EVM-call.
bool movable = false; bool movable = false;
/// If true, a call to this function can be omitted without changing semantics.
bool sideEffectFree = false;
/// If true, can only accept literals as arguments and they cannot be moved to variables. /// If true, can only accept literals as arguments and they cannot be moved to variables.
bool literalArguments = false; bool literalArguments = false;
}; };

View File

@ -42,7 +42,7 @@ EVMDialect::EVMDialect(AsmFlavour _flavour, bool _objectAccess, langutil::EVMVer
if (!m_objectAccess) if (!m_objectAccess)
return; return;
addFunction("datasize", 1, 1, true, true, [this]( addFunction("datasize", 1, 1, true, true, true, [this](
FunctionCall const& _call, FunctionCall const& _call,
AbstractAssembly& _assembly, AbstractAssembly& _assembly,
std::function<void()> std::function<void()>
@ -59,7 +59,7 @@ EVMDialect::EVMDialect(AsmFlavour _flavour, bool _objectAccess, langutil::EVMVer
_assembly.appendDataSize(m_subIDs.at(dataName)); _assembly.appendDataSize(m_subIDs.at(dataName));
} }
}); });
addFunction("dataoffset", 1, 1, true, true, [this]( addFunction("dataoffset", 1, 1, true, true, true, [this](
FunctionCall const& _call, FunctionCall const& _call,
AbstractAssembly& _assembly, AbstractAssembly& _assembly,
std::function<void()> std::function<void()>
@ -76,7 +76,7 @@ EVMDialect::EVMDialect(AsmFlavour _flavour, bool _objectAccess, langutil::EVMVer
_assembly.appendDataOffset(m_subIDs.at(dataName)); _assembly.appendDataOffset(m_subIDs.at(dataName));
} }
}); });
addFunction("datacopy", 3, 0, false, false, []( addFunction("datacopy", 3, 0, false, false, false, [](
FunctionCall const&, FunctionCall const&,
AbstractAssembly& _assembly, AbstractAssembly& _assembly,
std::function<void()> _visitArguments std::function<void()> _visitArguments
@ -132,6 +132,7 @@ void EVMDialect::addFunction(
size_t _params, size_t _params,
size_t _returns, size_t _returns,
bool _movable, bool _movable,
bool _sideEffectFree,
bool _literalArguments, bool _literalArguments,
std::function<void(FunctionCall const&, AbstractAssembly&, std::function<void()>)> _generateCode std::function<void(FunctionCall const&, AbstractAssembly&, std::function<void()>)> _generateCode
) )
@ -142,6 +143,7 @@ void EVMDialect::addFunction(
f.parameters.resize(_params); f.parameters.resize(_params);
f.returns.resize(_returns); f.returns.resize(_returns);
f.movable = _movable; f.movable = _movable;
f.sideEffectFree = _sideEffectFree;
f.literalArguments = _literalArguments; f.literalArguments = _literalArguments;
f.generateCode = std::move(_generateCode); f.generateCode = std::move(_generateCode);
} }

View File

@ -75,6 +75,7 @@ protected:
size_t _params, size_t _params,
size_t _returns, size_t _returns,
bool _movable, bool _movable,
bool _sideEffectFree,
bool _literalArguments, bool _literalArguments,
std::function<void(FunctionCall const&, AbstractAssembly&, std::function<void()>)> _generateCode std::function<void(FunctionCall const&, AbstractAssembly&, std::function<void()>)> _generateCode
); );

View File

@ -71,5 +71,6 @@ void WasmDialect::addFunction(string _name, size_t _params, size_t _returns)
f.parameters.resize(_params); f.parameters.resize(_params);
f.returns.resize(_returns); f.returns.resize(_returns);
f.movable = false; f.movable = false;
f.sideEffectFree = false;
f.literalArguments = false; f.literalArguments = false;
} }

View File

@ -51,21 +51,30 @@ void MovableChecker::operator()(Identifier const& _identifier)
void MovableChecker::operator()(FunctionalInstruction const& _instr) void MovableChecker::operator()(FunctionalInstruction const& _instr)
{ {
ASTWalker::operator()(_instr);
if (!eth::SemanticInformation::movable(_instr.instruction)) if (!eth::SemanticInformation::movable(_instr.instruction))
m_movable = false; m_movable = false;
else if (!eth::SemanticInformation::sideEffectFree(_instr.instruction))
ASTWalker::operator()(_instr); m_sideEffectFree = false;
} }
void MovableChecker::operator()(FunctionCall const& _functionCall) void MovableChecker::operator()(FunctionCall const& _functionCall)
{
if (BuiltinFunction const* f = m_dialect.builtin(_functionCall.functionName.name))
if (f->movable)
{ {
ASTWalker::operator()(_functionCall); ASTWalker::operator()(_functionCall);
return;
} if (BuiltinFunction const* f = m_dialect.builtin(_functionCall.functionName.name))
{
if (!f->movable)
m_movable = false; m_movable = false;
if (!f->sideEffectFree)
m_sideEffectFree = false;
}
else
{
m_movable = false;
m_sideEffectFree = false;
}
} }
void MovableChecker::visit(Statement const&) void MovableChecker::visit(Statement const&)

View File

@ -46,6 +46,8 @@ public:
using ASTWalker::visit; using ASTWalker::visit;
bool movable() const { return m_movable; } bool movable() const { return m_movable; }
bool sideEffectFree() const { return m_sideEffectFree; }
std::set<YulString> const& referencedVariables() const { return m_variableReferences; } std::set<YulString> const& referencedVariables() const { return m_variableReferences; }
private: private:
@ -54,6 +56,9 @@ private:
std::set<YulString> m_variableReferences; std::set<YulString> m_variableReferences;
/// Is the current expression movable or not. /// Is the current expression movable or not.
bool m_movable = true; bool m_movable = true;
/// Is the current expression side-effect free, i.e. can be removed
/// without changing the semantics.
bool m_sideEffectFree = true;
}; };
/** /**

View File

@ -75,7 +75,7 @@ void UnusedPruner::operator()(Block& _block)
{ {
if (!varDecl.value) if (!varDecl.value)
statement = Block{std::move(varDecl.location), {}}; statement = Block{std::move(varDecl.location), {}};
else if (MovableChecker(m_dialect, *varDecl.value).movable()) else if (MovableChecker(m_dialect, *varDecl.value).sideEffectFree())
{ {
subtractReferences(ReferencesCounter::countReferences(*varDecl.value)); subtractReferences(ReferencesCounter::countReferences(*varDecl.value));
statement = Block{std::move(varDecl.location), {}}; statement = Block{std::move(varDecl.location), {}};
@ -93,9 +93,8 @@ void UnusedPruner::operator()(Block& _block)
else if (statement.type() == typeid(ExpressionStatement)) else if (statement.type() == typeid(ExpressionStatement))
{ {
ExpressionStatement& exprStmt = boost::get<ExpressionStatement>(statement); ExpressionStatement& exprStmt = boost::get<ExpressionStatement>(statement);
if (MovableChecker(m_dialect, exprStmt.expression).movable()) if (MovableChecker(m_dialect, exprStmt.expression).sideEffectFree())
{ {
// pop(x) should be movable!
subtractReferences(ReferencesCounter::countReferences(exprStmt.expression)); subtractReferences(ReferencesCounter::countReferences(exprStmt.expression));
statement = Block{std::move(exprStmt.location), {}}; statement = Block{std::move(exprStmt.location), {}};
} }

View File

@ -32,7 +32,7 @@ struct Dialect;
/** /**
* Optimisation stage that removes unused variables and functions and also * Optimisation stage that removes unused variables and functions and also
* removes movable expression statements. * removes side-effect-free expression statements.
* *
* Note that this does not remove circular references. * Note that this does not remove circular references.
* *

View File

@ -5,8 +5,4 @@
// ==== // ====
// step: fullSimplify // step: fullSimplify
// ---- // ----
// { // { mstore(0, 0) }
// let _1 := 0
// pop(mload(_1))
// mstore(_1, 0)
// }

View File

@ -1,6 +1,6 @@
// div is eliminated, but keccak256 has side-effects. // div is eliminated, but create has side-effects.
{ {
let a := div(keccak256(0, 0), 0) let a := div(create(0, 0, 0), 0)
mstore(0, a) mstore(0, a)
} }
// ==== // ====
@ -8,6 +8,6 @@
// ---- // ----
// { // {
// let _1 := 0 // let _1 := 0
// pop(keccak256(_1, _1)) // pop(create(_1, _1, _1))
// mstore(_1, 0) // mstore(_1, 0)
// } // }

View File

@ -23,7 +23,6 @@
// step: fullSimplify // step: fullSimplify
// ---- // ----
// { // {
// pop(mload(0))
// mstore(1, 0) // mstore(1, 0)
// mstore(2, 0) // mstore(2, 0)
// mstore(3, 0) // mstore(3, 0)

View File

@ -24,8 +24,6 @@
// for { } lt(mload(a), mload(b)) { a := mload(b) } // for { } lt(mload(a), mload(b)) { a := mload(b) }
// { // {
// let b_3 := mload(a) // let b_3 := mload(a)
// pop(mload(b_3))
// pop(mload(b_3))
// let a_6 := mload(b_3) // let a_6 := mload(b_3)
// b := mload(a_6) // b := mload(a_6)
// } // }

View File

@ -10,10 +10,6 @@
// step: ssaAndBack // step: ssaAndBack
// ---- // ----
// { // {
// pop(mload(0))
// pop(mload(1))
// pop(mload(2))
// pop(mload(3))
// let a_5 := mload(4) // let a_5 := mload(4)
// mstore(a_5, 0) // mstore(a_5, 0)
// } // }

View File

@ -13,11 +13,6 @@
// ---- // ----
// { // {
// let a := mload(0) // let a := mload(0)
// if mload(1) // if mload(1) { a := mload(3) }
// {
// pop(mload(1))
// pop(mload(2))
// a := mload(3)
// }
// mstore(a, 0) // mstore(a, 0)
// } // }

View File

@ -19,15 +19,7 @@
// { // {
// let a := mload(0) // let a := mload(0)
// switch mload(1) // switch mload(1)
// case 0 { // case 0 { a := mload(3) }
// pop(mload(1)) // default { a := mload(6) }
// pop(mload(2))
// a := mload(3)
// }
// default {
// pop(mload(4))
// pop(mload(5))
// a := mload(6)
// }
// mstore(a, 0) // mstore(a, 0)
// } // }

View File

@ -7,7 +7,6 @@
// step: ssaAndBack // step: ssaAndBack
// ---- // ----
// { // {
// pop(mload(0))
// let a_2 := mload(1) // let a_2 := mload(1)
// mstore(a_2, 0) // mstore(a_2, 0)
// } // }