mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Introduce side-effect-free as relaxed version of movable.
This commit is contained in:
parent
d172b9bf11
commit
e8a88b13e4
@ -208,6 +208,27 @@ bool SemanticInformation::movable(Instruction _instruction)
|
||||
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)
|
||||
{
|
||||
switch (_instruction)
|
||||
|
@ -56,6 +56,10 @@ struct SemanticInformation
|
||||
/// 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.
|
||||
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.
|
||||
static bool invalidatesMemory(Instruction _instruction);
|
||||
/// @returns true if the given instruction modifies storage (even indirectly).
|
||||
|
@ -49,6 +49,8 @@ struct BuiltinFunction
|
||||
/// 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.
|
||||
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.
|
||||
bool literalArguments = false;
|
||||
};
|
||||
|
@ -42,7 +42,7 @@ EVMDialect::EVMDialect(AsmFlavour _flavour, bool _objectAccess, langutil::EVMVer
|
||||
if (!m_objectAccess)
|
||||
return;
|
||||
|
||||
addFunction("datasize", 1, 1, true, true, [this](
|
||||
addFunction("datasize", 1, 1, true, true, true, [this](
|
||||
FunctionCall const& _call,
|
||||
AbstractAssembly& _assembly,
|
||||
std::function<void()>
|
||||
@ -59,7 +59,7 @@ EVMDialect::EVMDialect(AsmFlavour _flavour, bool _objectAccess, langutil::EVMVer
|
||||
_assembly.appendDataSize(m_subIDs.at(dataName));
|
||||
}
|
||||
});
|
||||
addFunction("dataoffset", 1, 1, true, true, [this](
|
||||
addFunction("dataoffset", 1, 1, true, true, true, [this](
|
||||
FunctionCall const& _call,
|
||||
AbstractAssembly& _assembly,
|
||||
std::function<void()>
|
||||
@ -76,7 +76,7 @@ EVMDialect::EVMDialect(AsmFlavour _flavour, bool _objectAccess, langutil::EVMVer
|
||||
_assembly.appendDataOffset(m_subIDs.at(dataName));
|
||||
}
|
||||
});
|
||||
addFunction("datacopy", 3, 0, false, false, [](
|
||||
addFunction("datacopy", 3, 0, false, false, false, [](
|
||||
FunctionCall const&,
|
||||
AbstractAssembly& _assembly,
|
||||
std::function<void()> _visitArguments
|
||||
@ -132,6 +132,7 @@ void EVMDialect::addFunction(
|
||||
size_t _params,
|
||||
size_t _returns,
|
||||
bool _movable,
|
||||
bool _sideEffectFree,
|
||||
bool _literalArguments,
|
||||
std::function<void(FunctionCall const&, AbstractAssembly&, std::function<void()>)> _generateCode
|
||||
)
|
||||
@ -142,6 +143,7 @@ void EVMDialect::addFunction(
|
||||
f.parameters.resize(_params);
|
||||
f.returns.resize(_returns);
|
||||
f.movable = _movable;
|
||||
f.sideEffectFree = _sideEffectFree;
|
||||
f.literalArguments = _literalArguments;
|
||||
f.generateCode = std::move(_generateCode);
|
||||
}
|
||||
|
@ -75,6 +75,7 @@ protected:
|
||||
size_t _params,
|
||||
size_t _returns,
|
||||
bool _movable,
|
||||
bool _sideEffectFree,
|
||||
bool _literalArguments,
|
||||
std::function<void(FunctionCall const&, AbstractAssembly&, std::function<void()>)> _generateCode
|
||||
);
|
||||
|
@ -71,5 +71,6 @@ void WasmDialect::addFunction(string _name, size_t _params, size_t _returns)
|
||||
f.parameters.resize(_params);
|
||||
f.returns.resize(_returns);
|
||||
f.movable = false;
|
||||
f.sideEffectFree = false;
|
||||
f.literalArguments = false;
|
||||
}
|
||||
|
@ -51,21 +51,30 @@ void MovableChecker::operator()(Identifier const& _identifier)
|
||||
|
||||
void MovableChecker::operator()(FunctionalInstruction const& _instr)
|
||||
{
|
||||
ASTWalker::operator()(_instr);
|
||||
|
||||
if (!eth::SemanticInformation::movable(_instr.instruction))
|
||||
m_movable = false;
|
||||
else
|
||||
ASTWalker::operator()(_instr);
|
||||
if (!eth::SemanticInformation::sideEffectFree(_instr.instruction))
|
||||
m_sideEffectFree = false;
|
||||
}
|
||||
|
||||
void MovableChecker::operator()(FunctionCall const& _functionCall)
|
||||
{
|
||||
if (BuiltinFunction const* f = m_dialect.builtin(_functionCall.functionName.name))
|
||||
if (f->movable)
|
||||
{
|
||||
ASTWalker::operator()(_functionCall);
|
||||
return;
|
||||
}
|
||||
|
||||
if (BuiltinFunction const* f = m_dialect.builtin(_functionCall.functionName.name))
|
||||
{
|
||||
if (!f->movable)
|
||||
m_movable = false;
|
||||
if (!f->sideEffectFree)
|
||||
m_sideEffectFree = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_movable = false;
|
||||
m_sideEffectFree = false;
|
||||
}
|
||||
}
|
||||
|
||||
void MovableChecker::visit(Statement const&)
|
||||
|
@ -46,6 +46,8 @@ public:
|
||||
using ASTWalker::visit;
|
||||
|
||||
bool movable() const { return m_movable; }
|
||||
bool sideEffectFree() const { return m_sideEffectFree; }
|
||||
|
||||
std::set<YulString> const& referencedVariables() const { return m_variableReferences; }
|
||||
|
||||
private:
|
||||
@ -54,6 +56,9 @@ private:
|
||||
std::set<YulString> m_variableReferences;
|
||||
/// Is the current expression movable or not.
|
||||
bool m_movable = true;
|
||||
/// Is the current expression side-effect free, i.e. can be removed
|
||||
/// without changing the semantics.
|
||||
bool m_sideEffectFree = true;
|
||||
};
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user