mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[Yul] leave statement.
This commit is contained in:
@@ -136,6 +136,11 @@ Statement ASTCopier::operator()(Continue const& _continue)
|
||||
return Continue{ _continue };
|
||||
}
|
||||
|
||||
Statement ASTCopier::operator()(Leave const& _leave)
|
||||
{
|
||||
return Leave{_leave};
|
||||
}
|
||||
|
||||
Statement ASTCopier::operator ()(Block const& _block)
|
||||
{
|
||||
return translate(_block);
|
||||
|
||||
@@ -58,6 +58,7 @@ public:
|
||||
virtual Statement operator()(ForLoop const&) = 0;
|
||||
virtual Statement operator()(Break const&) = 0;
|
||||
virtual Statement operator()(Continue const&) = 0;
|
||||
virtual Statement operator()(Leave const&) = 0;
|
||||
virtual Statement operator()(Block const& _block) = 0;
|
||||
};
|
||||
|
||||
@@ -83,6 +84,7 @@ public:
|
||||
Statement operator()(ForLoop const&) override;
|
||||
Statement operator()(Break const&) override;
|
||||
Statement operator()(Continue const&) override;
|
||||
Statement operator()(Leave const&) override;
|
||||
Statement operator()(Block const& _block) override;
|
||||
|
||||
virtual Expression translate(Expression const& _expression);
|
||||
|
||||
@@ -169,6 +169,10 @@ void ASTModifier::operator()(Continue&)
|
||||
{
|
||||
}
|
||||
|
||||
void ASTModifier::operator()(Leave&)
|
||||
{
|
||||
}
|
||||
|
||||
void ASTModifier::operator()(Block& _block)
|
||||
{
|
||||
walkVector(_block.statements);
|
||||
|
||||
@@ -56,6 +56,7 @@ public:
|
||||
virtual void operator()(ForLoop const&);
|
||||
virtual void operator()(Break const&) {}
|
||||
virtual void operator()(Continue const&) {}
|
||||
virtual void operator()(Leave const&) {}
|
||||
virtual void operator()(Block const& _block);
|
||||
|
||||
virtual void visit(Statement const& _st);
|
||||
@@ -91,6 +92,7 @@ public:
|
||||
virtual void operator()(ForLoop&);
|
||||
virtual void operator()(Break&);
|
||||
virtual void operator()(Continue&);
|
||||
virtual void operator()(Leave&);
|
||||
virtual void operator()(Block& _block);
|
||||
|
||||
virtual void visit(Statement& _st);
|
||||
|
||||
@@ -173,6 +173,11 @@ void BlockHasher::operator()(Continue const& _continue)
|
||||
ASTWalker::operator()(_continue);
|
||||
}
|
||||
|
||||
void BlockHasher::operator()(Leave const& _leave)
|
||||
{
|
||||
hash64(compileTimeLiteralHash("Leave"));
|
||||
ASTWalker::operator()(_leave);
|
||||
}
|
||||
|
||||
void BlockHasher::operator()(Block const& _block)
|
||||
{
|
||||
|
||||
@@ -60,6 +60,7 @@ public:
|
||||
void operator()(ForLoop const&) override;
|
||||
void operator()(Break const&) override;
|
||||
void operator()(Continue const&) override;
|
||||
void operator()(Leave const&) override;
|
||||
void operator()(Block const& _block) override;
|
||||
|
||||
static std::map<Block const*, uint64_t> run(Block const& _block);
|
||||
|
||||
@@ -159,7 +159,10 @@ void ControlFlowSimplifier::visit(Statement& _st)
|
||||
isTerminating = true;
|
||||
--m_numBreakStatements;
|
||||
}
|
||||
else if (controlFlow == TerminationFinder::ControlFlow::Terminate)
|
||||
else if (
|
||||
controlFlow == TerminationFinder::ControlFlow::Terminate ||
|
||||
controlFlow == TerminationFinder::ControlFlow::Leave
|
||||
)
|
||||
isTerminating = true;
|
||||
|
||||
if (isTerminating && m_numContinueStatements == 0 && m_numBreakStatements == 0)
|
||||
|
||||
@@ -161,6 +161,10 @@ void DataFlowAnalyzer::operator()(FunctionDefinition& _fun)
|
||||
}
|
||||
ASTModifier::operator()(_fun);
|
||||
|
||||
// Note that the contents of return variables, storage and memory at this point
|
||||
// might be incorrect due to the fact that the DataFlowAnalyzer ignores the ``leave``
|
||||
// statement.
|
||||
|
||||
popScope();
|
||||
m_value.swap(value);
|
||||
swap(m_references, references);
|
||||
|
||||
@@ -64,6 +64,10 @@ struct SideEffects;
|
||||
* older version of the other and thus overlapping contents would have been deleted already
|
||||
* at the point of assignment.
|
||||
*
|
||||
* The DataFlowAnalyzer currently does not deal with the ``leave`` statement. This is because
|
||||
* it only matters at the end of a function body, which is a point in the code a derived class
|
||||
* can not easily deal with.
|
||||
*
|
||||
* Prerequisite: Disambiguator, ForLoopInitRewriter.
|
||||
*/
|
||||
class DataFlowAnalyzer: public ASTModifier
|
||||
|
||||
@@ -35,7 +35,7 @@ struct OptimiserStepContext;
|
||||
* Optimisation stage that removes unreachable code
|
||||
*
|
||||
* Unreachable code is any code within a block which is preceded by a
|
||||
* return, invalid, break, continue, selfdestruct or revert.
|
||||
* leave, return, invalid, break, continue, selfdestruct or revert.
|
||||
*
|
||||
* Function definitions are retained as they might be called by earlier
|
||||
* code and thus are considered reachable.
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <libyul/optimiser/OptimizerUtilities.h>
|
||||
#include <libyul/optimiser/Metrics.h>
|
||||
#include <libyul/optimiser/SSAValueTracker.h>
|
||||
#include <libyul/optimiser/Semantics.h>
|
||||
#include <libyul/Exceptions.h>
|
||||
#include <libyul/AsmData.h>
|
||||
|
||||
@@ -62,6 +63,8 @@ FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser):
|
||||
continue;
|
||||
FunctionDefinition& fun = boost::get<FunctionDefinition>(statement);
|
||||
m_functions[fun.name] = &fun;
|
||||
if (LeaveFinder::containsLeave(fun))
|
||||
m_noInlineFunctions.insert(fun.name);
|
||||
// Always inline functions that are only called once.
|
||||
if (references[fun.name] == 1)
|
||||
m_singleUse.emplace(fun.name);
|
||||
@@ -94,7 +97,7 @@ bool FullInliner::shallInline(FunctionCall const& _funCall, YulString _callSite)
|
||||
if (!calledFunction)
|
||||
return false;
|
||||
|
||||
if (recursive(*calledFunction))
|
||||
if (m_noInlineFunctions.count(_funCall.functionName.name) || recursive(*calledFunction))
|
||||
return false;
|
||||
|
||||
// Inline really, really tiny functions
|
||||
|
||||
@@ -102,6 +102,8 @@ private:
|
||||
/// we store pointers to functions.
|
||||
Block& m_ast;
|
||||
std::map<YulString, FunctionDefinition*> m_functions;
|
||||
/// Functions not to be inlined (because they contain the ``leave`` statement).
|
||||
std::set<YulString> m_noInlineFunctions;
|
||||
/// Names of functions to always inline.
|
||||
std::set<YulString> m_singleUse;
|
||||
/// Variables that are constants (used for inlining heuristic)
|
||||
|
||||
@@ -70,7 +70,8 @@ void CodeSize::visit(Statement const& _statement)
|
||||
else if (
|
||||
_statement.type() == typeid(If) ||
|
||||
_statement.type() == typeid(Break) ||
|
||||
_statement.type() == typeid(Continue)
|
||||
_statement.type() == typeid(Continue) ||
|
||||
_statement.type() == typeid(Leave)
|
||||
)
|
||||
m_size += 2;
|
||||
else if (_statement.type() == typeid(ForLoop))
|
||||
|
||||
@@ -104,12 +104,17 @@ void RedundantAssignEliminator::operator()(Switch const& _switch)
|
||||
void RedundantAssignEliminator::operator()(FunctionDefinition const& _functionDefinition)
|
||||
{
|
||||
std::set<YulString> outerDeclaredVariables;
|
||||
std::set<YulString> outerReturnVariables;
|
||||
TrackedAssignments outerAssignments;
|
||||
ForLoopInfo forLoopInfo;
|
||||
swap(m_declaredVariables, outerDeclaredVariables);
|
||||
swap(m_returnVariables, outerReturnVariables);
|
||||
swap(m_assignments, outerAssignments);
|
||||
swap(m_forLoopInfo, forLoopInfo);
|
||||
|
||||
for (auto const& retParam: _functionDefinition.returnVariables)
|
||||
m_returnVariables.insert(retParam.name);
|
||||
|
||||
(*this)(_functionDefinition.body);
|
||||
|
||||
for (auto const& param: _functionDefinition.parameters)
|
||||
@@ -118,6 +123,7 @@ void RedundantAssignEliminator::operator()(FunctionDefinition const& _functionDe
|
||||
finalize(retParam.name, State::Used);
|
||||
|
||||
swap(m_declaredVariables, outerDeclaredVariables);
|
||||
swap(m_returnVariables, outerReturnVariables);
|
||||
swap(m_assignments, outerAssignments);
|
||||
swap(m_forLoopInfo, forLoopInfo);
|
||||
}
|
||||
@@ -200,6 +206,12 @@ void RedundantAssignEliminator::operator()(Continue const&)
|
||||
m_assignments.clear();
|
||||
}
|
||||
|
||||
void RedundantAssignEliminator::operator()(Leave const&)
|
||||
{
|
||||
for (YulString name: m_returnVariables)
|
||||
changeUndecidedTo(name, State::Used);
|
||||
}
|
||||
|
||||
void RedundantAssignEliminator::operator()(Block const& _block)
|
||||
{
|
||||
set<YulString> outerDeclaredVariables;
|
||||
|
||||
@@ -91,6 +91,8 @@ struct Dialect;
|
||||
* For switch statements that have a "default"-case, there is no control-flow
|
||||
* part that skips the switch.
|
||||
*
|
||||
* At ``leave`` statements, all return variables are set to "used".
|
||||
*
|
||||
* When a variable goes out of scope, all statements still in the "undecided"
|
||||
* state are changed to "unused", unless the variable is the return
|
||||
* parameter of a function - there, the state changes to "used".
|
||||
@@ -125,6 +127,7 @@ public:
|
||||
void operator()(ForLoop const&) override;
|
||||
void operator()(Break const&) override;
|
||||
void operator()(Continue const&) override;
|
||||
void operator()(Leave const&) override;
|
||||
void operator()(Block const& _block) override;
|
||||
|
||||
private:
|
||||
@@ -163,6 +166,7 @@ private:
|
||||
|
||||
Dialect const* m_dialect;
|
||||
std::set<YulString> m_declaredVariables;
|
||||
std::set<YulString> m_returnVariables;
|
||||
std::set<Assignment const*> m_pendingRemovals;
|
||||
TrackedAssignments m_assignments;
|
||||
|
||||
|
||||
@@ -173,6 +173,8 @@ TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement cons
|
||||
return ControlFlow::Break;
|
||||
else if (_statement.type() == typeid(Continue))
|
||||
return ControlFlow::Continue;
|
||||
else if (_statement.type() == typeid(Leave))
|
||||
return ControlFlow::Leave;
|
||||
else
|
||||
return ControlFlow::FlowOut;
|
||||
}
|
||||
|
||||
@@ -113,6 +113,31 @@ private:
|
||||
bool m_msizeFound = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Class that can be used to find out if the given function contains the ``leave`` statement.
|
||||
*
|
||||
* Returns true even in the case where the function definition contains another function definition
|
||||
* that contains the leave statement.
|
||||
*/
|
||||
class LeaveFinder: public ASTWalker
|
||||
{
|
||||
public:
|
||||
static bool containsLeave(FunctionDefinition const& _fun)
|
||||
{
|
||||
LeaveFinder f;
|
||||
f(_fun);
|
||||
return f.m_leaveFound;
|
||||
}
|
||||
|
||||
using ASTWalker::operator();
|
||||
void operator()(Leave const&) { m_leaveFound = true; }
|
||||
|
||||
private:
|
||||
LeaveFinder() = default;
|
||||
|
||||
bool m_leaveFound = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Specific AST walker that determines whether an expression is movable
|
||||
* and collects the referenced variables.
|
||||
@@ -148,12 +173,13 @@ private:
|
||||
class TerminationFinder
|
||||
{
|
||||
public:
|
||||
enum class ControlFlow { FlowOut, Break, Continue, Terminate };
|
||||
// TODO check all uses of TerminationFinder!
|
||||
enum class ControlFlow { FlowOut, Break, Continue, Terminate, Leave };
|
||||
|
||||
TerminationFinder(Dialect const& _dialect): m_dialect(_dialect) {}
|
||||
|
||||
/// @returns the index of the first statement in the provided sequence
|
||||
/// that is an unconditional ``break``, ``continue`` or a
|
||||
/// that is an unconditional ``break``, ``continue``, ``leave`` or a
|
||||
/// call to a terminating builtin function.
|
||||
/// If control flow can continue at the end of the list,
|
||||
/// returns `FlowOut` and ``size_t(-1)``.
|
||||
|
||||
@@ -57,6 +57,7 @@ public:
|
||||
bool statementEqual(ForLoop const& _lhs, ForLoop const& _rhs);
|
||||
bool statementEqual(Break const&, Break const&) { return true; }
|
||||
bool statementEqual(Continue const&, Continue const&) { return true; }
|
||||
bool statementEqual(Leave const&, Leave const&) { return true; }
|
||||
bool statementEqual(Block const& _lhs, Block const& _rhs);
|
||||
private:
|
||||
bool statementEqual(Instruction const& _lhs, Instruction const& _rhs);
|
||||
|
||||
Reference in New Issue
Block a user