[Yul] leave statement.

This commit is contained in:
chriseth
2019-10-29 14:32:16 +01:00
parent edf1e83fda
commit ceb8ee9124
41 changed files with 214 additions and 27 deletions
+6
View File
@@ -519,6 +519,12 @@ bool AsmAnalyzer::operator()(Continue const& _continue)
return true;
}
bool AsmAnalyzer::operator()(Leave const& _leave)
{
m_info.stackHeightInfo[&_leave] = m_stackHeight;
return true;
}
bool AsmAnalyzer::operator()(Block const& _block)
{
bool success = true;
+1
View File
@@ -93,6 +93,7 @@ public:
bool operator()(ForLoop const& _forLoop);
bool operator()(Break const&);
bool operator()(Continue const&);
bool operator()(Leave const&);
bool operator()(Block const& _block);
private:
+2
View File
@@ -78,6 +78,8 @@ struct ForLoop { langutil::SourceLocation location; Block pre; std::unique_ptr<E
struct Break { langutil::SourceLocation location; };
/// Continue statement (valid within for loop)
struct Continue { langutil::SourceLocation location; };
/// Leave statement (valid within function)
struct Leave { langutil::SourceLocation location; };
struct LocationExtractor: boost::static_visitor<langutil::SourceLocation>
{
+2 -1
View File
@@ -42,12 +42,13 @@ struct Case;
struct ForLoop;
struct Break;
struct Continue;
struct Leave;
struct ExpressionStatement;
struct Block;
struct TypedName;
using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>;
using Statement = boost::variant<ExpressionStatement, Instruction, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Break, Continue, Block>;
using Statement = boost::variant<ExpressionStatement, Instruction, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Break, Continue, Leave, Block>;
}
+13
View File
@@ -144,6 +144,16 @@ Statement Parser::parseStatement()
m_scanner->next();
return stmt;
}
case Token::Identifier:
if (currentLiteral() == "leave")
{
Statement stmt{createWithLocation<Leave>()};
if (!m_insideFunction)
m_errorReporter.syntaxError(location(), "Keyword \"leave\" can only be used inside a function.");
m_scanner->next();
return stmt;
}
break;
default:
break;
}
@@ -439,7 +449,10 @@ FunctionDefinition Parser::parseFunctionDefinition()
expectToken(Token::Comma);
}
}
bool preInsideFunction = m_insideFunction;
m_insideFunction = true;
funDef.body = parseBlock();
m_insideFunction = preInsideFunction;
funDef.location.end = funDef.body.location.end;
m_currentForLoopComponent = outerForLoopComponent;
+1
View File
@@ -98,6 +98,7 @@ protected:
private:
Dialect const& m_dialect;
ForLoopComponent m_currentForLoopComponent = ForLoopComponent::None;
bool m_insideFunction = false;
};
}
+5
View File
@@ -227,6 +227,11 @@ string AsmPrinter::operator()(Continue const&) const
return "continue";
}
string AsmPrinter::operator()(Leave const&) const
{
return "leave";
}
string AsmPrinter::operator()(Block const& _block) const
{
if (_block.statements.empty())
+1
View File
@@ -50,6 +50,7 @@ public:
std::string operator()(ForLoop const& _forLoop) const;
std::string operator()(Break const& _break) const;
std::string operator()(Continue const& _continue) const;
std::string operator()(Leave const& _continue) const;
std::string operator()(Block const& _block) const;
private:
+1
View File
@@ -63,6 +63,7 @@ public:
bool operator()(ForLoop const& _forLoop);
bool operator()(Break const&) { return true; }
bool operator()(Continue const&) { return true; }
bool operator()(Leave const&) { return true; }
bool operator()(Block const& _block);
private:
+17
View File
@@ -490,6 +490,9 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
m_assembly.appendConstant(u256(0));
}
m_context->functionExitPoints.push(
CodeTransformContext::JumpInfo{m_assembly.newLabelId(), m_assembly.stackHeight()}
);
try
{
CodeTransform(
@@ -518,6 +521,9 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
stackError(std::move(error), height);
}
m_assembly.appendLabel(m_context->functionExitPoints.top().label);
m_context->functionExitPoints.pop();
{
// The stack layout here is:
// <return label>? <arguments...> <return values...>
@@ -643,6 +649,17 @@ void CodeTransform::operator()(Continue const& _continue)
checkStackHeight(&_continue);
}
void CodeTransform::operator()(Leave const& _leave)
{
yulAssert(!m_context->functionExitPoints.empty(), "Invalid leave-statement. Requires surrounding function in code generation.");
m_assembly.setSourceLocation(_leave.location);
Context::JumpInfo const& jump = m_context->functionExitPoints.top();
m_assembly.appendJumpTo(jump.label, appendPopUntil(jump.targetStackHeight));
checkStackHeight(&_leave);
}
void CodeTransform::operator()(Block const& _block)
{
Scope* originalScope = m_scope;
+2
View File
@@ -73,6 +73,7 @@ struct CodeTransformContext
};
std::stack<ForLoopLabels> forLoopStack;
std::stack<JumpInfo> functionExitPoints;
};
/**
@@ -185,6 +186,7 @@ public:
void operator()(ForLoop const&);
void operator()(Break const&);
void operator()(Continue const&);
void operator()(Leave const&);
void operator()(Block const& _block);
private:
+3 -1
View File
@@ -42,10 +42,11 @@ struct If;
struct Loop;
struct Break;
struct BreakIf;
struct Return;
using Expression = boost::variant<
Literal, StringLiteral, LocalVariable, GlobalVariable,
FunctionCall, BuiltinCall, LocalAssignment, GlobalAssignment,
Block, If, Loop, Break, BreakIf
Block, If, Loop, Break, BreakIf, Return
>;
struct Literal { uint64_t value; };
@@ -65,6 +66,7 @@ struct If {
};
struct Loop { std::string labelName; std::vector<Expression> statements; };
struct Break { Label label; };
struct Return {};
struct BreakIf { Label label; std::unique_ptr<Expression> condition; };
struct VariableDeclaration { std::string variableName; };
@@ -257,6 +257,11 @@ wasm::Expression EWasmCodeTransform::operator()(Continue const&)
return wasm::Break{wasm::Label{m_breakContinueLabelNames.top().second}};
}
wasm::Expression EWasmCodeTransform::operator()(Leave const&)
{
return wasm::Return{};
}
wasm::Expression EWasmCodeTransform::operator()(Block const& _block)
{
return wasm::Block{{}, visit(_block.statements)};
@@ -52,6 +52,7 @@ public:
wasm::Expression operator()(yul::ForLoop const&);
wasm::Expression operator()(yul::Break const&);
wasm::Expression operator()(yul::Continue const&);
wasm::Expression operator()(yul::Leave const&);
wasm::Expression operator()(yul::Block const& _block);
private:
+5
View File
@@ -128,6 +128,11 @@ string EWasmToText::operator()(wasm::BreakIf const& _break)
return "(br_if $" + _break.label.name + " " + visit(*_break.condition) + ")\n";
}
string EWasmToText::operator()(wasm::Return const&)
{
return "(return)\n";
}
string EWasmToText::operator()(wasm::Block const& _block)
{
string label = _block.labelName.empty() ? "" : " $" + _block.labelName;
+1
View File
@@ -49,6 +49,7 @@ public:
std::string operator()(wasm::If const& _if);
std::string operator()(wasm::Loop const& _loop);
std::string operator()(wasm::Break const& _break);
std::string operator()(wasm::Return const& _return);
std::string operator()(wasm::BreakIf const& _break);
std::string operator()(wasm::Block const& _block);
+5
View File
@@ -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);
+2
View File
@@ -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);
+4
View File
@@ -169,6 +169,10 @@ void ASTModifier::operator()(Continue&)
{
}
void ASTModifier::operator()(Leave&)
{
}
void ASTModifier::operator()(Block& _block)
{
walkVector(_block.statements);
+2
View File
@@ -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);
+5
View File
@@ -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)
{
+1
View File
@@ -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);
+4 -1
View File
@@ -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)
+4
View File
@@ -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);
+4
View File
@@ -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
+1 -1
View File
@@ -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.
+4 -1
View File
@@ -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
+2
View File
@@ -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)
+2 -1
View File
@@ -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;
+2
View File
@@ -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;
}
+28 -2
View File
@@ -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)``.
+1
View File
@@ -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);