mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
commit
432b158890
@ -20,6 +20,7 @@ Language Features:
|
|||||||
* Introduce syntax for array slices and implement them for dynamic calldata arrays.
|
* Introduce syntax for array slices and implement them for dynamic calldata arrays.
|
||||||
* Introduce ``push()`` for dynamic storage arrays. It returns a reference to the newly allocated element, if applicable.
|
* Introduce ``push()`` for dynamic storage arrays. It returns a reference to the newly allocated element, if applicable.
|
||||||
* Modify ``push(element)`` for dynamic storage arrays such that it does not return the new length anymore.
|
* Modify ``push(element)`` for dynamic storage arrays such that it does not return the new length anymore.
|
||||||
|
* Yul: Introduce ``leave`` statement that exits the current function.
|
||||||
|
|
||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
|
@ -54,6 +54,7 @@ New Features
|
|||||||
============
|
============
|
||||||
|
|
||||||
* The :ref:`try/catch statement <try-catch>` allows you to react on failed external calls.
|
* The :ref:`try/catch statement <try-catch>` allows you to react on failed external calls.
|
||||||
|
* Yul and Inline Assembly have a new statement called ``leave`` that exits the current function.
|
||||||
|
|
||||||
|
|
||||||
Deprecated Elements
|
Deprecated Elements
|
||||||
|
@ -530,6 +530,9 @@ the other two are blocks. If the initializing part
|
|||||||
declares any variables, the scope of these variables is extended into the
|
declares any variables, the scope of these variables is extended into the
|
||||||
body (including the condition and the post-iteration part).
|
body (including the condition and the post-iteration part).
|
||||||
|
|
||||||
|
The ``break`` and ``continue`` statements can be used to exit the loop
|
||||||
|
or skip to the post-part, respectively.
|
||||||
|
|
||||||
The following example computes the sum of an area in memory.
|
The following example computes the sum of an area in memory.
|
||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
@ -571,6 +574,11 @@ statement.
|
|||||||
If you call a function that returns multiple values, you have to assign
|
If you call a function that returns multiple values, you have to assign
|
||||||
them to a tuple using ``a, b := f(x)`` or ``let a, b := f(x)``.
|
them to a tuple using ``a, b := f(x)`` or ``let a, b := f(x)``.
|
||||||
|
|
||||||
|
The ``leave`` statement can be used to exit the current function. It
|
||||||
|
works like the ``return`` statement in other languages just that it does
|
||||||
|
not take a value to return, it just exits the functions and the function
|
||||||
|
will return whatever values are currently assigned to the return variable(s).
|
||||||
|
|
||||||
The following example implements the power function by square-and-multiply.
|
The following example implements the power function by square-and-multiply.
|
||||||
|
|
||||||
.. code::
|
.. code::
|
||||||
@ -769,6 +777,7 @@ Grammar::
|
|||||||
AssemblyFor |
|
AssemblyFor |
|
||||||
'break' |
|
'break' |
|
||||||
'continue' |
|
'continue' |
|
||||||
|
'leave' |
|
||||||
SubAssembly
|
SubAssembly
|
||||||
AssemblyExpression = AssemblyCall | Identifier | AssemblyLiteral
|
AssemblyExpression = AssemblyCall | Identifier | AssemblyLiteral
|
||||||
AssemblyLiteral = NumberLiteral | StringLiteral | HexLiteral
|
AssemblyLiteral = NumberLiteral | StringLiteral | HexLiteral
|
||||||
|
29
docs/yul.rst
29
docs/yul.rst
@ -100,7 +100,8 @@ Grammar::
|
|||||||
Expression |
|
Expression |
|
||||||
Switch |
|
Switch |
|
||||||
ForLoop |
|
ForLoop |
|
||||||
BreakContinue
|
BreakContinue |
|
||||||
|
Leave
|
||||||
FunctionDefinition =
|
FunctionDefinition =
|
||||||
'function' Identifier '(' TypedIdentifierList? ')'
|
'function' Identifier '(' TypedIdentifierList? ')'
|
||||||
( '->' TypedIdentifierList )? Block
|
( '->' TypedIdentifierList )? Block
|
||||||
@ -122,6 +123,7 @@ Grammar::
|
|||||||
'for' Block Expression Block Block
|
'for' Block Expression Block Block
|
||||||
BreakContinue =
|
BreakContinue =
|
||||||
'break' | 'continue'
|
'break' | 'continue'
|
||||||
|
Leave = 'leave'
|
||||||
FunctionCall =
|
FunctionCall =
|
||||||
Identifier '(' ( Expression ( ',' Expression )* )? ')'
|
Identifier '(' ( Expression ( ',' Expression )* )? ')'
|
||||||
Identifier = [a-zA-Z_$] [a-zA-Z_$0-9.]*
|
Identifier = [a-zA-Z_$] [a-zA-Z_$0-9.]*
|
||||||
@ -167,6 +169,7 @@ In all other situations, expressions have to evaluate to exactly one value.
|
|||||||
The ``continue`` and ``break`` statements can only be used inside loop bodies
|
The ``continue`` and ``break`` statements can only be used inside loop bodies
|
||||||
and have to be in the same function as the loop (or both have to be at the
|
and have to be in the same function as the loop (or both have to be at the
|
||||||
top level).
|
top level).
|
||||||
|
The ``leave`` statement can only be used inside a function.
|
||||||
The condition part of the for-loop has to evaluate to exactly one value.
|
The condition part of the for-loop has to evaluate to exactly one value.
|
||||||
Functions cannot be defined inside for loop init blocks.
|
Functions cannot be defined inside for loop init blocks.
|
||||||
|
|
||||||
@ -214,7 +217,7 @@ The two state objects are the global state object
|
|||||||
blockchain) and the local state object (the state of local variables, i.e. a
|
blockchain) and the local state object (the state of local variables, i.e. a
|
||||||
segment of the stack in the EVM).
|
segment of the stack in the EVM).
|
||||||
If the AST node is a statement, E returns the two state objects and a "mode",
|
If the AST node is a statement, E returns the two state objects and a "mode",
|
||||||
which is used for the ``break`` and ``continue`` statements.
|
which is used for the ``break``, ``continue`` and ``leave`` statements.
|
||||||
If the AST node is an expression, E returns the two state objects and
|
If the AST node is an expression, E returns the two state objects and
|
||||||
as many values as the expression evaluates to.
|
as many values as the expression evaluates to.
|
||||||
|
|
||||||
@ -255,12 +258,13 @@ We will use a destructuring notation for the AST nodes.
|
|||||||
G, L2, regular
|
G, L2, regular
|
||||||
E(G, L, <for { i1, ..., in } condition post body>: ForLoop) =
|
E(G, L, <for { i1, ..., in } condition post body>: ForLoop) =
|
||||||
if n >= 1:
|
if n >= 1:
|
||||||
let G1, L1, mode = E(G, L, i1, ..., in)
|
let G1, L, mode = E(G, L, i1, ..., in)
|
||||||
// mode has to be regular due to the syntactic restrictions
|
// mode has to be regular or leave due to the syntactic restrictions
|
||||||
let G2, L2, mode = E(G1, L1, for {} condition post body)
|
if mode is leave then
|
||||||
// mode has to be regular due to the syntactic restrictions
|
G1, L1 restricted to variables of L, leave
|
||||||
let L3 be the restriction of L2 to only variables of L
|
otherwise
|
||||||
G2, L3, regular
|
let G2, L2, mode = E(G1, L1, for {} condition post body)
|
||||||
|
G2, L2 restricted to variables of L, mode
|
||||||
else:
|
else:
|
||||||
let G1, L1, v = E(G, L, condition)
|
let G1, L1, v = E(G, L, condition)
|
||||||
if v is false:
|
if v is false:
|
||||||
@ -269,13 +273,20 @@ We will use a destructuring notation for the AST nodes.
|
|||||||
let G2, L2, mode = E(G1, L, body)
|
let G2, L2, mode = E(G1, L, body)
|
||||||
if mode is break:
|
if mode is break:
|
||||||
G2, L2, regular
|
G2, L2, regular
|
||||||
|
otherwise if mode is leave:
|
||||||
|
G2, L2, leave
|
||||||
else:
|
else:
|
||||||
G3, L3, mode = E(G2, L2, post)
|
G3, L3, mode = E(G2, L2, post)
|
||||||
E(G3, L3, for {} condition post body)
|
if mode is leave:
|
||||||
|
G2, L3, leave
|
||||||
|
otherwise
|
||||||
|
E(G3, L3, for {} condition post body)
|
||||||
E(G, L, break: BreakContinue) =
|
E(G, L, break: BreakContinue) =
|
||||||
G, L, break
|
G, L, break
|
||||||
E(G, L, continue: BreakContinue) =
|
E(G, L, continue: BreakContinue) =
|
||||||
G, L, continue
|
G, L, continue
|
||||||
|
E(G, L, leave: Leave) =
|
||||||
|
G, L, leave
|
||||||
E(G, L, <if condition body>: If) =
|
E(G, L, <if condition body>: If) =
|
||||||
let G0, L0, v = E(G, L, condition)
|
let G0, L0, v = E(G, L, condition)
|
||||||
if v is true:
|
if v is true:
|
||||||
|
@ -108,6 +108,9 @@ public:
|
|||||||
void operator()(yul::Continue const&)
|
void operator()(yul::Continue const&)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
void operator()(yul::Leave const&)
|
||||||
|
{
|
||||||
|
}
|
||||||
void operator()(yul::Block const& _block)
|
void operator()(yul::Block const& _block)
|
||||||
{
|
{
|
||||||
for (auto const& s: _block.statements)
|
for (auto const& s: _block.statements)
|
||||||
|
@ -132,10 +132,7 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function)
|
|||||||
return m_context.functionCollector()->createFunction(functionName, [&]() {
|
return m_context.functionCollector()->createFunction(functionName, [&]() {
|
||||||
Whiskers t(R"(
|
Whiskers t(R"(
|
||||||
function <functionName>(<params>) <returns> {
|
function <functionName>(<params>) <returns> {
|
||||||
for { let return_flag := 1 } return_flag {} {
|
<body>
|
||||||
<body>
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
t("functionName", functionName);
|
t("functionName", functionName);
|
||||||
|
@ -296,7 +296,7 @@ void IRGeneratorForStatements::endVisit(Return const& _return)
|
|||||||
expressionAsType(*value, *types.front()) <<
|
expressionAsType(*value, *types.front()) <<
|
||||||
"\n";
|
"\n";
|
||||||
}
|
}
|
||||||
m_code << "return_flag := 0\n" << "break\n";
|
m_code << "leave\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation)
|
void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation)
|
||||||
@ -1349,7 +1349,7 @@ void IRGeneratorForStatements::generateLoop(
|
|||||||
m_code << "for {\n";
|
m_code << "for {\n";
|
||||||
if (_initExpression)
|
if (_initExpression)
|
||||||
_initExpression->accept(*this);
|
_initExpression->accept(*this);
|
||||||
m_code << "} return_flag {\n";
|
m_code << "} 1 {\n";
|
||||||
if (_loopExpression)
|
if (_loopExpression)
|
||||||
_loopExpression->accept(*this);
|
_loopExpression->accept(*this);
|
||||||
m_code << "}\n";
|
m_code << "}\n";
|
||||||
@ -1373,8 +1373,6 @@ void IRGeneratorForStatements::generateLoop(
|
|||||||
_body.accept(*this);
|
_body.accept(*this);
|
||||||
|
|
||||||
m_code << "}\n";
|
m_code << "}\n";
|
||||||
// Bubble up the return condition.
|
|
||||||
m_code << "if iszero(return_flag) { break }\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Type const& IRGeneratorForStatements::type(Expression const& _expression)
|
Type const& IRGeneratorForStatements::type(Expression const& _expression)
|
||||||
|
@ -519,6 +519,12 @@ bool AsmAnalyzer::operator()(Continue const& _continue)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AsmAnalyzer::operator()(Leave const& _leave)
|
||||||
|
{
|
||||||
|
m_info.stackHeightInfo[&_leave] = m_stackHeight;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool AsmAnalyzer::operator()(Block const& _block)
|
bool AsmAnalyzer::operator()(Block const& _block)
|
||||||
{
|
{
|
||||||
bool success = true;
|
bool success = true;
|
||||||
|
@ -93,6 +93,7 @@ public:
|
|||||||
bool operator()(ForLoop const& _forLoop);
|
bool operator()(ForLoop const& _forLoop);
|
||||||
bool operator()(Break const&);
|
bool operator()(Break const&);
|
||||||
bool operator()(Continue const&);
|
bool operator()(Continue const&);
|
||||||
|
bool operator()(Leave const&);
|
||||||
bool operator()(Block const& _block);
|
bool operator()(Block const& _block);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -78,6 +78,8 @@ struct ForLoop { langutil::SourceLocation location; Block pre; std::unique_ptr<E
|
|||||||
struct Break { langutil::SourceLocation location; };
|
struct Break { langutil::SourceLocation location; };
|
||||||
/// Continue statement (valid within for loop)
|
/// Continue statement (valid within for loop)
|
||||||
struct Continue { langutil::SourceLocation location; };
|
struct Continue { langutil::SourceLocation location; };
|
||||||
|
/// Leave statement (valid within function)
|
||||||
|
struct Leave { langutil::SourceLocation location; };
|
||||||
|
|
||||||
struct LocationExtractor: boost::static_visitor<langutil::SourceLocation>
|
struct LocationExtractor: boost::static_visitor<langutil::SourceLocation>
|
||||||
{
|
{
|
||||||
|
@ -42,12 +42,13 @@ struct Case;
|
|||||||
struct ForLoop;
|
struct ForLoop;
|
||||||
struct Break;
|
struct Break;
|
||||||
struct Continue;
|
struct Continue;
|
||||||
|
struct Leave;
|
||||||
struct ExpressionStatement;
|
struct ExpressionStatement;
|
||||||
struct Block;
|
struct Block;
|
||||||
|
|
||||||
struct TypedName;
|
struct TypedName;
|
||||||
|
|
||||||
using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>;
|
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>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -144,6 +144,16 @@ Statement Parser::parseStatement()
|
|||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
return stmt;
|
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:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -439,7 +449,10 @@ FunctionDefinition Parser::parseFunctionDefinition()
|
|||||||
expectToken(Token::Comma);
|
expectToken(Token::Comma);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
bool preInsideFunction = m_insideFunction;
|
||||||
|
m_insideFunction = true;
|
||||||
funDef.body = parseBlock();
|
funDef.body = parseBlock();
|
||||||
|
m_insideFunction = preInsideFunction;
|
||||||
funDef.location.end = funDef.body.location.end;
|
funDef.location.end = funDef.body.location.end;
|
||||||
|
|
||||||
m_currentForLoopComponent = outerForLoopComponent;
|
m_currentForLoopComponent = outerForLoopComponent;
|
||||||
|
@ -98,6 +98,7 @@ protected:
|
|||||||
private:
|
private:
|
||||||
Dialect const& m_dialect;
|
Dialect const& m_dialect;
|
||||||
ForLoopComponent m_currentForLoopComponent = ForLoopComponent::None;
|
ForLoopComponent m_currentForLoopComponent = ForLoopComponent::None;
|
||||||
|
bool m_insideFunction = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -227,6 +227,11 @@ string AsmPrinter::operator()(Continue const&) const
|
|||||||
return "continue";
|
return "continue";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string AsmPrinter::operator()(Leave const&) const
|
||||||
|
{
|
||||||
|
return "leave";
|
||||||
|
}
|
||||||
|
|
||||||
string AsmPrinter::operator()(Block const& _block) const
|
string AsmPrinter::operator()(Block const& _block) const
|
||||||
{
|
{
|
||||||
if (_block.statements.empty())
|
if (_block.statements.empty())
|
||||||
|
@ -50,6 +50,7 @@ public:
|
|||||||
std::string operator()(ForLoop const& _forLoop) const;
|
std::string operator()(ForLoop const& _forLoop) const;
|
||||||
std::string operator()(Break const& _break) const;
|
std::string operator()(Break const& _break) const;
|
||||||
std::string operator()(Continue const& _continue) const;
|
std::string operator()(Continue const& _continue) const;
|
||||||
|
std::string operator()(Leave const& _continue) const;
|
||||||
std::string operator()(Block const& _block) const;
|
std::string operator()(Block const& _block) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -63,6 +63,7 @@ public:
|
|||||||
bool operator()(ForLoop const& _forLoop);
|
bool operator()(ForLoop const& _forLoop);
|
||||||
bool operator()(Break const&) { return true; }
|
bool operator()(Break const&) { return true; }
|
||||||
bool operator()(Continue const&) { return true; }
|
bool operator()(Continue const&) { return true; }
|
||||||
|
bool operator()(Leave const&) { return true; }
|
||||||
bool operator()(Block const& _block);
|
bool operator()(Block const& _block);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -490,6 +490,9 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
|
|||||||
m_assembly.appendConstant(u256(0));
|
m_assembly.appendConstant(u256(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_context->functionExitPoints.push(
|
||||||
|
CodeTransformContext::JumpInfo{m_assembly.newLabelId(), m_assembly.stackHeight()}
|
||||||
|
);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
CodeTransform(
|
CodeTransform(
|
||||||
@ -518,6 +521,9 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
|
|||||||
stackError(std::move(error), height);
|
stackError(std::move(error), height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_assembly.appendLabel(m_context->functionExitPoints.top().label);
|
||||||
|
m_context->functionExitPoints.pop();
|
||||||
|
|
||||||
{
|
{
|
||||||
// The stack layout here is:
|
// The stack layout here is:
|
||||||
// <return label>? <arguments...> <return values...>
|
// <return label>? <arguments...> <return values...>
|
||||||
@ -643,6 +649,17 @@ void CodeTransform::operator()(Continue const& _continue)
|
|||||||
checkStackHeight(&_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)
|
void CodeTransform::operator()(Block const& _block)
|
||||||
{
|
{
|
||||||
Scope* originalScope = m_scope;
|
Scope* originalScope = m_scope;
|
||||||
|
@ -73,6 +73,7 @@ struct CodeTransformContext
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::stack<ForLoopLabels> forLoopStack;
|
std::stack<ForLoopLabels> forLoopStack;
|
||||||
|
std::stack<JumpInfo> functionExitPoints;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -185,6 +186,7 @@ public:
|
|||||||
void operator()(ForLoop const&);
|
void operator()(ForLoop const&);
|
||||||
void operator()(Break const&);
|
void operator()(Break const&);
|
||||||
void operator()(Continue const&);
|
void operator()(Continue const&);
|
||||||
|
void operator()(Leave const&);
|
||||||
void operator()(Block const& _block);
|
void operator()(Block const& _block);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -42,10 +42,11 @@ struct If;
|
|||||||
struct Loop;
|
struct Loop;
|
||||||
struct Break;
|
struct Break;
|
||||||
struct BreakIf;
|
struct BreakIf;
|
||||||
|
struct Return;
|
||||||
using Expression = boost::variant<
|
using Expression = boost::variant<
|
||||||
Literal, StringLiteral, LocalVariable, GlobalVariable,
|
Literal, StringLiteral, LocalVariable, GlobalVariable,
|
||||||
FunctionCall, BuiltinCall, LocalAssignment, GlobalAssignment,
|
FunctionCall, BuiltinCall, LocalAssignment, GlobalAssignment,
|
||||||
Block, If, Loop, Break, BreakIf
|
Block, If, Loop, Break, BreakIf, Return
|
||||||
>;
|
>;
|
||||||
|
|
||||||
struct Literal { uint64_t value; };
|
struct Literal { uint64_t value; };
|
||||||
@ -65,6 +66,7 @@ struct If {
|
|||||||
};
|
};
|
||||||
struct Loop { std::string labelName; std::vector<Expression> statements; };
|
struct Loop { std::string labelName; std::vector<Expression> statements; };
|
||||||
struct Break { Label label; };
|
struct Break { Label label; };
|
||||||
|
struct Return {};
|
||||||
struct BreakIf { Label label; std::unique_ptr<Expression> condition; };
|
struct BreakIf { Label label; std::unique_ptr<Expression> condition; };
|
||||||
|
|
||||||
struct VariableDeclaration { std::string variableName; };
|
struct VariableDeclaration { std::string variableName; };
|
||||||
|
@ -257,6 +257,11 @@ wasm::Expression EWasmCodeTransform::operator()(Continue const&)
|
|||||||
return wasm::Break{wasm::Label{m_breakContinueLabelNames.top().second}};
|
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)
|
wasm::Expression EWasmCodeTransform::operator()(Block const& _block)
|
||||||
{
|
{
|
||||||
return wasm::Block{{}, visit(_block.statements)};
|
return wasm::Block{{}, visit(_block.statements)};
|
||||||
|
@ -52,6 +52,7 @@ public:
|
|||||||
wasm::Expression operator()(yul::ForLoop const&);
|
wasm::Expression operator()(yul::ForLoop const&);
|
||||||
wasm::Expression operator()(yul::Break const&);
|
wasm::Expression operator()(yul::Break const&);
|
||||||
wasm::Expression operator()(yul::Continue const&);
|
wasm::Expression operator()(yul::Continue const&);
|
||||||
|
wasm::Expression operator()(yul::Leave const&);
|
||||||
wasm::Expression operator()(yul::Block const& _block);
|
wasm::Expression operator()(yul::Block const& _block);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -128,6 +128,11 @@ string EWasmToText::operator()(wasm::BreakIf const& _break)
|
|||||||
return "(br_if $" + _break.label.name + " " + visit(*_break.condition) + ")\n";
|
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 EWasmToText::operator()(wasm::Block const& _block)
|
||||||
{
|
{
|
||||||
string label = _block.labelName.empty() ? "" : " $" + _block.labelName;
|
string label = _block.labelName.empty() ? "" : " $" + _block.labelName;
|
||||||
|
@ -49,6 +49,7 @@ public:
|
|||||||
std::string operator()(wasm::If const& _if);
|
std::string operator()(wasm::If const& _if);
|
||||||
std::string operator()(wasm::Loop const& _loop);
|
std::string operator()(wasm::Loop const& _loop);
|
||||||
std::string operator()(wasm::Break const& _break);
|
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::BreakIf const& _break);
|
||||||
std::string operator()(wasm::Block const& _block);
|
std::string operator()(wasm::Block const& _block);
|
||||||
|
|
||||||
|
@ -136,6 +136,11 @@ Statement ASTCopier::operator()(Continue const& _continue)
|
|||||||
return Continue{ _continue };
|
return Continue{ _continue };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Statement ASTCopier::operator()(Leave const& _leave)
|
||||||
|
{
|
||||||
|
return Leave{_leave};
|
||||||
|
}
|
||||||
|
|
||||||
Statement ASTCopier::operator ()(Block const& _block)
|
Statement ASTCopier::operator ()(Block const& _block)
|
||||||
{
|
{
|
||||||
return translate(_block);
|
return translate(_block);
|
||||||
|
@ -58,6 +58,7 @@ public:
|
|||||||
virtual Statement operator()(ForLoop const&) = 0;
|
virtual Statement operator()(ForLoop const&) = 0;
|
||||||
virtual Statement operator()(Break const&) = 0;
|
virtual Statement operator()(Break const&) = 0;
|
||||||
virtual Statement operator()(Continue const&) = 0;
|
virtual Statement operator()(Continue const&) = 0;
|
||||||
|
virtual Statement operator()(Leave const&) = 0;
|
||||||
virtual Statement operator()(Block const& _block) = 0;
|
virtual Statement operator()(Block const& _block) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -83,6 +84,7 @@ public:
|
|||||||
Statement operator()(ForLoop const&) override;
|
Statement operator()(ForLoop const&) override;
|
||||||
Statement operator()(Break const&) override;
|
Statement operator()(Break const&) override;
|
||||||
Statement operator()(Continue const&) override;
|
Statement operator()(Continue const&) override;
|
||||||
|
Statement operator()(Leave const&) override;
|
||||||
Statement operator()(Block const& _block) override;
|
Statement operator()(Block const& _block) override;
|
||||||
|
|
||||||
virtual Expression translate(Expression const& _expression);
|
virtual Expression translate(Expression const& _expression);
|
||||||
|
@ -169,6 +169,10 @@ void ASTModifier::operator()(Continue&)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ASTModifier::operator()(Leave&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void ASTModifier::operator()(Block& _block)
|
void ASTModifier::operator()(Block& _block)
|
||||||
{
|
{
|
||||||
walkVector(_block.statements);
|
walkVector(_block.statements);
|
||||||
|
@ -56,6 +56,7 @@ public:
|
|||||||
virtual void operator()(ForLoop const&);
|
virtual void operator()(ForLoop const&);
|
||||||
virtual void operator()(Break const&) {}
|
virtual void operator()(Break const&) {}
|
||||||
virtual void operator()(Continue const&) {}
|
virtual void operator()(Continue const&) {}
|
||||||
|
virtual void operator()(Leave const&) {}
|
||||||
virtual void operator()(Block const& _block);
|
virtual void operator()(Block const& _block);
|
||||||
|
|
||||||
virtual void visit(Statement const& _st);
|
virtual void visit(Statement const& _st);
|
||||||
@ -91,6 +92,7 @@ public:
|
|||||||
virtual void operator()(ForLoop&);
|
virtual void operator()(ForLoop&);
|
||||||
virtual void operator()(Break&);
|
virtual void operator()(Break&);
|
||||||
virtual void operator()(Continue&);
|
virtual void operator()(Continue&);
|
||||||
|
virtual void operator()(Leave&);
|
||||||
virtual void operator()(Block& _block);
|
virtual void operator()(Block& _block);
|
||||||
|
|
||||||
virtual void visit(Statement& _st);
|
virtual void visit(Statement& _st);
|
||||||
|
@ -173,6 +173,11 @@ void BlockHasher::operator()(Continue const& _continue)
|
|||||||
ASTWalker::operator()(_continue);
|
ASTWalker::operator()(_continue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BlockHasher::operator()(Leave const& _leave)
|
||||||
|
{
|
||||||
|
hash64(compileTimeLiteralHash("Leave"));
|
||||||
|
ASTWalker::operator()(_leave);
|
||||||
|
}
|
||||||
|
|
||||||
void BlockHasher::operator()(Block const& _block)
|
void BlockHasher::operator()(Block const& _block)
|
||||||
{
|
{
|
||||||
|
@ -60,6 +60,7 @@ public:
|
|||||||
void operator()(ForLoop const&) override;
|
void operator()(ForLoop const&) override;
|
||||||
void operator()(Break const&) override;
|
void operator()(Break const&) override;
|
||||||
void operator()(Continue const&) override;
|
void operator()(Continue const&) override;
|
||||||
|
void operator()(Leave const&) override;
|
||||||
void operator()(Block const& _block) override;
|
void operator()(Block const& _block) override;
|
||||||
|
|
||||||
static std::map<Block const*, uint64_t> run(Block const& _block);
|
static std::map<Block const*, uint64_t> run(Block const& _block);
|
||||||
|
@ -136,6 +136,13 @@ void ControlFlowSimplifier::operator()(Block& _block)
|
|||||||
simplify(_block.statements);
|
simplify(_block.statements);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ControlFlowSimplifier::operator()(FunctionDefinition& _funDef)
|
||||||
|
{
|
||||||
|
ASTModifier::operator()(_funDef);
|
||||||
|
if (!_funDef.body.statements.empty() && _funDef.body.statements.back().type() == typeid(Leave))
|
||||||
|
_funDef.body.statements.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
void ControlFlowSimplifier::visit(Statement& _st)
|
void ControlFlowSimplifier::visit(Statement& _st)
|
||||||
{
|
{
|
||||||
if (_st.type() == typeid(ForLoop))
|
if (_st.type() == typeid(ForLoop))
|
||||||
@ -159,7 +166,10 @@ void ControlFlowSimplifier::visit(Statement& _st)
|
|||||||
isTerminating = true;
|
isTerminating = true;
|
||||||
--m_numBreakStatements;
|
--m_numBreakStatements;
|
||||||
}
|
}
|
||||||
else if (controlFlow == TerminationFinder::ControlFlow::Terminate)
|
else if (
|
||||||
|
controlFlow == TerminationFinder::ControlFlow::Terminate ||
|
||||||
|
controlFlow == TerminationFinder::ControlFlow::Leave
|
||||||
|
)
|
||||||
isTerminating = true;
|
isTerminating = true;
|
||||||
|
|
||||||
if (isTerminating && m_numContinueStatements == 0 && m_numBreakStatements == 0)
|
if (isTerminating && m_numContinueStatements == 0 && m_numBreakStatements == 0)
|
||||||
|
@ -34,6 +34,7 @@ struct OptimiserStepContext;
|
|||||||
* - replace switch with only default case with pop(expression) and body
|
* - replace switch with only default case with pop(expression) and body
|
||||||
* - replace switch with const expr with matching case body
|
* - replace switch with const expr with matching case body
|
||||||
* - replace ``for`` with terminating control flow and without other break/continue by ``if``
|
* - replace ``for`` with terminating control flow and without other break/continue by ``if``
|
||||||
|
* - remove ``leave`` at the end of a function.
|
||||||
*
|
*
|
||||||
* None of these operations depend on the data flow. The StructuralSimplifier
|
* None of these operations depend on the data flow. The StructuralSimplifier
|
||||||
* performs similar tasks that do depend on data flow.
|
* performs similar tasks that do depend on data flow.
|
||||||
@ -55,6 +56,7 @@ public:
|
|||||||
void operator()(Break&) override { ++m_numBreakStatements; }
|
void operator()(Break&) override { ++m_numBreakStatements; }
|
||||||
void operator()(Continue&) override { ++m_numContinueStatements; }
|
void operator()(Continue&) override { ++m_numContinueStatements; }
|
||||||
void operator()(Block& _block) override;
|
void operator()(Block& _block) override;
|
||||||
|
void operator()(FunctionDefinition& _funDef) override;
|
||||||
|
|
||||||
void visit(Statement& _st) override;
|
void visit(Statement& _st) override;
|
||||||
|
|
||||||
|
@ -161,6 +161,10 @@ void DataFlowAnalyzer::operator()(FunctionDefinition& _fun)
|
|||||||
}
|
}
|
||||||
ASTModifier::operator()(_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();
|
popScope();
|
||||||
m_value.swap(value);
|
m_value.swap(value);
|
||||||
swap(m_references, references);
|
swap(m_references, references);
|
||||||
|
@ -64,6 +64,10 @@ struct SideEffects;
|
|||||||
* older version of the other and thus overlapping contents would have been deleted already
|
* older version of the other and thus overlapping contents would have been deleted already
|
||||||
* at the point of assignment.
|
* 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.
|
* Prerequisite: Disambiguator, ForLoopInitRewriter.
|
||||||
*/
|
*/
|
||||||
class DataFlowAnalyzer: public ASTModifier
|
class DataFlowAnalyzer: public ASTModifier
|
||||||
|
@ -35,7 +35,7 @@ struct OptimiserStepContext;
|
|||||||
* Optimisation stage that removes unreachable code
|
* Optimisation stage that removes unreachable code
|
||||||
*
|
*
|
||||||
* Unreachable code is any code within a block which is preceded by a
|
* 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
|
* Function definitions are retained as they might be called by earlier
|
||||||
* code and thus are considered reachable.
|
* code and thus are considered reachable.
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include <libyul/optimiser/OptimizerUtilities.h>
|
#include <libyul/optimiser/OptimizerUtilities.h>
|
||||||
#include <libyul/optimiser/Metrics.h>
|
#include <libyul/optimiser/Metrics.h>
|
||||||
#include <libyul/optimiser/SSAValueTracker.h>
|
#include <libyul/optimiser/SSAValueTracker.h>
|
||||||
|
#include <libyul/optimiser/Semantics.h>
|
||||||
#include <libyul/Exceptions.h>
|
#include <libyul/Exceptions.h>
|
||||||
#include <libyul/AsmData.h>
|
#include <libyul/AsmData.h>
|
||||||
|
|
||||||
@ -62,6 +63,8 @@ FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser):
|
|||||||
continue;
|
continue;
|
||||||
FunctionDefinition& fun = boost::get<FunctionDefinition>(statement);
|
FunctionDefinition& fun = boost::get<FunctionDefinition>(statement);
|
||||||
m_functions[fun.name] = &fun;
|
m_functions[fun.name] = &fun;
|
||||||
|
if (LeaveFinder::containsLeave(fun))
|
||||||
|
m_noInlineFunctions.insert(fun.name);
|
||||||
// Always inline functions that are only called once.
|
// Always inline functions that are only called once.
|
||||||
if (references[fun.name] == 1)
|
if (references[fun.name] == 1)
|
||||||
m_singleUse.emplace(fun.name);
|
m_singleUse.emplace(fun.name);
|
||||||
@ -94,7 +97,7 @@ bool FullInliner::shallInline(FunctionCall const& _funCall, YulString _callSite)
|
|||||||
if (!calledFunction)
|
if (!calledFunction)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (recursive(*calledFunction))
|
if (m_noInlineFunctions.count(_funCall.functionName.name) || recursive(*calledFunction))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Inline really, really tiny functions
|
// Inline really, really tiny functions
|
||||||
|
@ -102,6 +102,8 @@ private:
|
|||||||
/// we store pointers to functions.
|
/// we store pointers to functions.
|
||||||
Block& m_ast;
|
Block& m_ast;
|
||||||
std::map<YulString, FunctionDefinition*> m_functions;
|
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.
|
/// Names of functions to always inline.
|
||||||
std::set<YulString> m_singleUse;
|
std::set<YulString> m_singleUse;
|
||||||
/// Variables that are constants (used for inlining heuristic)
|
/// Variables that are constants (used for inlining heuristic)
|
||||||
|
@ -70,7 +70,8 @@ void CodeSize::visit(Statement const& _statement)
|
|||||||
else if (
|
else if (
|
||||||
_statement.type() == typeid(If) ||
|
_statement.type() == typeid(If) ||
|
||||||
_statement.type() == typeid(Break) ||
|
_statement.type() == typeid(Break) ||
|
||||||
_statement.type() == typeid(Continue)
|
_statement.type() == typeid(Continue) ||
|
||||||
|
_statement.type() == typeid(Leave)
|
||||||
)
|
)
|
||||||
m_size += 2;
|
m_size += 2;
|
||||||
else if (_statement.type() == typeid(ForLoop))
|
else if (_statement.type() == typeid(ForLoop))
|
||||||
|
@ -104,12 +104,17 @@ void RedundantAssignEliminator::operator()(Switch const& _switch)
|
|||||||
void RedundantAssignEliminator::operator()(FunctionDefinition const& _functionDefinition)
|
void RedundantAssignEliminator::operator()(FunctionDefinition const& _functionDefinition)
|
||||||
{
|
{
|
||||||
std::set<YulString> outerDeclaredVariables;
|
std::set<YulString> outerDeclaredVariables;
|
||||||
|
std::set<YulString> outerReturnVariables;
|
||||||
TrackedAssignments outerAssignments;
|
TrackedAssignments outerAssignments;
|
||||||
ForLoopInfo forLoopInfo;
|
ForLoopInfo forLoopInfo;
|
||||||
swap(m_declaredVariables, outerDeclaredVariables);
|
swap(m_declaredVariables, outerDeclaredVariables);
|
||||||
|
swap(m_returnVariables, outerReturnVariables);
|
||||||
swap(m_assignments, outerAssignments);
|
swap(m_assignments, outerAssignments);
|
||||||
swap(m_forLoopInfo, forLoopInfo);
|
swap(m_forLoopInfo, forLoopInfo);
|
||||||
|
|
||||||
|
for (auto const& retParam: _functionDefinition.returnVariables)
|
||||||
|
m_returnVariables.insert(retParam.name);
|
||||||
|
|
||||||
(*this)(_functionDefinition.body);
|
(*this)(_functionDefinition.body);
|
||||||
|
|
||||||
for (auto const& param: _functionDefinition.parameters)
|
for (auto const& param: _functionDefinition.parameters)
|
||||||
@ -118,6 +123,7 @@ void RedundantAssignEliminator::operator()(FunctionDefinition const& _functionDe
|
|||||||
finalize(retParam.name, State::Used);
|
finalize(retParam.name, State::Used);
|
||||||
|
|
||||||
swap(m_declaredVariables, outerDeclaredVariables);
|
swap(m_declaredVariables, outerDeclaredVariables);
|
||||||
|
swap(m_returnVariables, outerReturnVariables);
|
||||||
swap(m_assignments, outerAssignments);
|
swap(m_assignments, outerAssignments);
|
||||||
swap(m_forLoopInfo, forLoopInfo);
|
swap(m_forLoopInfo, forLoopInfo);
|
||||||
}
|
}
|
||||||
@ -200,6 +206,12 @@ void RedundantAssignEliminator::operator()(Continue const&)
|
|||||||
m_assignments.clear();
|
m_assignments.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RedundantAssignEliminator::operator()(Leave const&)
|
||||||
|
{
|
||||||
|
for (YulString name: m_returnVariables)
|
||||||
|
changeUndecidedTo(name, State::Used);
|
||||||
|
}
|
||||||
|
|
||||||
void RedundantAssignEliminator::operator()(Block const& _block)
|
void RedundantAssignEliminator::operator()(Block const& _block)
|
||||||
{
|
{
|
||||||
set<YulString> outerDeclaredVariables;
|
set<YulString> outerDeclaredVariables;
|
||||||
|
@ -91,6 +91,8 @@ struct Dialect;
|
|||||||
* For switch statements that have a "default"-case, there is no control-flow
|
* For switch statements that have a "default"-case, there is no control-flow
|
||||||
* part that skips the switch.
|
* 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"
|
* When a variable goes out of scope, all statements still in the "undecided"
|
||||||
* state are changed to "unused", unless the variable is the return
|
* state are changed to "unused", unless the variable is the return
|
||||||
* parameter of a function - there, the state changes to "used".
|
* parameter of a function - there, the state changes to "used".
|
||||||
@ -125,6 +127,7 @@ public:
|
|||||||
void operator()(ForLoop const&) override;
|
void operator()(ForLoop const&) override;
|
||||||
void operator()(Break const&) override;
|
void operator()(Break const&) override;
|
||||||
void operator()(Continue const&) override;
|
void operator()(Continue const&) override;
|
||||||
|
void operator()(Leave const&) override;
|
||||||
void operator()(Block const& _block) override;
|
void operator()(Block const& _block) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -163,6 +166,7 @@ private:
|
|||||||
|
|
||||||
Dialect const* m_dialect;
|
Dialect const* m_dialect;
|
||||||
std::set<YulString> m_declaredVariables;
|
std::set<YulString> m_declaredVariables;
|
||||||
|
std::set<YulString> m_returnVariables;
|
||||||
std::set<Assignment const*> m_pendingRemovals;
|
std::set<Assignment const*> m_pendingRemovals;
|
||||||
TrackedAssignments m_assignments;
|
TrackedAssignments m_assignments;
|
||||||
|
|
||||||
|
@ -173,6 +173,8 @@ TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement cons
|
|||||||
return ControlFlow::Break;
|
return ControlFlow::Break;
|
||||||
else if (_statement.type() == typeid(Continue))
|
else if (_statement.type() == typeid(Continue))
|
||||||
return ControlFlow::Continue;
|
return ControlFlow::Continue;
|
||||||
|
else if (_statement.type() == typeid(Leave))
|
||||||
|
return ControlFlow::Leave;
|
||||||
else
|
else
|
||||||
return ControlFlow::FlowOut;
|
return ControlFlow::FlowOut;
|
||||||
}
|
}
|
||||||
|
@ -113,6 +113,31 @@ private:
|
|||||||
bool m_msizeFound = false;
|
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
|
* Specific AST walker that determines whether an expression is movable
|
||||||
* and collects the referenced variables.
|
* and collects the referenced variables.
|
||||||
@ -148,12 +173,13 @@ private:
|
|||||||
class TerminationFinder
|
class TerminationFinder
|
||||||
{
|
{
|
||||||
public:
|
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) {}
|
TerminationFinder(Dialect const& _dialect): m_dialect(_dialect) {}
|
||||||
|
|
||||||
/// @returns the index of the first statement in the provided sequence
|
/// @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.
|
/// call to a terminating builtin function.
|
||||||
/// If control flow can continue at the end of the list,
|
/// If control flow can continue at the end of the list,
|
||||||
/// returns `FlowOut` and ``size_t(-1)``.
|
/// returns `FlowOut` and ``size_t(-1)``.
|
||||||
|
@ -57,6 +57,7 @@ public:
|
|||||||
bool statementEqual(ForLoop const& _lhs, ForLoop const& _rhs);
|
bool statementEqual(ForLoop const& _lhs, ForLoop const& _rhs);
|
||||||
bool statementEqual(Break const&, Break const&) { return true; }
|
bool statementEqual(Break const&, Break const&) { return true; }
|
||||||
bool statementEqual(Continue const&, Continue 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);
|
bool statementEqual(Block const& _lhs, Block const& _rhs);
|
||||||
private:
|
private:
|
||||||
bool statementEqual(Instruction const& _lhs, Instruction const& _rhs);
|
bool statementEqual(Instruction const& _lhs, Instruction const& _rhs);
|
||||||
|
@ -11,10 +11,7 @@ object \"C_6\" {
|
|||||||
codecopy(0, dataoffset(\"C_6_deployed\"), datasize(\"C_6_deployed\"))
|
codecopy(0, dataoffset(\"C_6_deployed\"), datasize(\"C_6_deployed\"))
|
||||||
return(0, datasize(\"C_6_deployed\"))
|
return(0, datasize(\"C_6_deployed\"))
|
||||||
function fun_f_5()
|
function fun_f_5()
|
||||||
{
|
{ }
|
||||||
for { let return_flag := 1 } return_flag { }
|
|
||||||
{ break }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
object \"C_6_deployed\" {
|
object \"C_6_deployed\" {
|
||||||
code {
|
code {
|
||||||
@ -48,10 +45,7 @@ object \"C_6\" {
|
|||||||
mstore(64, newFreePtr)
|
mstore(64, newFreePtr)
|
||||||
}
|
}
|
||||||
function fun_f_5()
|
function fun_f_5()
|
||||||
{
|
{ }
|
||||||
for { let return_flag := 1 } return_flag { }
|
|
||||||
{ break }
|
|
||||||
}
|
|
||||||
function shift_right_224_unsigned(value) -> newValue
|
function shift_right_224_unsigned(value) -> newValue
|
||||||
{ newValue := shr(224, value) }
|
{ newValue := shr(224, value) }
|
||||||
}
|
}
|
||||||
|
@ -19,10 +19,7 @@ object \"C_6\" {
|
|||||||
|
|
||||||
|
|
||||||
function fun_f_5() {
|
function fun_f_5() {
|
||||||
for { let return_flag := 1 } return_flag {} {
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -70,10 +67,7 @@ object \"C_6\" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function fun_f_5() {
|
function fun_f_5() {
|
||||||
for { let return_flag := 1 } return_flag {} {
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function shift_right_224_unsigned(value) -> newValue {
|
function shift_right_224_unsigned(value) -> newValue {
|
||||||
|
@ -31,7 +31,7 @@ object "object" {
|
|||||||
|
|
||||||
|
|
||||||
Binary representation:
|
Binary representation:
|
||||||
60056032565b505050505050505050505050505050601a6032565b5050505050505050505050505050508082555050609a565b60006000600060006000600060006000600060006000600060006000600060006001808155806002558060035580600455806005558060065580600755806008558060095580600a5580600b5580600c5580600d5550909192939495969798999a9b9c9d9e9f565b
|
60056032565b505050505050505050505050505050601a6032565b5050505050505050505050505050508082555050609b565b60006000600060006000600060006000600060006000600060006000600060006001808155806002558060035580600455806005558060065580600755806008558060095580600a5580600b5580600c5580600d55505b909192939495969798999a9b9c9d9e9f565b
|
||||||
|
|
||||||
Text representation:
|
Text representation:
|
||||||
/* "yul_stack_opt/input.sol":495:500 */
|
/* "yul_stack_opt/input.sol":495:500 */
|
||||||
@ -181,6 +181,7 @@ tag_2:
|
|||||||
sstore
|
sstore
|
||||||
pop
|
pop
|
||||||
/* "yul_stack_opt/input.sol":85:423 */
|
/* "yul_stack_opt/input.sol":85:423 */
|
||||||
|
tag_5:
|
||||||
swap1
|
swap1
|
||||||
swap2
|
swap2
|
||||||
swap3
|
swap3
|
||||||
|
14
test/libsolidity/semanticTests/inlineAssembly/leave.sol
Normal file
14
test/libsolidity/semanticTests/inlineAssembly/leave.sol
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure returns (uint w) {
|
||||||
|
assembly {
|
||||||
|
function f() -> t {
|
||||||
|
t := 2
|
||||||
|
leave
|
||||||
|
t := 9
|
||||||
|
}
|
||||||
|
w := f()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// f() -> 2
|
10
test/libsolidity/syntaxTests/inlineAssembly/leave.sol
Normal file
10
test/libsolidity/syntaxTests/inlineAssembly/leave.sol
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
assembly {
|
||||||
|
function f() {
|
||||||
|
leave
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
@ -0,0 +1,9 @@
|
|||||||
|
contract C {
|
||||||
|
function f() public pure {
|
||||||
|
assembly {
|
||||||
|
leave
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// SyntaxError: (63-68): Keyword "leave" can only be used inside a function.
|
@ -171,7 +171,7 @@ BOOST_AUTO_TEST_CASE(function_trivial)
|
|||||||
function f() { }
|
function f() { }
|
||||||
})";
|
})";
|
||||||
BOOST_CHECK_EQUAL(assemble(in),
|
BOOST_CHECK_EQUAL(assemble(in),
|
||||||
"PUSH1 0x5 JUMP JUMPDEST JUMP JUMPDEST "
|
"PUSH1 0x6 JUMP JUMPDEST JUMPDEST JUMP JUMPDEST "
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,8 +181,8 @@ BOOST_AUTO_TEST_CASE(function_retparam)
|
|||||||
function f() -> x, y { }
|
function f() -> x, y { }
|
||||||
})";
|
})";
|
||||||
BOOST_CHECK_EQUAL(assemble(in),
|
BOOST_CHECK_EQUAL(assemble(in),
|
||||||
"PUSH1 0xB JUMP "
|
"PUSH1 0xC JUMP "
|
||||||
"JUMPDEST PUSH1 0x0 PUSH1 0x0 SWAP1 SWAP2 JUMP "
|
"JUMPDEST PUSH1 0x0 PUSH1 0x0 JUMPDEST SWAP1 SWAP2 JUMP "
|
||||||
"JUMPDEST "
|
"JUMPDEST "
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -192,7 +192,7 @@ BOOST_AUTO_TEST_CASE(function_params)
|
|||||||
string in = R"({
|
string in = R"({
|
||||||
function f(a, b) { }
|
function f(a, b) { }
|
||||||
})";
|
})";
|
||||||
BOOST_CHECK_EQUAL(assemble(in), "PUSH1 0x7 JUMP JUMPDEST POP POP JUMP JUMPDEST ");
|
BOOST_CHECK_EQUAL(assemble(in), "PUSH1 0x8 JUMP JUMPDEST JUMPDEST POP POP JUMP JUMPDEST ");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(function_params_and_retparams)
|
BOOST_AUTO_TEST_CASE(function_params_and_retparams)
|
||||||
@ -205,7 +205,7 @@ BOOST_AUTO_TEST_CASE(function_params_and_retparams)
|
|||||||
// layout for a function is still fixed, even though parameters
|
// layout for a function is still fixed, even though parameters
|
||||||
// can be re-used.
|
// can be re-used.
|
||||||
BOOST_CHECK_EQUAL(assemble(in),
|
BOOST_CHECK_EQUAL(assemble(in),
|
||||||
"PUSH1 0x10 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x0 SWAP5 POP SWAP5 SWAP3 POP POP POP JUMP JUMPDEST "
|
"PUSH1 0x11 JUMP JUMPDEST PUSH1 0x0 PUSH1 0x0 JUMPDEST SWAP5 POP SWAP5 SWAP3 POP POP POP JUMP JUMPDEST "
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,12 +215,12 @@ BOOST_AUTO_TEST_CASE(function_params_and_retparams_partly_unused)
|
|||||||
function f(a, b, c, d) -> x, y { b := 3 let s := 9 y := 2 mstore(s, y) }
|
function f(a, b, c, d) -> x, y { b := 3 let s := 9 y := 2 mstore(s, y) }
|
||||||
})";
|
})";
|
||||||
BOOST_CHECK_EQUAL(assemble(in),
|
BOOST_CHECK_EQUAL(assemble(in),
|
||||||
"PUSH1 0x1E JUMP "
|
"PUSH1 0x1F JUMP "
|
||||||
"JUMPDEST PUSH1 0x0 PUSH1 0x0 "
|
"JUMPDEST PUSH1 0x0 PUSH1 0x0 "
|
||||||
"PUSH1 0x3 SWAP4 POP "
|
"PUSH1 0x3 SWAP4 POP "
|
||||||
"PUSH1 0x9 PUSH1 0x2 SWAP2 POP "
|
"PUSH1 0x9 PUSH1 0x2 SWAP2 POP "
|
||||||
"DUP2 DUP2 MSTORE "
|
"DUP2 DUP2 MSTORE "
|
||||||
"POP SWAP5 POP SWAP5 SWAP3 POP POP POP JUMP "
|
"POP JUMPDEST SWAP5 POP SWAP5 SWAP3 POP POP POP JUMP "
|
||||||
"JUMPDEST "
|
"JUMPDEST "
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -237,12 +237,12 @@ BOOST_AUTO_TEST_CASE(function_with_body_embedded)
|
|||||||
})";
|
})";
|
||||||
BOOST_CHECK_EQUAL(assemble(in),
|
BOOST_CHECK_EQUAL(assemble(in),
|
||||||
"PUSH1 0x3 PUSH1 "
|
"PUSH1 0x3 PUSH1 "
|
||||||
"0x16 JUMP "
|
"0x17 JUMP "
|
||||||
"JUMPDEST PUSH1 0x0 " // start of f, initialize t
|
"JUMPDEST PUSH1 0x0 " // start of f, initialize t
|
||||||
"DUP2 POP " // let x := a
|
"DUP2 POP " // let x := a
|
||||||
"PUSH1 0x3 SWAP2 POP "
|
"PUSH1 0x3 SWAP2 POP "
|
||||||
"DUP2 SWAP1 POP "
|
"DUP2 SWAP1 POP "
|
||||||
"SWAP3 SWAP2 POP POP JUMP "
|
"JUMPDEST SWAP3 SWAP2 POP POP JUMP "
|
||||||
"JUMPDEST PUSH1 0x7 SWAP1 "
|
"JUMPDEST PUSH1 0x7 SWAP1 "
|
||||||
"POP POP "
|
"POP POP "
|
||||||
);
|
);
|
||||||
@ -257,9 +257,9 @@ BOOST_AUTO_TEST_CASE(function_call)
|
|||||||
})";
|
})";
|
||||||
BOOST_CHECK_EQUAL(assemble(in),
|
BOOST_CHECK_EQUAL(assemble(in),
|
||||||
"PUSH1 0x9 PUSH1 0x2 PUSH1 0x1 PUSH1 0xD JUMP "
|
"PUSH1 0x9 PUSH1 0x2 PUSH1 0x1 PUSH1 0xD JUMP "
|
||||||
"JUMPDEST PUSH1 0x15 JUMP " // jump over f
|
"JUMPDEST PUSH1 0x16 JUMP " // jump over f
|
||||||
"JUMPDEST PUSH1 0x0 SWAP3 SWAP2 POP POP JUMP " // f
|
"JUMPDEST PUSH1 0x0 JUMPDEST SWAP3 SWAP2 POP POP JUMP " // f
|
||||||
"JUMPDEST PUSH1 0x1F PUSH1 0x4 PUSH1 0x3 PUSH1 0xD JUMP "
|
"JUMPDEST PUSH1 0x20 PUSH1 0x4 PUSH1 0x3 PUSH1 0xD JUMP "
|
||||||
"JUMPDEST SWAP1 POP POP "
|
"JUMPDEST SWAP1 POP POP "
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -277,15 +277,15 @@ BOOST_AUTO_TEST_CASE(functions_multi_return)
|
|||||||
let unused := 7
|
let unused := 7
|
||||||
})";
|
})";
|
||||||
BOOST_CHECK_EQUAL(assemble(in),
|
BOOST_CHECK_EQUAL(assemble(in),
|
||||||
"PUSH1 0x13 JUMP "
|
"PUSH1 0x15 JUMP "
|
||||||
"JUMPDEST PUSH1 0x0 SWAP3 SWAP2 POP POP JUMP " // f
|
"JUMPDEST PUSH1 0x0 JUMPDEST SWAP3 SWAP2 POP POP JUMP " // f
|
||||||
"JUMPDEST PUSH1 0x0 PUSH1 0x0 SWAP1 SWAP2 JUMP " // g
|
"JUMPDEST PUSH1 0x0 PUSH1 0x0 JUMPDEST SWAP1 SWAP2 JUMP " // g
|
||||||
"JUMPDEST PUSH1 0x1D PUSH1 0x2 PUSH1 0x1 PUSH1 0x3 JUMP " // f(1, 2)
|
"JUMPDEST PUSH1 0x1F PUSH1 0x2 PUSH1 0x1 PUSH1 0x3 JUMP " // f(1, 2)
|
||||||
"JUMPDEST PUSH1 0x27 PUSH1 0x4 PUSH1 0x3 PUSH1 0x3 JUMP " // f(3, 4)
|
"JUMPDEST PUSH1 0x29 PUSH1 0x4 PUSH1 0x3 PUSH1 0x3 JUMP " // f(3, 4)
|
||||||
"JUMPDEST SWAP1 POP " // assignment to x
|
"JUMPDEST SWAP1 POP " // assignment to x
|
||||||
"POP " // remove x
|
"POP " // remove x
|
||||||
"PUSH1 0x30 PUSH1 0xB JUMP " // g()
|
"PUSH1 0x32 PUSH1 0xC JUMP " // g()
|
||||||
"JUMPDEST PUSH1 0x36 PUSH1 0xB JUMP " // g()
|
"JUMPDEST PUSH1 0x38 PUSH1 0xC JUMP " // g()
|
||||||
"JUMPDEST SWAP2 POP SWAP2 POP " // assignments
|
"JUMPDEST SWAP2 POP SWAP2 POP " // assignments
|
||||||
"POP POP " // removal of y and z
|
"POP POP " // removal of y and z
|
||||||
"PUSH1 0x7 POP "
|
"PUSH1 0x7 POP "
|
||||||
@ -299,9 +299,9 @@ BOOST_AUTO_TEST_CASE(reuse_slots_function)
|
|||||||
let a, b, c, d := f() let x1 := 2 let y1 := 3 mstore(x1, a) mstore(y1, c)
|
let a, b, c, d := f() let x1 := 2 let y1 := 3 mstore(x1, a) mstore(y1, c)
|
||||||
})";
|
})";
|
||||||
BOOST_CHECK_EQUAL(assemble(in),
|
BOOST_CHECK_EQUAL(assemble(in),
|
||||||
"PUSH1 0x11 JUMP "
|
"PUSH1 0x12 JUMP "
|
||||||
"JUMPDEST PUSH1 0x0 PUSH1 0x0 PUSH1 0x0 PUSH1 0x0 SWAP1 SWAP2 SWAP3 SWAP4 JUMP "
|
"JUMPDEST PUSH1 0x0 PUSH1 0x0 PUSH1 0x0 PUSH1 0x0 JUMPDEST SWAP1 SWAP2 SWAP3 SWAP4 JUMP "
|
||||||
"JUMPDEST PUSH1 0x17 PUSH1 0x3 JUMP "
|
"JUMPDEST PUSH1 0x18 PUSH1 0x3 JUMP "
|
||||||
// Stack: a b c d
|
// Stack: a b c d
|
||||||
"JUMPDEST POP " // d is unused
|
"JUMPDEST POP " // d is unused
|
||||||
// Stack: a b c
|
// Stack: a b c
|
||||||
@ -327,9 +327,9 @@ BOOST_AUTO_TEST_CASE(reuse_slots_function_with_gaps)
|
|||||||
BOOST_CHECK_EQUAL(assemble(in),
|
BOOST_CHECK_EQUAL(assemble(in),
|
||||||
"PUSH1 0x5 PUSH1 0x6 PUSH1 0x7 "
|
"PUSH1 0x5 PUSH1 0x6 PUSH1 0x7 "
|
||||||
"DUP2 DUP4 MSTORE "
|
"DUP2 DUP4 MSTORE "
|
||||||
"PUSH1 0x1A JUMP " // jump across function
|
"PUSH1 0x1B JUMP " // jump across function
|
||||||
"JUMPDEST PUSH1 0x0 PUSH1 0x0 PUSH1 0x0 PUSH1 0x0 SWAP1 SWAP2 SWAP3 SWAP4 JUMP "
|
"JUMPDEST PUSH1 0x0 PUSH1 0x0 PUSH1 0x0 PUSH1 0x0 JUMPDEST SWAP1 SWAP2 SWAP3 SWAP4 JUMP "
|
||||||
"JUMPDEST PUSH1 0x20 PUSH1 0xC JUMP "
|
"JUMPDEST PUSH1 0x21 PUSH1 0xC JUMP "
|
||||||
// stack: x1 x2 x3 a b c d
|
// stack: x1 x2 x3 a b c d
|
||||||
"JUMPDEST SWAP6 POP " // move d into x1
|
"JUMPDEST SWAP6 POP " // move d into x1
|
||||||
// stack: d x2 x3 a b c
|
// stack: d x2 x3 a b c
|
||||||
|
@ -12,10 +12,12 @@ object "Contract" {
|
|||||||
// jump(tag_1)
|
// jump(tag_1)
|
||||||
// tag_2:
|
// tag_2:
|
||||||
// /* "source":46:48 */
|
// /* "source":46:48 */
|
||||||
|
// tag_3:
|
||||||
// jump
|
// jump
|
||||||
// /* "source":53:68 */
|
// /* "source":53:68 */
|
||||||
// tag_3:
|
// tag_4:
|
||||||
// /* "source":66:68 */
|
// /* "source":66:68 */
|
||||||
|
// tag_5:
|
||||||
// jump
|
// jump
|
||||||
// tag_1:
|
// tag_1:
|
||||||
// /* "source":83:84 */
|
// /* "source":83:84 */
|
||||||
@ -24,5 +26,5 @@ object "Contract" {
|
|||||||
// 0x00
|
// 0x00
|
||||||
// /* "source":73:85 */
|
// /* "source":73:85 */
|
||||||
// sstore
|
// sstore
|
||||||
// Bytecode: 6007565b565b565b6001600055
|
// Bytecode: 6009565b5b565b5b565b6001600055
|
||||||
// Opcodes: PUSH1 0x7 JUMP JUMPDEST JUMP JUMPDEST JUMP JUMPDEST PUSH1 0x1 PUSH1 0x0 SSTORE
|
// Opcodes: PUSH1 0x9 JUMP JUMPDEST JUMPDEST JUMP JUMPDEST JUMPDEST JUMP JUMPDEST PUSH1 0x1 PUSH1 0x0 SSTORE
|
||||||
|
20
test/libyul/yulInterpreterTests/leave.yul
Normal file
20
test/libyul/yulInterpreterTests/leave.yul
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
function f() -> x, y
|
||||||
|
{
|
||||||
|
for { x := 0 } lt(x, 10) { x := add(x, 1) } {
|
||||||
|
if eq(x, 5) { y := 1 leave }
|
||||||
|
}
|
||||||
|
x := 9
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let a, b := f()
|
||||||
|
sstore(a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// EVMVersion: >=constantinople
|
||||||
|
// ----
|
||||||
|
// Trace:
|
||||||
|
// Memory dump:
|
||||||
|
// Storage dump:
|
||||||
|
// 0000000000000000000000000000000000000000000000000000000000000005: 0000000000000000000000000000000000000000000000000000000000000001
|
18
test/libyul/yulInterpreterTests/leave_for_init.yul
Normal file
18
test/libyul/yulInterpreterTests/leave_for_init.yul
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
function f() -> x
|
||||||
|
{
|
||||||
|
for { leave x := 2 } eq(x, 0) { } {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let a := f()
|
||||||
|
sstore(a, 7)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// EVMVersion: >=constantinople
|
||||||
|
// ----
|
||||||
|
// Trace:
|
||||||
|
// Memory dump:
|
||||||
|
// Storage dump:
|
||||||
|
// 0000000000000000000000000000000000000000000000000000000000000000: 0000000000000000000000000000000000000000000000000000000000000007
|
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
function f() -> x { x := 7 leave }
|
||||||
|
function g() -> x { leave x := 7 }
|
||||||
|
function h() -> x { if x { leave } }
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// step: controlFlowSimplifier
|
||||||
|
// ----
|
||||||
|
// {
|
||||||
|
// function f() -> x
|
||||||
|
// { x := 7 }
|
||||||
|
// function g() -> x_1
|
||||||
|
// {
|
||||||
|
// leave
|
||||||
|
// x_1 := 7
|
||||||
|
// }
|
||||||
|
// function h() -> x_2
|
||||||
|
// { if x_2 { leave } }
|
||||||
|
// }
|
@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
function f() -> x {
|
||||||
|
for {
|
||||||
|
let a := 20
|
||||||
|
}
|
||||||
|
lt(a, 40)
|
||||||
|
{
|
||||||
|
a := add(a, 2)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
a := a
|
||||||
|
leave
|
||||||
|
mstore(0, a)
|
||||||
|
a := add(a, 10)
|
||||||
|
}
|
||||||
|
x := 9
|
||||||
|
}
|
||||||
|
pop(f())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// step: deadCodeEliminator
|
||||||
|
// ----
|
||||||
|
// {
|
||||||
|
// function f() -> x
|
||||||
|
// {
|
||||||
|
// let a := 20
|
||||||
|
// for { } lt(a, 40) { a := add(a, 2) }
|
||||||
|
// {
|
||||||
|
// a := a
|
||||||
|
// leave
|
||||||
|
// }
|
||||||
|
// x := 9
|
||||||
|
// }
|
||||||
|
// pop(f())
|
||||||
|
// }
|
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
function g() -> x { x := 8 leave }
|
||||||
|
function f(a) { a := g() }
|
||||||
|
let a1 := calldataload(0)
|
||||||
|
f(a1)
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// step: fullInliner
|
||||||
|
// ----
|
||||||
|
// {
|
||||||
|
// {
|
||||||
|
// let a_2 := calldataload(0)
|
||||||
|
// a_2 := g()
|
||||||
|
// }
|
||||||
|
// function g() -> x
|
||||||
|
// {
|
||||||
|
// x := 8
|
||||||
|
// leave
|
||||||
|
// }
|
||||||
|
// function f(a)
|
||||||
|
// { a := g() }
|
||||||
|
// }
|
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
function f(a, b) -> x {
|
||||||
|
let t
|
||||||
|
a := 2
|
||||||
|
x := 2
|
||||||
|
t := 2
|
||||||
|
if b { leave }
|
||||||
|
a := 8
|
||||||
|
x := 8
|
||||||
|
t := 8
|
||||||
|
}
|
||||||
|
function g(a, b) -> x {
|
||||||
|
let t
|
||||||
|
a := 2
|
||||||
|
x := 2
|
||||||
|
t := 2
|
||||||
|
if b { }
|
||||||
|
a := 8
|
||||||
|
x := 8
|
||||||
|
t := 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// step: redundantAssignEliminator
|
||||||
|
// ----
|
||||||
|
// {
|
||||||
|
// function f(a, b) -> x
|
||||||
|
// {
|
||||||
|
// let t
|
||||||
|
// x := 2
|
||||||
|
// if b { leave }
|
||||||
|
// x := 8
|
||||||
|
// }
|
||||||
|
// function g(a_1, b_2) -> x_3
|
||||||
|
// {
|
||||||
|
// let t_4
|
||||||
|
// if b_2 { }
|
||||||
|
// x_3 := 8
|
||||||
|
// }
|
||||||
|
// }
|
@ -124,30 +124,43 @@ void Interpreter::operator()(ForLoop const& _forLoop)
|
|||||||
solAssert(_forLoop.condition, "");
|
solAssert(_forLoop.condition, "");
|
||||||
|
|
||||||
openScope();
|
openScope();
|
||||||
|
ScopeGuard g([this]{ closeScope(); });
|
||||||
|
|
||||||
for (auto const& statement: _forLoop.pre.statements)
|
for (auto const& statement: _forLoop.pre.statements)
|
||||||
|
{
|
||||||
visit(statement);
|
visit(statement);
|
||||||
|
if (m_state.controlFlowState == ControlFlowState::Leave)
|
||||||
|
return;
|
||||||
|
}
|
||||||
while (evaluate(*_forLoop.condition) != 0)
|
while (evaluate(*_forLoop.condition) != 0)
|
||||||
{
|
{
|
||||||
m_state.loopState = LoopState::Default;
|
m_state.controlFlowState = ControlFlowState::Default;
|
||||||
(*this)(_forLoop.body);
|
(*this)(_forLoop.body);
|
||||||
if (m_state.loopState == LoopState::Break)
|
if (m_state.controlFlowState == ControlFlowState::Break || m_state.controlFlowState == ControlFlowState::Leave)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
m_state.loopState = LoopState::Default;
|
m_state.controlFlowState = ControlFlowState::Default;
|
||||||
(*this)(_forLoop.post);
|
(*this)(_forLoop.post);
|
||||||
|
if (m_state.controlFlowState == ControlFlowState::Leave)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
m_state.loopState = LoopState::Default;
|
if (m_state.controlFlowState != ControlFlowState::Leave)
|
||||||
closeScope();
|
m_state.controlFlowState = ControlFlowState::Default;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interpreter::operator()(Break const&)
|
void Interpreter::operator()(Break const&)
|
||||||
{
|
{
|
||||||
m_state.loopState = LoopState::Break;
|
m_state.controlFlowState = ControlFlowState::Break;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interpreter::operator()(Continue const&)
|
void Interpreter::operator()(Continue const&)
|
||||||
{
|
{
|
||||||
m_state.loopState = LoopState::Continue;
|
m_state.controlFlowState = ControlFlowState::Continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Interpreter::operator()(Leave const&)
|
||||||
|
{
|
||||||
|
m_state.controlFlowState = ControlFlowState::Leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interpreter::operator()(Block const& _block)
|
void Interpreter::operator()(Block const& _block)
|
||||||
@ -171,7 +184,7 @@ void Interpreter::operator()(Block const& _block)
|
|||||||
for (auto const& statement: _block.statements)
|
for (auto const& statement: _block.statements)
|
||||||
{
|
{
|
||||||
visit(statement);
|
visit(statement);
|
||||||
if (m_state.loopState != LoopState::Default)
|
if (m_state.controlFlowState != ControlFlowState::Default)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,8 +258,10 @@ void ExpressionEvaluator::operator()(FunctionCall const& _funCall)
|
|||||||
for (size_t i = 0; i < fun->returnVariables.size(); ++i)
|
for (size_t i = 0; i < fun->returnVariables.size(); ++i)
|
||||||
variables[fun->returnVariables.at(i).name] = 0;
|
variables[fun->returnVariables.at(i).name] = 0;
|
||||||
|
|
||||||
|
m_state.controlFlowState = ControlFlowState::Default;
|
||||||
Interpreter interpreter(m_state, m_dialect, variables, functionScopes);
|
Interpreter interpreter(m_state, m_dialect, variables, functionScopes);
|
||||||
interpreter(fun->body);
|
interpreter(fun->body);
|
||||||
|
m_state.controlFlowState = ControlFlowState::Default;
|
||||||
|
|
||||||
m_values.clear();
|
m_values.clear();
|
||||||
for (auto const& retVar: fun->returnVariables)
|
for (auto const& retVar: fun->returnVariables)
|
||||||
|
@ -53,11 +53,12 @@ class TraceLimitReached: public InterpreterTerminatedGeneric
|
|||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class LoopState
|
enum class ControlFlowState
|
||||||
{
|
{
|
||||||
Default,
|
Default,
|
||||||
Continue,
|
Continue,
|
||||||
Break,
|
Break,
|
||||||
|
Leave
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InterpreterState
|
struct InterpreterState
|
||||||
@ -89,7 +90,7 @@ struct InterpreterState
|
|||||||
size_t maxTraceSize = 0;
|
size_t maxTraceSize = 0;
|
||||||
size_t maxSteps = 0;
|
size_t maxSteps = 0;
|
||||||
size_t numSteps = 0;
|
size_t numSteps = 0;
|
||||||
LoopState loopState = LoopState::Default;
|
ControlFlowState controlFlowState = ControlFlowState::Default;
|
||||||
|
|
||||||
void dumpTraceAndState(std::ostream& _out) const;
|
void dumpTraceAndState(std::ostream& _out) const;
|
||||||
};
|
};
|
||||||
@ -121,6 +122,7 @@ public:
|
|||||||
void operator()(ForLoop const&) override;
|
void operator()(ForLoop const&) override;
|
||||||
void operator()(Break const&) override;
|
void operator()(Break const&) override;
|
||||||
void operator()(Continue const&) override;
|
void operator()(Continue const&) override;
|
||||||
|
void operator()(Leave const&) override;
|
||||||
void operator()(Block const& _block) override;
|
void operator()(Block const& _block) override;
|
||||||
|
|
||||||
std::vector<std::string> const& trace() const { return m_state.trace; }
|
std::vector<std::string> const& trace() const { return m_state.trace; }
|
||||||
|
Loading…
Reference in New Issue
Block a user