mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Split MovableChecker and introduce SideEffectsUpToMSize.
This commit is contained in:
parent
b95eebee1c
commit
afe887adc1
@ -213,20 +213,15 @@ bool SemanticInformation::sideEffectFree(Instruction _instruction)
|
|||||||
// These are not really functional.
|
// These are not really functional.
|
||||||
assertThrow(!isDupInstruction(_instruction) && !isSwapInstruction(_instruction), AssemblyException, "");
|
assertThrow(!isDupInstruction(_instruction) && !isSwapInstruction(_instruction), AssemblyException, "");
|
||||||
|
|
||||||
InstructionInfo info = instructionInfo(_instruction);
|
return !instructionInfo(_instruction).sideEffects;
|
||||||
switch (_instruction)
|
}
|
||||||
{
|
|
||||||
// All the instructions that merely read memory are fine
|
bool SemanticInformation::sideEffectFreeIfNoMSize(Instruction _instruction)
|
||||||
// even though they are marked "sideEffects" in Instructions.cpp
|
{
|
||||||
case Instruction::KECCAK256:
|
if (_instruction == Instruction::KECCAK256 || _instruction == Instruction::MLOAD)
|
||||||
case Instruction::MLOAD:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (info.sideEffects)
|
|
||||||
return false;
|
|
||||||
return true;
|
return true;
|
||||||
|
else
|
||||||
|
return sideEffectFree(_instruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SemanticInformation::invalidatesMemory(Instruction _instruction)
|
bool SemanticInformation::invalidatesMemory(Instruction _instruction)
|
||||||
|
@ -60,6 +60,12 @@ struct SemanticInformation
|
|||||||
/// This does not mean that it has to be deterministic or retrieve information from
|
/// This does not mean that it has to be deterministic or retrieve information from
|
||||||
/// somewhere else than purely the values of its arguments.
|
/// somewhere else than purely the values of its arguments.
|
||||||
static bool sideEffectFree(Instruction _instruction);
|
static bool sideEffectFree(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.
|
||||||
|
/// If true, the instruction is still allowed to influence the value returned by the
|
||||||
|
/// msize instruction.
|
||||||
|
static bool sideEffectFreeIfNoMSize(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).
|
||||||
|
@ -51,6 +51,11 @@ struct BuiltinFunction
|
|||||||
bool movable = false;
|
bool movable = false;
|
||||||
/// If true, a call to this function can be omitted without changing semantics.
|
/// If true, a call to this function can be omitted without changing semantics.
|
||||||
bool sideEffectFree = false;
|
bool sideEffectFree = false;
|
||||||
|
/// If true, a call to this function can be omitted without changing semantics if the
|
||||||
|
/// program does not contain the msize instruction.
|
||||||
|
bool sideEffectFreeIfNoMSize = false;
|
||||||
|
/// If true, this is the msize instruction.
|
||||||
|
bool isMSize = 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;
|
||||||
};
|
};
|
||||||
|
@ -52,6 +52,8 @@ pair<YulString, BuiltinFunctionForEVM> createEVMFunction(
|
|||||||
f.returns.resize(info.ret);
|
f.returns.resize(info.ret);
|
||||||
f.movable = eth::SemanticInformation::movable(_instruction);
|
f.movable = eth::SemanticInformation::movable(_instruction);
|
||||||
f.sideEffectFree = eth::SemanticInformation::sideEffectFree(_instruction);
|
f.sideEffectFree = eth::SemanticInformation::sideEffectFree(_instruction);
|
||||||
|
f.sideEffectFreeIfNoMSize = eth::SemanticInformation::sideEffectFreeIfNoMSize(_instruction);
|
||||||
|
f.isMSize = _instruction == dev::eth::Instruction::MSIZE;
|
||||||
f.literalArguments = false;
|
f.literalArguments = false;
|
||||||
f.instruction = _instruction;
|
f.instruction = _instruction;
|
||||||
f.generateCode = [_instruction](
|
f.generateCode = [_instruction](
|
||||||
@ -73,6 +75,7 @@ pair<YulString, BuiltinFunctionForEVM> createFunction(
|
|||||||
size_t _returns,
|
size_t _returns,
|
||||||
bool _movable,
|
bool _movable,
|
||||||
bool _sideEffectFree,
|
bool _sideEffectFree,
|
||||||
|
bool _sideEffectFreeIfNoMSize,
|
||||||
bool _literalArguments,
|
bool _literalArguments,
|
||||||
std::function<void(FunctionCall const&, AbstractAssembly&, BuiltinContext&, std::function<void()>)> _generateCode
|
std::function<void(FunctionCall const&, AbstractAssembly&, BuiltinContext&, std::function<void()>)> _generateCode
|
||||||
)
|
)
|
||||||
@ -85,6 +88,8 @@ pair<YulString, BuiltinFunctionForEVM> createFunction(
|
|||||||
f.movable = _movable;
|
f.movable = _movable;
|
||||||
f.literalArguments = _literalArguments;
|
f.literalArguments = _literalArguments;
|
||||||
f.sideEffectFree = _sideEffectFree;
|
f.sideEffectFree = _sideEffectFree;
|
||||||
|
f.sideEffectFreeIfNoMSize = _sideEffectFreeIfNoMSize;
|
||||||
|
f.isMSize = false;
|
||||||
f.instruction = {};
|
f.instruction = {};
|
||||||
f.generateCode = std::move(_generateCode);
|
f.generateCode = std::move(_generateCode);
|
||||||
return {name, f};
|
return {name, f};
|
||||||
@ -105,7 +110,7 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
|
|||||||
|
|
||||||
if (_objectAccess)
|
if (_objectAccess)
|
||||||
{
|
{
|
||||||
builtins.emplace(createFunction("datasize", 1, 1, true, true, true, [](
|
builtins.emplace(createFunction("datasize", 1, 1, true, true, true, true, [](
|
||||||
FunctionCall const& _call,
|
FunctionCall const& _call,
|
||||||
AbstractAssembly& _assembly,
|
AbstractAssembly& _assembly,
|
||||||
BuiltinContext& _context,
|
BuiltinContext& _context,
|
||||||
@ -126,7 +131,7 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
|
|||||||
_assembly.appendDataSize(_context.subIDs.at(dataName));
|
_assembly.appendDataSize(_context.subIDs.at(dataName));
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
builtins.emplace(createFunction("dataoffset", 1, 1, true, true, true, [](
|
builtins.emplace(createFunction("dataoffset", 1, 1, true, true, true, true, [](
|
||||||
FunctionCall const& _call,
|
FunctionCall const& _call,
|
||||||
AbstractAssembly& _assembly,
|
AbstractAssembly& _assembly,
|
||||||
BuiltinContext& _context,
|
BuiltinContext& _context,
|
||||||
@ -147,7 +152,7 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
|
|||||||
_assembly.appendDataOffset(_context.subIDs.at(dataName));
|
_assembly.appendDataOffset(_context.subIDs.at(dataName));
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
builtins.emplace(createFunction("datacopy", 3, 0, false, false, false, [](
|
builtins.emplace(createFunction("datacopy", 3, 0, false, false, false, false, [](
|
||||||
FunctionCall const&,
|
FunctionCall const&,
|
||||||
AbstractAssembly& _assembly,
|
AbstractAssembly& _assembly,
|
||||||
BuiltinContext&,
|
BuiltinContext&,
|
||||||
|
@ -72,5 +72,7 @@ void WasmDialect::addFunction(string _name, size_t _params, size_t _returns)
|
|||||||
f.returns.resize(_returns);
|
f.returns.resize(_returns);
|
||||||
f.movable = false;
|
f.movable = false;
|
||||||
f.sideEffectFree = false;
|
f.sideEffectFree = false;
|
||||||
|
f.sideEffectFreeIfNoMSize = false;
|
||||||
|
f.isMSize = false;
|
||||||
f.literalArguments = false;
|
f.literalArguments = false;
|
||||||
}
|
}
|
||||||
|
@ -33,9 +33,59 @@ using namespace std;
|
|||||||
using namespace dev;
|
using namespace dev;
|
||||||
using namespace yul;
|
using namespace yul;
|
||||||
|
|
||||||
MovableChecker::MovableChecker(Dialect const& _dialect):
|
SideEffectsCollector::SideEffectsCollector(Dialect const& _dialect, Expression const& _expression):
|
||||||
m_dialect(_dialect)
|
SideEffectsCollector(_dialect)
|
||||||
{
|
{
|
||||||
|
visit(_expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
SideEffectsCollector::SideEffectsCollector(Dialect const& _dialect, Statement const& _statement):
|
||||||
|
SideEffectsCollector(_dialect)
|
||||||
|
{
|
||||||
|
visit(_statement);
|
||||||
|
}
|
||||||
|
|
||||||
|
SideEffectsCollector::SideEffectsCollector(Dialect const& _dialect, Block const& _ast):
|
||||||
|
SideEffectsCollector(_dialect)
|
||||||
|
{
|
||||||
|
operator()(_ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SideEffectsCollector::operator()(FunctionalInstruction const& _instr)
|
||||||
|
{
|
||||||
|
ASTWalker::operator()(_instr);
|
||||||
|
|
||||||
|
if (!eth::SemanticInformation::movable(_instr.instruction))
|
||||||
|
m_movable = false;
|
||||||
|
if (!eth::SemanticInformation::sideEffectFree(_instr.instruction))
|
||||||
|
m_sideEffectFree = false;
|
||||||
|
if (!eth::SemanticInformation::sideEffectFreeIfNoMSize(_instr.instruction))
|
||||||
|
m_sideEffectFreeIfNoMSize = false;
|
||||||
|
if (_instr.instruction == eth::Instruction::MSIZE)
|
||||||
|
m_containsMSize = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SideEffectsCollector::operator()(FunctionCall const& _functionCall)
|
||||||
|
{
|
||||||
|
ASTWalker::operator()(_functionCall);
|
||||||
|
|
||||||
|
if (BuiltinFunction const* f = m_dialect.builtin(_functionCall.functionName.name))
|
||||||
|
{
|
||||||
|
if (!f->movable)
|
||||||
|
m_movable = false;
|
||||||
|
if (!f->sideEffectFree)
|
||||||
|
m_sideEffectFree = false;
|
||||||
|
if (!f->sideEffectFreeIfNoMSize)
|
||||||
|
m_sideEffectFreeIfNoMSize = false;
|
||||||
|
if (f->isMSize)
|
||||||
|
m_containsMSize = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_movable = false;
|
||||||
|
m_sideEffectFree = false;
|
||||||
|
m_sideEffectFreeIfNoMSize = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MovableChecker::MovableChecker(Dialect const& _dialect, Expression const& _expression):
|
MovableChecker::MovableChecker(Dialect const& _dialect, Expression const& _expression):
|
||||||
@ -46,38 +96,10 @@ MovableChecker::MovableChecker(Dialect const& _dialect, Expression const& _expre
|
|||||||
|
|
||||||
void MovableChecker::operator()(Identifier const& _identifier)
|
void MovableChecker::operator()(Identifier const& _identifier)
|
||||||
{
|
{
|
||||||
ASTWalker::operator()(_identifier);
|
SideEffectsCollector::operator()(_identifier);
|
||||||
m_variableReferences.emplace(_identifier.name);
|
m_variableReferences.emplace(_identifier.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MovableChecker::operator()(FunctionalInstruction const& _instr)
|
|
||||||
{
|
|
||||||
ASTWalker::operator()(_instr);
|
|
||||||
|
|
||||||
if (!eth::SemanticInformation::movable(_instr.instruction))
|
|
||||||
m_movable = false;
|
|
||||||
if (!eth::SemanticInformation::sideEffectFree(_instr.instruction))
|
|
||||||
m_sideEffectFree = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MovableChecker::operator()(FunctionCall const& _functionCall)
|
|
||||||
{
|
|
||||||
ASTWalker::operator()(_functionCall);
|
|
||||||
|
|
||||||
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&)
|
void MovableChecker::visit(Statement const&)
|
||||||
{
|
{
|
||||||
assertThrow(false, OptimizerException, "Movability for statement requested.");
|
assertThrow(false, OptimizerException, "Movability for statement requested.");
|
||||||
|
@ -29,38 +29,67 @@ namespace yul
|
|||||||
struct Dialect;
|
struct Dialect;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specific AST walker that determines whether an expression is movable.
|
* Specific AST walker that determines side-effect free-ness and movability of code.
|
||||||
|
* Enters into function definitions.
|
||||||
*/
|
*/
|
||||||
class MovableChecker: public ASTWalker
|
class SideEffectsCollector: public ASTWalker
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit MovableChecker(Dialect const& _dialect);
|
explicit SideEffectsCollector(Dialect const& _dialect): m_dialect(_dialect) {}
|
||||||
MovableChecker(Dialect const& _dialect, Expression const& _expression);
|
SideEffectsCollector(Dialect const& _dialect, Expression const& _expression);
|
||||||
|
SideEffectsCollector(Dialect const& _dialect, Statement const& _statement);
|
||||||
|
SideEffectsCollector(Dialect const& _dialect, Block const& _ast);
|
||||||
|
|
||||||
void operator()(Identifier const& _identifier) override;
|
using ASTWalker::operator();
|
||||||
void operator()(FunctionalInstruction const& _functionalInstruction) override;
|
void operator()(FunctionalInstruction const& _functionalInstruction) override;
|
||||||
void operator()(FunctionCall const& _functionCall) override;
|
void operator()(FunctionCall const& _functionCall) override;
|
||||||
|
|
||||||
/// Disallow visiting anything apart from Expressions (this throws).
|
|
||||||
void visit(Statement const&) override;
|
|
||||||
using ASTWalker::visit;
|
|
||||||
|
|
||||||
bool movable() const { return m_movable; }
|
bool movable() const { return m_movable; }
|
||||||
bool sideEffectFree() const { return m_sideEffectFree; }
|
bool sideEffectFree() const { return m_sideEffectFree; }
|
||||||
|
bool sideEffectFreeIfNoMSize() const { return m_sideEffectFreeIfNoMSize; }
|
||||||
std::set<YulString> const& referencedVariables() const { return m_variableReferences; }
|
bool containsMSize() const { return m_containsMSize; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Dialect const& m_dialect;
|
Dialect const& m_dialect;
|
||||||
/// Which variables the current expression references.
|
|
||||||
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
|
/// Is the current expression side-effect free, i.e. can be removed
|
||||||
/// without changing the semantics.
|
/// without changing the semantics.
|
||||||
bool m_sideEffectFree = true;
|
bool m_sideEffectFree = true;
|
||||||
|
/// Is the current expression side-effect free up to msize, i.e. can be removed
|
||||||
|
/// without changing the semantics except for the value returned by the msize instruction.
|
||||||
|
bool m_sideEffectFreeIfNoMSize = true;
|
||||||
|
/// Does the current code contain the MSize operation?
|
||||||
|
/// Note that this is a purely syntactic property meaning that even if this is false,
|
||||||
|
/// the code can still contain calls to functions that contain the msize instruction.
|
||||||
|
bool m_containsMSize = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specific AST walker that determines whether an expression is movable
|
||||||
|
* and collects the referenced variables.
|
||||||
|
* Can only be used on expressions.
|
||||||
|
*/
|
||||||
|
class MovableChecker: public SideEffectsCollector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit MovableChecker(Dialect const& _dialect): SideEffectsCollector(_dialect) {}
|
||||||
|
MovableChecker(Dialect const& _dialect, Expression const& _expression);
|
||||||
|
|
||||||
|
void operator()(Identifier const& _identifier) override;
|
||||||
|
|
||||||
|
/// Disallow visiting anything apart from Expressions (this throws).
|
||||||
|
void visit(Statement const&) override;
|
||||||
|
using ASTWalker::visit;
|
||||||
|
|
||||||
|
std::set<YulString> const& referencedVariables() const { return m_variableReferences; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Which variables the current expression references.
|
||||||
|
std::set<YulString> m_variableReferences;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class to find "irregular" control flow.
|
* Helper class to find "irregular" control flow.
|
||||||
* This includes termination, break and continue.
|
* This includes termination, break and continue.
|
||||||
|
Loading…
Reference in New Issue
Block a user