mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Prototype of a full SSA transformation.
This commit is contained in:
parent
591f04e41c
commit
dab68e6254
@ -125,6 +125,10 @@ add_library(yul
|
|||||||
optimiser/ForLoopInitRewriter.h
|
optimiser/ForLoopInitRewriter.h
|
||||||
optimiser/FullInliner.cpp
|
optimiser/FullInliner.cpp
|
||||||
optimiser/FullInliner.h
|
optimiser/FullInliner.h
|
||||||
|
optimiser/FullSSAReverse.cpp
|
||||||
|
optimiser/FullSSAReverse.h
|
||||||
|
optimiser/FullSSATransform.cpp
|
||||||
|
optimiser/FullSSATransform.h
|
||||||
optimiser/FunctionCallFinder.cpp
|
optimiser/FunctionCallFinder.cpp
|
||||||
optimiser/FunctionCallFinder.h
|
optimiser/FunctionCallFinder.h
|
||||||
optimiser/FunctionGrouper.cpp
|
optimiser/FunctionGrouper.cpp
|
||||||
|
@ -150,6 +150,38 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
|
|||||||
builtins.emplace(createEVMFunction(name, opcode));
|
builtins.emplace(createEVMFunction(name, opcode));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
builtins.emplace(createFunction(
|
||||||
|
"phi_store",
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
SideEffects{false, false, false, false, true, SideEffects::None, SideEffects::None, SideEffects::None},
|
||||||
|
{LiteralKind::String, nullopt},
|
||||||
|
[](
|
||||||
|
FunctionCall const&,
|
||||||
|
AbstractAssembly&,
|
||||||
|
BuiltinContext&,
|
||||||
|
std::function<void(Expression const&)>
|
||||||
|
) {
|
||||||
|
yulAssert(false, "leftover phi_store");
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
builtins.emplace(createFunction(
|
||||||
|
"phi_load",
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
SideEffects{},
|
||||||
|
{LiteralKind::String},
|
||||||
|
[](
|
||||||
|
FunctionCall const&,
|
||||||
|
AbstractAssembly&,
|
||||||
|
BuiltinContext&,
|
||||||
|
std::function<void(Expression const&)>
|
||||||
|
) {
|
||||||
|
yulAssert(false, "leftover phi_load");
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
if (_objectAccess)
|
if (_objectAccess)
|
||||||
{
|
{
|
||||||
builtins.emplace(createFunction("linkersymbol", 1, 1, SideEffects{}, {LiteralKind::String}, [](
|
builtins.emplace(createFunction("linkersymbol", 1, 1, SideEffects{}, {LiteralKind::String}, [](
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/*(
|
/*
|
||||||
This file is part of solidity.
|
This file is part of solidity.
|
||||||
|
|
||||||
solidity is free software: you can redistribute it and/or modify
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
134
libyul/optimiser/FullSSAReverse.cpp
Normal file
134
libyul/optimiser/FullSSAReverse.cpp
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <libyul/optimiser/FullSSAReverse.h>
|
||||||
|
#include <libyul/optimiser/ASTCopier.h>
|
||||||
|
|
||||||
|
#include <libyul/optimiser/NameCollector.h>
|
||||||
|
#include <libyul/optimiser/NameDispenser.h>
|
||||||
|
#include <libyul/optimiser/Semantics.h>
|
||||||
|
#include <libyul/AST.h>
|
||||||
|
#include <libyul/Dialect.h>
|
||||||
|
#include <libyul/Exceptions.h>
|
||||||
|
|
||||||
|
#include <libsolutil/CommonData.h>
|
||||||
|
#include <libsolutil/Visitor.h>
|
||||||
|
|
||||||
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
|
#include <boost/range/algorithm_ext/erase.hpp>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace solidity;
|
||||||
|
using namespace solidity::util;
|
||||||
|
using namespace solidity::yul;
|
||||||
|
|
||||||
|
class FullSSAReverseLoad: public ASTCopier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FullSSAReverseLoad(std::map<YulString, YulString> const& _variableNames): m_variableNames(_variableNames) {}
|
||||||
|
Expression operator()(FunctionCall const& _funCall) override
|
||||||
|
{
|
||||||
|
if (_funCall.functionName.name == "phi_load"_yulstring)
|
||||||
|
{
|
||||||
|
yulAssert(_funCall.arguments.size() == 1, "");
|
||||||
|
Literal const* literal = std::get_if<Literal>(&_funCall.arguments.front());
|
||||||
|
yulAssert(literal && literal->kind == LiteralKind::String, "");
|
||||||
|
yulAssert(m_variableNames.count(literal->value), "");
|
||||||
|
return Identifier{
|
||||||
|
_funCall.location,
|
||||||
|
m_variableNames.at(literal->value)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return ASTCopier::operator()(_funCall);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
map<YulString, YulString> const& m_variableNames;
|
||||||
|
};
|
||||||
|
|
||||||
|
void FullSSAReverse::run(OptimiserStepContext& _context, Block& _ast)
|
||||||
|
{
|
||||||
|
FullSSAReverse fullSSAReverse{_context.dispenser};
|
||||||
|
fullSSAReverse(_ast);
|
||||||
|
_ast = FullSSAReverseLoad{fullSSAReverse.m_variableNames}.translate(_ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FullSSAReverse::operator()(Block& _block)
|
||||||
|
{
|
||||||
|
util::iterateReplacing(
|
||||||
|
_block.statements,
|
||||||
|
[&](Statement& _stmt) -> std::optional<vector<Statement>>
|
||||||
|
{
|
||||||
|
if (auto* expressionStatement = std::get_if<ExpressionStatement>(&_stmt))
|
||||||
|
if (auto* functionCall = std::get_if<FunctionCall>(&expressionStatement->expression))
|
||||||
|
if (functionCall->functionName.name == "phi_store"_yulstring)
|
||||||
|
{
|
||||||
|
yulAssert(functionCall->arguments.size() == 2, "");
|
||||||
|
Literal const* literal = std::get_if<Literal>(&functionCall->arguments.front());
|
||||||
|
yulAssert(literal && literal->kind == LiteralKind::String, "");
|
||||||
|
|
||||||
|
vector<Statement> result;
|
||||||
|
if (m_variableNames.count(literal->value))
|
||||||
|
result.emplace_back(Assignment{
|
||||||
|
functionCall->location,
|
||||||
|
{Identifier{literal->location, m_variableNames.at(literal->value)}},
|
||||||
|
make_unique<Expression>(move(functionCall->arguments.back()))
|
||||||
|
});
|
||||||
|
else
|
||||||
|
{
|
||||||
|
YulString newName = m_nameDispenser.newName(literal->value);
|
||||||
|
m_variableNames[literal->value] = newName;
|
||||||
|
result.emplace_back(VariableDeclaration{
|
||||||
|
functionCall->location,
|
||||||
|
{TypedName{literal->location, newName, YulString{}}},
|
||||||
|
make_unique<Expression>(move(functionCall->arguments.back()))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
visit(_stmt);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FullSSAReverse::operator()(FunctionDefinition& _funDef)
|
||||||
|
{
|
||||||
|
set<YulString> oldFunctionReturns;
|
||||||
|
swap(m_currentFunctionReturns, oldFunctionReturns);
|
||||||
|
vector<Statement> bodyPrefix;
|
||||||
|
for (auto& var: _funDef.returnVariables)
|
||||||
|
{
|
||||||
|
YulString newName = m_nameDispenser.newName(var.name);
|
||||||
|
m_variableNames[var.name] = newName;
|
||||||
|
m_currentFunctionReturns.emplace(var.name);
|
||||||
|
bodyPrefix.emplace_back(VariableDeclaration{
|
||||||
|
var.location,
|
||||||
|
{var},
|
||||||
|
make_unique<Expression>(Identifier{var.location, newName})
|
||||||
|
});
|
||||||
|
var.name = newName;
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyPrefix += std::move(_funDef.body.statements);
|
||||||
|
_funDef.body.statements = std::move(bodyPrefix);
|
||||||
|
|
||||||
|
ASTModifier::operator()(_funDef);
|
||||||
|
|
||||||
|
swap(m_currentFunctionReturns, oldFunctionReturns);
|
||||||
|
}
|
52
libyul/optimiser/FullSSAReverse.h
Normal file
52
libyul/optimiser/FullSSAReverse.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libyul/optimiser/ASTWalker.h>
|
||||||
|
#include <libyul/optimiser/KnowledgeBase.h>
|
||||||
|
#include <libyul/optimiser/OptimiserStep.h>
|
||||||
|
#include <libyul/YulString.h>
|
||||||
|
#include <libyul/AST.h>
|
||||||
|
#include <libyul/SideEffects.h>
|
||||||
|
|
||||||
|
#include <libsolutil/InvertibleMap.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
namespace solidity::yul
|
||||||
|
{
|
||||||
|
struct Dialect;
|
||||||
|
|
||||||
|
class FullSSAReverse: public ASTModifier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr char const* name{"FullSSAReverse"};
|
||||||
|
static void run(OptimiserStepContext& _context, Block& _ast);
|
||||||
|
using ASTModifier::operator();
|
||||||
|
void operator()(Block& _block) override;
|
||||||
|
void operator()(FunctionDefinition& _funDef) override;
|
||||||
|
private:
|
||||||
|
FullSSAReverse(NameDispenser& _nameDispenser): m_nameDispenser(_nameDispenser) {}
|
||||||
|
NameDispenser& m_nameDispenser;
|
||||||
|
std::map<YulString, YulString> m_variableNames;
|
||||||
|
std::set<YulString> m_currentFunctionReturns;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
300
libyul/optimiser/FullSSATransform.cpp
Normal file
300
libyul/optimiser/FullSSATransform.cpp
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include <libyul/optimiser/FullSSATransform.h>
|
||||||
|
|
||||||
|
#include <libyul/optimiser/NameCollector.h>
|
||||||
|
#include <libyul/optimiser/NameDispenser.h>
|
||||||
|
#include <libyul/AST.h>
|
||||||
|
#include <libyul/Exceptions.h>
|
||||||
|
|
||||||
|
#include <libsolutil/CommonData.h>
|
||||||
|
#include <libsolutil/Visitor.h>
|
||||||
|
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace solidity;
|
||||||
|
using namespace solidity::util;
|
||||||
|
using namespace solidity::yul;
|
||||||
|
|
||||||
|
void FullSSATransform::run(OptimiserStepContext& _context, Block& _ast)
|
||||||
|
{
|
||||||
|
FullSSATransform transform{_context};
|
||||||
|
transform(_ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
FullSSATransform::FullSSATransform(OptimiserStepContext& _context): m_nameDispenser(_context.dispenser)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void FullSSATransform::operator()(VariableDeclaration& _varDecl)
|
||||||
|
{
|
||||||
|
ASTModifier::operator()(_varDecl);
|
||||||
|
for (auto const& _var: _varDecl.variables)
|
||||||
|
{
|
||||||
|
yulAssert(!m_currentSSANames.count(_var.name), "");
|
||||||
|
m_currentSSANames[_var.name] = _var.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FullSSATransform::operator()(Identifier& _identifier)
|
||||||
|
{
|
||||||
|
yulAssert(m_currentSSANames.count(_identifier.name), "");
|
||||||
|
_identifier.name = m_currentSSANames[_identifier.name];
|
||||||
|
}
|
||||||
|
|
||||||
|
void FullSSATransform::operator()(FunctionDefinition& _functionDefinition)
|
||||||
|
{
|
||||||
|
vector<YulString> oldFunctionReturnVariables;
|
||||||
|
swap(oldFunctionReturnVariables, m_currentFunctionReturnVariables);
|
||||||
|
|
||||||
|
for (auto const& var: _functionDefinition.returnVariables)
|
||||||
|
m_currentFunctionReturnVariables.emplace_back(var.name);
|
||||||
|
|
||||||
|
for (auto const& argument: _functionDefinition.parameters + _functionDefinition.returnVariables)
|
||||||
|
{
|
||||||
|
yulAssert(!m_currentSSANames.count(argument.name), "");
|
||||||
|
m_currentSSANames[argument.name] = argument.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
(*this)(_functionDefinition.body);
|
||||||
|
|
||||||
|
for (auto const& var: _functionDefinition.returnVariables)
|
||||||
|
_functionDefinition.body.statements.emplace_back(makePhiStore(var.name, m_currentSSANames[var.name]));
|
||||||
|
|
||||||
|
swap(m_currentFunctionReturnVariables, oldFunctionReturnVariables);
|
||||||
|
}
|
||||||
|
|
||||||
|
Statement FullSSATransform::makePhiStore(YulString _var, YulString _value)
|
||||||
|
{
|
||||||
|
return ExpressionStatement{
|
||||||
|
langutil::SourceLocation{}, // TODO
|
||||||
|
FunctionCall{
|
||||||
|
langutil::SourceLocation{}, // TODO
|
||||||
|
Identifier{
|
||||||
|
langutil::SourceLocation{},
|
||||||
|
YulString{"phi_store"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Literal{
|
||||||
|
langutil::SourceLocation{}, // TODO
|
||||||
|
LiteralKind::String,
|
||||||
|
_var,
|
||||||
|
YulString{} // TODO
|
||||||
|
},
|
||||||
|
Identifier{
|
||||||
|
langutil::SourceLocation{}, // TODO
|
||||||
|
_value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression FullSSATransform::makePhiLoad(YulString _var)
|
||||||
|
{
|
||||||
|
return FunctionCall{
|
||||||
|
langutil::SourceLocation{}, // TODO
|
||||||
|
Identifier{
|
||||||
|
langutil::SourceLocation{},
|
||||||
|
YulString{"phi_load"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Literal{
|
||||||
|
langutil::SourceLocation{}, // TODO
|
||||||
|
LiteralKind::String,
|
||||||
|
_var,
|
||||||
|
YulString{} // TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void FullSSATransform::operator()(Block& _block)
|
||||||
|
{
|
||||||
|
auto addReloads = [&](map<YulString, YulString> const& _variables, vector<Statement>& _statements) {
|
||||||
|
for (auto [origName, oldName]: _variables)
|
||||||
|
{
|
||||||
|
YulString newName = m_nameDispenser.newName(origName);
|
||||||
|
_statements.emplace_back(
|
||||||
|
VariableDeclaration{
|
||||||
|
langutil::SourceLocation{},
|
||||||
|
{TypedName{langutil::SourceLocation{}, newName, YulString{}}},
|
||||||
|
make_unique<Expression>(makePhiLoad(oldName))
|
||||||
|
}
|
||||||
|
);
|
||||||
|
m_currentSSANames[origName] = newName;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
auto saveAssignedVariables = [&](auto const& _names, vector<Statement>& _result) {
|
||||||
|
map<YulString, YulString> storedNames;
|
||||||
|
for (YulString var: _names)
|
||||||
|
if (m_currentSSANames.count(var))
|
||||||
|
{
|
||||||
|
YulString currentName = m_currentSSANames[var];
|
||||||
|
_result.emplace_back(makePhiStore(currentName, currentName));
|
||||||
|
storedNames[var] = currentName;
|
||||||
|
}
|
||||||
|
return storedNames;
|
||||||
|
};
|
||||||
|
auto restoreAssignedVariables = [&](map<YulString, YulString> const& _storedNames, vector<Statement>& _result) {
|
||||||
|
for (auto [origName, oldName]: _storedNames)
|
||||||
|
{
|
||||||
|
yulAssert(m_currentSSANames.count(origName), "");
|
||||||
|
YulString currentName = m_currentSSANames[origName];
|
||||||
|
_result.emplace_back(makePhiStore(oldName, currentName));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using OptionalStatements = optional<vector<Statement>>;
|
||||||
|
util::iterateReplacing(
|
||||||
|
_block.statements,
|
||||||
|
[&](Statement& _stmt) -> OptionalStatements
|
||||||
|
{
|
||||||
|
return std::visit(util::GenericVisitor{
|
||||||
|
[&](Assignment& _assignment) -> OptionalStatements {
|
||||||
|
visit(*_assignment.value);
|
||||||
|
vector<Statement> result;
|
||||||
|
VariableDeclaration varDecl{
|
||||||
|
_assignment.location,
|
||||||
|
{},
|
||||||
|
std::move(_assignment.value)
|
||||||
|
};
|
||||||
|
for (auto& var: _assignment.variableNames)
|
||||||
|
{
|
||||||
|
yulAssert(m_currentSSANames.count(var.name), "");
|
||||||
|
YulString newName = m_nameDispenser.newName(var.name);
|
||||||
|
varDecl.variables.emplace_back(TypedName{
|
||||||
|
var.location,
|
||||||
|
newName,
|
||||||
|
YulString{}
|
||||||
|
});
|
||||||
|
m_currentSSANames[var.name] = newName;
|
||||||
|
}
|
||||||
|
result.emplace_back(move(varDecl));
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
[&](If& _if) -> OptionalStatements {
|
||||||
|
Assignments assignments;
|
||||||
|
assignments(_if);
|
||||||
|
vector<Statement> result;
|
||||||
|
auto storedAssignedVariables = saveAssignedVariables(assignments.names(), result);
|
||||||
|
|
||||||
|
(*this)(_if);
|
||||||
|
restoreAssignedVariables(storedAssignedVariables, _if.body.statements);
|
||||||
|
result.emplace_back(std::move(_if));
|
||||||
|
|
||||||
|
addReloads(storedAssignedVariables, result);
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
[&](Switch& _switch) -> OptionalStatements {
|
||||||
|
Assignments assignments;
|
||||||
|
assignments(_switch);
|
||||||
|
vector<Statement> result;
|
||||||
|
auto storedAssignedVariables = saveAssignedVariables(assignments.names(), result);
|
||||||
|
|
||||||
|
visit(*_switch.expression);
|
||||||
|
auto saved = m_currentSSANames;
|
||||||
|
for (auto& switchCase: _switch.cases)
|
||||||
|
{
|
||||||
|
m_currentSSANames = saved;
|
||||||
|
if (switchCase.value)
|
||||||
|
(*this)(*switchCase.value);
|
||||||
|
(*this)(switchCase.body);
|
||||||
|
restoreAssignedVariables(storedAssignedVariables, switchCase.body.statements);
|
||||||
|
}
|
||||||
|
result.emplace_back(std::move(_switch));
|
||||||
|
|
||||||
|
addReloads(storedAssignedVariables, result);
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
[&](ForLoop& _loop) -> OptionalStatements {
|
||||||
|
yulAssert(_loop.pre.statements.empty(), "");
|
||||||
|
std::map<YulString, YulString> oldLoopAssignments;
|
||||||
|
swap(oldLoopAssignments, m_currentLoopAssignments);
|
||||||
|
Assignments assignments;
|
||||||
|
assignments(_loop);
|
||||||
|
|
||||||
|
vector<Statement> result;
|
||||||
|
|
||||||
|
if (auto* identifier = std::get_if<Identifier>(_loop.condition.get()))
|
||||||
|
{
|
||||||
|
if (assignments.names().count(identifier->name))
|
||||||
|
{
|
||||||
|
yulAssert(m_currentSSANames.count(identifier->name), "");
|
||||||
|
_loop.condition = make_unique<Expression>(makePhiLoad(m_currentSSANames[identifier->name]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
yulAssert(holds_alternative<Literal>(*_loop.condition), "for loop into body required");
|
||||||
|
|
||||||
|
m_currentLoopAssignments = saveAssignedVariables(assignments.names(), result);
|
||||||
|
|
||||||
|
vector<Statement> newBody;
|
||||||
|
addReloads(m_currentLoopAssignments, newBody);
|
||||||
|
|
||||||
|
(*this)(_loop.body);
|
||||||
|
|
||||||
|
newBody += std::move(_loop.body.statements);
|
||||||
|
_loop.body.statements = std::move(newBody);
|
||||||
|
|
||||||
|
restoreAssignedVariables(m_currentLoopAssignments, _loop.body.statements);
|
||||||
|
|
||||||
|
vector<Statement> newPost;
|
||||||
|
addReloads(m_currentLoopAssignments, newPost);
|
||||||
|
|
||||||
|
(*this)(_loop.post);
|
||||||
|
|
||||||
|
restoreAssignedVariables(m_currentLoopAssignments, _loop.post.statements);
|
||||||
|
|
||||||
|
newPost += std::move(_loop.post.statements);
|
||||||
|
_loop.post.statements = std::move(newPost);
|
||||||
|
|
||||||
|
result.emplace_back(move(_loop));
|
||||||
|
|
||||||
|
addReloads(m_currentLoopAssignments, result);
|
||||||
|
|
||||||
|
swap(m_currentLoopAssignments, oldLoopAssignments);
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
[&](Continue& _continue) -> OptionalStatements {
|
||||||
|
vector<Statement> result;
|
||||||
|
restoreAssignedVariables(m_currentLoopAssignments, result);
|
||||||
|
result.emplace_back(std::move(_continue));
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
[&](Break& _break) -> OptionalStatements {
|
||||||
|
vector<Statement> result;
|
||||||
|
restoreAssignedVariables(m_currentLoopAssignments, result);
|
||||||
|
result.emplace_back(std::move(_break));
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
[&](Leave& _leaveStatement) -> OptionalStatements {
|
||||||
|
vector<Statement> result;
|
||||||
|
for (YulString var: m_currentFunctionReturnVariables)
|
||||||
|
{
|
||||||
|
yulAssert(m_currentSSANames.count(var), "");
|
||||||
|
result.emplace_back(makePhiStore(var, m_currentSSANames[var]));
|
||||||
|
}
|
||||||
|
result.emplace_back(std::move(_leaveStatement));
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
[&](auto& _stmt) -> OptionalStatements { (*this)(_stmt); return std::nullopt; }
|
||||||
|
}, _stmt);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
59
libyul/optimiser/FullSSATransform.h
Normal file
59
libyul/optimiser/FullSSATransform.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
This file is part of solidity.
|
||||||
|
|
||||||
|
solidity is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
solidity is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libyul/optimiser/ASTWalker.h>
|
||||||
|
#include <libyul/optimiser/KnowledgeBase.h>
|
||||||
|
#include <libyul/optimiser/OptimiserStep.h>
|
||||||
|
#include <libyul/YulString.h>
|
||||||
|
#include <libyul/AST.h>
|
||||||
|
#include <libyul/SideEffects.h>
|
||||||
|
|
||||||
|
#include <libsolutil/InvertibleMap.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
namespace solidity::yul
|
||||||
|
{
|
||||||
|
struct Dialect;
|
||||||
|
|
||||||
|
class FullSSATransform: public ASTModifier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr char const* name{"FullSSATransform"};
|
||||||
|
static void run(OptimiserStepContext& _context, Block& _ast);
|
||||||
|
explicit FullSSATransform(OptimiserStepContext& _context);
|
||||||
|
|
||||||
|
using ASTModifier::operator();
|
||||||
|
void operator()(VariableDeclaration& _varDecl) override;
|
||||||
|
void operator()(Identifier& _varDecl) override;
|
||||||
|
void operator()(FunctionDefinition& _functionDefinition) override;
|
||||||
|
void operator()(Block& _block) override;
|
||||||
|
private:
|
||||||
|
std::map<YulString, YulString> m_currentSSANames;
|
||||||
|
NameDispenser& m_nameDispenser;
|
||||||
|
std::vector<YulString> m_currentFunctionReturnVariables;
|
||||||
|
std::map<YulString, YulString> m_currentLoopAssignments;
|
||||||
|
|
||||||
|
static Statement makePhiStore(YulString _var, YulString _value);
|
||||||
|
static Expression makePhiLoad(YulString _var);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -44,6 +44,18 @@ void NameCollector::operator ()(FunctionDefinition const& _funDef)
|
|||||||
ASTWalker::operator ()(_funDef);
|
ASTWalker::operator ()(_funDef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NameCollector::operator()(FunctionCall const& _funCall)
|
||||||
|
{
|
||||||
|
if (_funCall.functionName.name == "phi_store"_yulstring || _funCall.functionName.name == "phi_load"_yulstring)
|
||||||
|
{
|
||||||
|
yulAssert(!_funCall.arguments.empty(), "");
|
||||||
|
auto const* literal = std::get_if<Literal>(&_funCall.arguments.front());
|
||||||
|
yulAssert(literal && literal->kind == LiteralKind::String, "");
|
||||||
|
m_names.emplace(literal->value);
|
||||||
|
}
|
||||||
|
ASTWalker::operator ()(_funCall);
|
||||||
|
}
|
||||||
|
|
||||||
void ReferencesCounter::operator()(Identifier const& _identifier)
|
void ReferencesCounter::operator()(Identifier const& _identifier)
|
||||||
{
|
{
|
||||||
++m_references[_identifier.name];
|
++m_references[_identifier.name];
|
||||||
|
@ -43,6 +43,7 @@ public:
|
|||||||
using ASTWalker::operator ();
|
using ASTWalker::operator ();
|
||||||
void operator()(VariableDeclaration const& _varDecl) override;
|
void operator()(VariableDeclaration const& _varDecl) override;
|
||||||
void operator()(FunctionDefinition const& _funDef) override;
|
void operator()(FunctionDefinition const& _funDef) override;
|
||||||
|
void operator()(FunctionCall const& _funCall) override;
|
||||||
|
|
||||||
std::set<YulString> names() const { return m_names; }
|
std::set<YulString> names() const { return m_names; }
|
||||||
private:
|
private:
|
||||||
|
@ -308,7 +308,19 @@ void RedundantAssignEliminator::finalize(YulString _variable, RedundantAssignEli
|
|||||||
void AssignmentRemover::operator()(Block& _block)
|
void AssignmentRemover::operator()(Block& _block)
|
||||||
{
|
{
|
||||||
boost::range::remove_erase_if(_block.statements, [&](Statement const& _statement) -> bool {
|
boost::range::remove_erase_if(_block.statements, [&](Statement const& _statement) -> bool {
|
||||||
return holds_alternative<Assignment>(_statement) && m_toRemove.count(&std::get<Assignment>(_statement));
|
if (auto const* assignment = std::get_if<Assignment>(&_statement))
|
||||||
|
{
|
||||||
|
if (m_toRemove.count(assignment))
|
||||||
|
return true;
|
||||||
|
// TODO: hack to generally get rid of assignments like a := a
|
||||||
|
if (assignment->variableNames.size() == 1)
|
||||||
|
{
|
||||||
|
if (auto const* identifier = std::get_if<Identifier>(assignment->value.get()))
|
||||||
|
if (identifier->name == assignment->variableNames.front().name)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
ASTModifier::operator()(_block);
|
ASTModifier::operator()(_block);
|
||||||
|
@ -37,6 +37,8 @@
|
|||||||
#include <libyul/optimiser/ExpressionJoiner.h>
|
#include <libyul/optimiser/ExpressionJoiner.h>
|
||||||
#include <libyul/optimiser/ExpressionInliner.h>
|
#include <libyul/optimiser/ExpressionInliner.h>
|
||||||
#include <libyul/optimiser/FullInliner.h>
|
#include <libyul/optimiser/FullInliner.h>
|
||||||
|
#include <libyul/optimiser/FullSSAReverse.h>
|
||||||
|
#include <libyul/optimiser/FullSSATransform.h>
|
||||||
#include <libyul/optimiser/ForLoopConditionIntoBody.h>
|
#include <libyul/optimiser/ForLoopConditionIntoBody.h>
|
||||||
#include <libyul/optimiser/ForLoopConditionOutOfBody.h>
|
#include <libyul/optimiser/ForLoopConditionOutOfBody.h>
|
||||||
#include <libyul/optimiser/ForLoopInitRewriter.h>
|
#include <libyul/optimiser/ForLoopInitRewriter.h>
|
||||||
@ -190,6 +192,8 @@ map<string, unique_ptr<OptimiserStep>> const& OptimiserSuite::allSteps()
|
|||||||
ForLoopConditionOutOfBody,
|
ForLoopConditionOutOfBody,
|
||||||
ForLoopInitRewriter,
|
ForLoopInitRewriter,
|
||||||
FullInliner,
|
FullInliner,
|
||||||
|
FullSSAReverse,
|
||||||
|
FullSSATransform,
|
||||||
FunctionGrouper,
|
FunctionGrouper,
|
||||||
FunctionHoister,
|
FunctionHoister,
|
||||||
LiteralRematerialiser,
|
LiteralRematerialiser,
|
||||||
@ -229,6 +233,8 @@ map<string, char> const& OptimiserSuite::stepNameToAbbreviationMap()
|
|||||||
{ForLoopConditionOutOfBody::name, 'O'},
|
{ForLoopConditionOutOfBody::name, 'O'},
|
||||||
{ForLoopInitRewriter::name, 'o'},
|
{ForLoopInitRewriter::name, 'o'},
|
||||||
{FullInliner::name, 'i'},
|
{FullInliner::name, 'i'},
|
||||||
|
{FullSSAReverse::name, 'Z'},
|
||||||
|
{FullSSATransform::name, 'z'},
|
||||||
{FunctionGrouper::name, 'g'},
|
{FunctionGrouper::name, 'g'},
|
||||||
{FunctionHoister::name, 'h'},
|
{FunctionHoister::name, 'h'},
|
||||||
{LiteralRematerialiser::name, 'T'},
|
{LiteralRematerialiser::name, 'T'},
|
||||||
|
@ -40,6 +40,8 @@
|
|||||||
#include <libyul/optimiser/FunctionHoister.h>
|
#include <libyul/optimiser/FunctionHoister.h>
|
||||||
#include <libyul/optimiser/ExpressionInliner.h>
|
#include <libyul/optimiser/ExpressionInliner.h>
|
||||||
#include <libyul/optimiser/FullInliner.h>
|
#include <libyul/optimiser/FullInliner.h>
|
||||||
|
#include <libyul/optimiser/FullSSAReverse.h>
|
||||||
|
#include <libyul/optimiser/FullSSATransform.h>
|
||||||
#include <libyul/optimiser/ForLoopConditionIntoBody.h>
|
#include <libyul/optimiser/ForLoopConditionIntoBody.h>
|
||||||
#include <libyul/optimiser/ForLoopConditionOutOfBody.h>
|
#include <libyul/optimiser/ForLoopConditionOutOfBody.h>
|
||||||
#include <libyul/optimiser/ForLoopInitRewriter.h>
|
#include <libyul/optimiser/ForLoopInitRewriter.h>
|
||||||
@ -168,6 +170,38 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
|
|||||||
disambiguate();
|
disambiguate();
|
||||||
ForLoopInitRewriter::run(*m_context, *m_object->code);
|
ForLoopInitRewriter::run(*m_context, *m_object->code);
|
||||||
}
|
}
|
||||||
|
else if (m_optimizerStep == "fullSSATransform")
|
||||||
|
{
|
||||||
|
disambiguate();
|
||||||
|
ForLoopInitRewriter::run(*m_context, *m_object->code);
|
||||||
|
ForLoopConditionIntoBody::run(*m_context, *m_object->code);
|
||||||
|
FullSSATransform::run(*m_context, *m_object->code);
|
||||||
|
}
|
||||||
|
else if (m_optimizerStep == "fullSSAReverse")
|
||||||
|
{
|
||||||
|
disambiguate();
|
||||||
|
FullSSAReverse::run(*m_context, *m_object->code);
|
||||||
|
ForLoopConditionOutOfBody::run(*m_context, *m_object->code);
|
||||||
|
}
|
||||||
|
else if (m_optimizerStep == "fullSSAandBack")
|
||||||
|
{
|
||||||
|
disambiguate();
|
||||||
|
ForLoopInitRewriter::run(*m_context, *m_object->code);
|
||||||
|
ForLoopConditionIntoBody::run(*m_context, *m_object->code);
|
||||||
|
FullSSATransform::run(*m_context, *m_object->code);
|
||||||
|
|
||||||
|
UnusedPruner::run(*m_context, *m_object->code);
|
||||||
|
|
||||||
|
FullSSAReverse::run(*m_context, *m_object->code);
|
||||||
|
|
||||||
|
RedundantAssignEliminator::run(*m_context, *m_object->code);
|
||||||
|
CommonSubexpressionEliminator::run(*m_context, *m_object->code);
|
||||||
|
Rematerialiser::run(*m_context, *m_object->code);
|
||||||
|
UnusedPruner::run(*m_context, *m_object->code);
|
||||||
|
RedundantAssignEliminator::run(*m_context, *m_object->code);
|
||||||
|
|
||||||
|
ForLoopConditionOutOfBody::run(*m_context, *m_object->code);
|
||||||
|
}
|
||||||
else if (m_optimizerStep == "commonSubexpressionEliminator")
|
else if (m_optimizerStep == "commonSubexpressionEliminator")
|
||||||
{
|
{
|
||||||
disambiguate();
|
disambiguate();
|
||||||
|
13
test/libyul/yulOptimizerTests/fullSSAReverse/load.yul
Normal file
13
test/libyul/yulOptimizerTests/fullSSAReverse/load.yul
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
phi_store("a", 23)
|
||||||
|
let a := 42
|
||||||
|
sstore(0, phi_load("a"))
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSAReverse
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// let a_1 := 23
|
||||||
|
// let a := 42
|
||||||
|
// sstore(0, a_1)
|
||||||
|
// }
|
13
test/libyul/yulOptimizerTests/fullSSAReverse/store.yul
Normal file
13
test/libyul/yulOptimizerTests/fullSSAReverse/store.yul
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
phi_store("a", 42)
|
||||||
|
let b := 0
|
||||||
|
phi_store("b", 23)
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSAReverse
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// let a_1 := 42
|
||||||
|
// let b := 0
|
||||||
|
// let b_2 := 23
|
||||||
|
// }
|
28
test/libyul/yulOptimizerTests/fullSSATransform/for.yul
Normal file
28
test/libyul/yulOptimizerTests/fullSSATransform/for.yul
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
let a := calldataload(42)
|
||||||
|
for {} a { sstore(1, a) } {
|
||||||
|
a := sub(a,1)
|
||||||
|
}
|
||||||
|
sstore(0, a)
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSATransform
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// let a := calldataload(42)
|
||||||
|
// phi_store("a", a)
|
||||||
|
// for { }
|
||||||
|
// phi_load("a")
|
||||||
|
// {
|
||||||
|
// let a_3 := phi_load("a")
|
||||||
|
// sstore(1, a_3)
|
||||||
|
// phi_store("a", a_3)
|
||||||
|
// }
|
||||||
|
// {
|
||||||
|
// let a_1 := phi_load("a")
|
||||||
|
// let a_2 := sub(a_1, 1)
|
||||||
|
// phi_store("a", a_2)
|
||||||
|
// }
|
||||||
|
// let a_4 := phi_load("a")
|
||||||
|
// sstore(0, a_4)
|
||||||
|
// }
|
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
let a := calldataload(42)
|
||||||
|
for {} a { sstore(1, a) } {
|
||||||
|
a := sub(a,1)
|
||||||
|
if lt(a,4) { continue }
|
||||||
|
if eq(a,42) { break }
|
||||||
|
}
|
||||||
|
sstore(0, a)
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSATransform
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// let a := calldataload(42)
|
||||||
|
// phi_store("a", a)
|
||||||
|
// for { }
|
||||||
|
// phi_load("a")
|
||||||
|
// {
|
||||||
|
// let a_3 := phi_load("a")
|
||||||
|
// sstore(1, a_3)
|
||||||
|
// phi_store("a", a_3)
|
||||||
|
// }
|
||||||
|
// {
|
||||||
|
// let a_1 := phi_load("a")
|
||||||
|
// let a_2 := sub(a_1, 1)
|
||||||
|
// if lt(a_2, 4)
|
||||||
|
// {
|
||||||
|
// phi_store("a", a_2)
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// if eq(a_2, 42)
|
||||||
|
// {
|
||||||
|
// phi_store("a", a_2)
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// phi_store("a", a_2)
|
||||||
|
// }
|
||||||
|
// let a_4 := phi_load("a")
|
||||||
|
// sstore(0, a_4)
|
||||||
|
// }
|
@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
let a := 0
|
||||||
|
for {} lt(a, 10) {
|
||||||
|
// checked increment
|
||||||
|
if eq(a, sub(0, 1)) { revert(0, 0) }
|
||||||
|
a := add(a, 1)
|
||||||
|
} {
|
||||||
|
sstore(a, 42)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSATransform
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// let a := 0
|
||||||
|
// phi_store("a", a)
|
||||||
|
// for { }
|
||||||
|
// true
|
||||||
|
// {
|
||||||
|
// let a_2 := phi_load("a")
|
||||||
|
// if eq(a_2, sub(0, 1)) { revert(0, 0) }
|
||||||
|
// let a_3 := add(a_2, 1)
|
||||||
|
// phi_store("a", a_3)
|
||||||
|
// }
|
||||||
|
// {
|
||||||
|
// let a_1 := phi_load("a")
|
||||||
|
// if iszero(lt(a_1, 10))
|
||||||
|
// {
|
||||||
|
// phi_store("a", a_1)
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// sstore(a_1, 42)
|
||||||
|
// phi_store("a", a_1)
|
||||||
|
// }
|
||||||
|
// let a_4 := phi_load("a")
|
||||||
|
// }
|
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
let a := calldataload(42)
|
||||||
|
for {} a { for {} a { a := sub(a, 1) } { sstore(1, a) }} {
|
||||||
|
a := sub(a,1)
|
||||||
|
}
|
||||||
|
sstore(0, a)
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSATransform
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// let a := calldataload(42)
|
||||||
|
// phi_store("a", a)
|
||||||
|
// for { }
|
||||||
|
// phi_load("a")
|
||||||
|
// {
|
||||||
|
// let a_3 := phi_load("a")
|
||||||
|
// phi_store("a_3", a_3)
|
||||||
|
// for { }
|
||||||
|
// phi_load("a_3")
|
||||||
|
// {
|
||||||
|
// let a_5 := phi_load("a_3")
|
||||||
|
// let a_6 := sub(a_5, 1)
|
||||||
|
// phi_store("a_3", a_6)
|
||||||
|
// }
|
||||||
|
// {
|
||||||
|
// let a_4 := phi_load("a_3")
|
||||||
|
// sstore(1, a_4)
|
||||||
|
// phi_store("a_3", a_4)
|
||||||
|
// }
|
||||||
|
// let a_7 := phi_load("a_3")
|
||||||
|
// phi_store("a", a_7)
|
||||||
|
// }
|
||||||
|
// {
|
||||||
|
// let a_1 := phi_load("a")
|
||||||
|
// let a_2 := sub(a_1, 1)
|
||||||
|
// phi_store("a", a_2)
|
||||||
|
// }
|
||||||
|
// let a_8 := phi_load("a")
|
||||||
|
// sstore(0, a_8)
|
||||||
|
// }
|
25
test/libyul/yulOptimizerTests/fullSSATransform/function.yul
Normal file
25
test/libyul/yulOptimizerTests/fullSSATransform/function.yul
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
function f(a, b) -> c {
|
||||||
|
c := calldataload(0)
|
||||||
|
sstore(0, a)
|
||||||
|
a := b
|
||||||
|
sstore(1, b)
|
||||||
|
c := calldataload(1)
|
||||||
|
}
|
||||||
|
sstore(2, f(calldataload(2), calldataload(3)))
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSATransform
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// function f(a, b) -> c
|
||||||
|
// {
|
||||||
|
// let c_1 := calldataload(0)
|
||||||
|
// sstore(0, a)
|
||||||
|
// let a_2 := b
|
||||||
|
// sstore(1, b)
|
||||||
|
// let c_3 := calldataload(1)
|
||||||
|
// phi_store("c", c_3)
|
||||||
|
// }
|
||||||
|
// sstore(2, f(calldataload(2), calldataload(3)))
|
||||||
|
// }
|
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
function f(a, b) -> c {
|
||||||
|
c := calldataload(0)
|
||||||
|
for {} gt(a,0) { a := sub(a,b) } {
|
||||||
|
c := calldataload(a)
|
||||||
|
}
|
||||||
|
c := calldataload(c)
|
||||||
|
}
|
||||||
|
sstore(2, f(calldataload(2), calldataload(3)))
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSATransform
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// function f(a, b) -> c
|
||||||
|
// {
|
||||||
|
// let c_1 := calldataload(0)
|
||||||
|
// phi_store("c_1", c_1)
|
||||||
|
// phi_store("a", a)
|
||||||
|
// for { }
|
||||||
|
// true
|
||||||
|
// {
|
||||||
|
// let c_5 := phi_load("c_1")
|
||||||
|
// let a_6 := phi_load("a")
|
||||||
|
// let a_7 := sub(a_6, b)
|
||||||
|
// phi_store("c_1", c_5)
|
||||||
|
// phi_store("a", a_7)
|
||||||
|
// }
|
||||||
|
// {
|
||||||
|
// let c_2 := phi_load("c_1")
|
||||||
|
// let a_3 := phi_load("a")
|
||||||
|
// if iszero(gt(a_3, 0))
|
||||||
|
// {
|
||||||
|
// phi_store("c_1", c_2)
|
||||||
|
// phi_store("a", a_3)
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// let c_4 := calldataload(a_3)
|
||||||
|
// phi_store("c_1", c_4)
|
||||||
|
// phi_store("a", a_3)
|
||||||
|
// }
|
||||||
|
// let c_8 := phi_load("c_1")
|
||||||
|
// let a_9 := phi_load("a")
|
||||||
|
// let c_10 := calldataload(c_8)
|
||||||
|
// phi_store("c", c_10)
|
||||||
|
// }
|
||||||
|
// sstore(2, f(calldataload(2), calldataload(3)))
|
||||||
|
// }
|
@ -0,0 +1,56 @@
|
|||||||
|
{
|
||||||
|
function f(a, b) -> c {
|
||||||
|
c := calldataload(0)
|
||||||
|
for {} gt(a,0) { a := sub(a,b) } {
|
||||||
|
c := calldataload(a)
|
||||||
|
if lt(c, add(b, a)) {
|
||||||
|
leave
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c := calldataload(c)
|
||||||
|
}
|
||||||
|
sstore(2, f(calldataload(2), calldataload(3)))
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSATransform
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// function f(a, b) -> c
|
||||||
|
// {
|
||||||
|
// let c_1 := calldataload(0)
|
||||||
|
// phi_store("c_1", c_1)
|
||||||
|
// phi_store("a", a)
|
||||||
|
// for { }
|
||||||
|
// true
|
||||||
|
// {
|
||||||
|
// let c_5 := phi_load("c_1")
|
||||||
|
// let a_6 := phi_load("a")
|
||||||
|
// let a_7 := sub(a_6, b)
|
||||||
|
// phi_store("c_1", c_5)
|
||||||
|
// phi_store("a", a_7)
|
||||||
|
// }
|
||||||
|
// {
|
||||||
|
// let c_2 := phi_load("c_1")
|
||||||
|
// let a_3 := phi_load("a")
|
||||||
|
// if iszero(gt(a_3, 0))
|
||||||
|
// {
|
||||||
|
// phi_store("c_1", c_2)
|
||||||
|
// phi_store("a", a_3)
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// let c_4 := calldataload(a_3)
|
||||||
|
// if lt(c_4, add(b, a_3))
|
||||||
|
// {
|
||||||
|
// phi_store("c", c_4)
|
||||||
|
// leave
|
||||||
|
// }
|
||||||
|
// phi_store("c_1", c_4)
|
||||||
|
// phi_store("a", a_3)
|
||||||
|
// }
|
||||||
|
// let c_8 := phi_load("c_1")
|
||||||
|
// let a_9 := phi_load("a")
|
||||||
|
// let c_10 := calldataload(c_8)
|
||||||
|
// phi_store("c", c_10)
|
||||||
|
// }
|
||||||
|
// sstore(2, f(calldataload(2), calldataload(3)))
|
||||||
|
// }
|
21
test/libyul/yulOptimizerTests/fullSSATransform/if.yul
Normal file
21
test/libyul/yulOptimizerTests/fullSSATransform/if.yul
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
let a := 1
|
||||||
|
if calldataload(42) {
|
||||||
|
a := 2
|
||||||
|
}
|
||||||
|
sstore(0, a)
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSATransform
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// let a := 1
|
||||||
|
// phi_store("a", a)
|
||||||
|
// if calldataload(42)
|
||||||
|
// {
|
||||||
|
// let a_1 := 2
|
||||||
|
// phi_store("a", a_1)
|
||||||
|
// }
|
||||||
|
// let a_2 := phi_load("a")
|
||||||
|
// sstore(0, a_2)
|
||||||
|
// }
|
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
let a := 42
|
||||||
|
if calldataload(42) { a := 23 }
|
||||||
|
sstore(0, 42)
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSATransform
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// let a := 42
|
||||||
|
// phi_store("a", a)
|
||||||
|
// if calldataload(42)
|
||||||
|
// {
|
||||||
|
// let a_1 := 23
|
||||||
|
// phi_store("a", a_1)
|
||||||
|
// }
|
||||||
|
// let a_2 := phi_load("a")
|
||||||
|
// sstore(0, 42)
|
||||||
|
// }
|
31
test/libyul/yulOptimizerTests/fullSSATransform/if_twice.yul
Normal file
31
test/libyul/yulOptimizerTests/fullSSATransform/if_twice.yul
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
let a := 1
|
||||||
|
if calldataload(42) {
|
||||||
|
a := 2
|
||||||
|
}
|
||||||
|
if calldataload(a) {
|
||||||
|
a := add(a, 4)
|
||||||
|
}
|
||||||
|
sstore(0, a)
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSATransform
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// let a := 1
|
||||||
|
// phi_store("a", a)
|
||||||
|
// if calldataload(42)
|
||||||
|
// {
|
||||||
|
// let a_1 := 2
|
||||||
|
// phi_store("a", a_1)
|
||||||
|
// }
|
||||||
|
// let a_2 := phi_load("a")
|
||||||
|
// phi_store("a_2", a_2)
|
||||||
|
// if calldataload(a_2)
|
||||||
|
// {
|
||||||
|
// let a_3 := add(a_2, 4)
|
||||||
|
// phi_store("a_2", a_3)
|
||||||
|
// }
|
||||||
|
// let a_4 := phi_load("a_2")
|
||||||
|
// sstore(0, a_4)
|
||||||
|
// }
|
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
let a := 1
|
||||||
|
a := 2
|
||||||
|
sstore(0, a)
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSATransform
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// let a := 1
|
||||||
|
// let a_1 := 2
|
||||||
|
// sstore(0, a_1)
|
||||||
|
// }
|
35
test/libyul/yulOptimizerTests/fullSSATransform/switch.yul
Normal file
35
test/libyul/yulOptimizerTests/fullSSATransform/switch.yul
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
let a := 1
|
||||||
|
switch a
|
||||||
|
case 1
|
||||||
|
{
|
||||||
|
a := calldataload(1)
|
||||||
|
}
|
||||||
|
case 2
|
||||||
|
{
|
||||||
|
a := calldataload(a)
|
||||||
|
}
|
||||||
|
default
|
||||||
|
{
|
||||||
|
}
|
||||||
|
sstore(0, a)
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSATransform
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// let a := 1
|
||||||
|
// phi_store("a", a)
|
||||||
|
// switch a
|
||||||
|
// case 1 {
|
||||||
|
// let a_1 := calldataload(1)
|
||||||
|
// phi_store("a", a_1)
|
||||||
|
// }
|
||||||
|
// case 2 {
|
||||||
|
// let a_2 := calldataload(a)
|
||||||
|
// phi_store("a", a_2)
|
||||||
|
// }
|
||||||
|
// default { phi_store("a", a) }
|
||||||
|
// let a_3 := phi_load("a")
|
||||||
|
// sstore(0, a_3)
|
||||||
|
// }
|
16
test/libyul/yulOptimizerTests/fullSSAandBack/for.yul
Normal file
16
test/libyul/yulOptimizerTests/fullSSAandBack/for.yul
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
let a := calldataload(42)
|
||||||
|
for {} a { sstore(1, a) } {
|
||||||
|
a := sub(a,1)
|
||||||
|
}
|
||||||
|
sstore(0, a)
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSAandBack
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// let a_5 := calldataload(42)
|
||||||
|
// for { } a_5 { sstore(1, a_5) }
|
||||||
|
// { a_5 := sub(a_5, 1) }
|
||||||
|
// sstore(0, a_5)
|
||||||
|
// }
|
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
let a := calldataload(42)
|
||||||
|
for {} a { sstore(1, a) } {
|
||||||
|
a := sub(a,1)
|
||||||
|
if lt(a,4) { continue }
|
||||||
|
if eq(a,42) { break }
|
||||||
|
}
|
||||||
|
sstore(0, a)
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSAandBack
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// let a_5 := calldataload(42)
|
||||||
|
// for { } a_5 { sstore(1, a_5) }
|
||||||
|
// {
|
||||||
|
// let a_2 := sub(a_5, 1)
|
||||||
|
// if lt(a_2, 4)
|
||||||
|
// {
|
||||||
|
// a_5 := a_2
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// if eq(a_2, 42)
|
||||||
|
// {
|
||||||
|
// a_5 := a_2
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// a_5 := a_2
|
||||||
|
// }
|
||||||
|
// sstore(0, a_5)
|
||||||
|
// }
|
23
test/libyul/yulOptimizerTests/fullSSAandBack/for_nested.yul
Normal file
23
test/libyul/yulOptimizerTests/fullSSAandBack/for_nested.yul
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
let a := calldataload(42)
|
||||||
|
for {} a { for {} a { a := sub(a, 1) } { sstore(1, a) }} {
|
||||||
|
a := sub(a,1)
|
||||||
|
}
|
||||||
|
sstore(0, a)
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSAandBack
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// let a_9 := calldataload(42)
|
||||||
|
// for { }
|
||||||
|
// a_9
|
||||||
|
// {
|
||||||
|
// let a_3_10 := a_9
|
||||||
|
// for { } a_3_10 { a_3_10 := sub(a_3_10, 1) }
|
||||||
|
// { sstore(1, a_3_10) }
|
||||||
|
// a_9 := a_3_10
|
||||||
|
// }
|
||||||
|
// { a_9 := sub(a_9, 1) }
|
||||||
|
// sstore(0, a_9)
|
||||||
|
// }
|
22
test/libyul/yulOptimizerTests/fullSSAandBack/function.yul
Normal file
22
test/libyul/yulOptimizerTests/fullSSAandBack/function.yul
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
function f(a, b) -> c {
|
||||||
|
c := calldataload(0)
|
||||||
|
sstore(0, a)
|
||||||
|
a := b
|
||||||
|
sstore(1, b)
|
||||||
|
c := calldataload(1)
|
||||||
|
}
|
||||||
|
sstore(2, f(calldataload(2), calldataload(3)))
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSAandBack
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// function f(a, b) -> c_4
|
||||||
|
// {
|
||||||
|
// sstore(0, a)
|
||||||
|
// sstore(1, b)
|
||||||
|
// c_4 := calldataload(1)
|
||||||
|
// }
|
||||||
|
// sstore(2, f(calldataload(2), calldataload(3)))
|
||||||
|
// }
|
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
function f(a, b) -> c {
|
||||||
|
c := calldataload(0)
|
||||||
|
for {} gt(a,0) { a := sub(a,b) } {
|
||||||
|
c := calldataload(a)
|
||||||
|
}
|
||||||
|
c := calldataload(c)
|
||||||
|
}
|
||||||
|
sstore(2, f(calldataload(2), calldataload(3)))
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSAandBack
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// function f(a, b) -> c_11
|
||||||
|
// {
|
||||||
|
// let c_1_12 := calldataload(0)
|
||||||
|
// let a_13 := a
|
||||||
|
// for { } gt(a_13, c_11) { a_13 := sub(a_13, b) }
|
||||||
|
// { c_1_12 := calldataload(a_13) }
|
||||||
|
// c_11 := calldataload(c_1_12)
|
||||||
|
// }
|
||||||
|
// sstore(2, f(calldataload(2), calldataload(3)))
|
||||||
|
// }
|
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
function f(a, b) -> c {
|
||||||
|
c := calldataload(0)
|
||||||
|
for {} gt(a,0) { a := sub(a,b) } {
|
||||||
|
c := calldataload(a)
|
||||||
|
if lt(c, add(b, a)) {
|
||||||
|
leave
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c := calldataload(c)
|
||||||
|
}
|
||||||
|
sstore(2, f(calldataload(2), calldataload(3)))
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSAandBack
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// function f(a, b) -> c_11
|
||||||
|
// {
|
||||||
|
// let c_1_12 := calldataload(0)
|
||||||
|
// let a_13 := a
|
||||||
|
// for { } gt(a_13, 0) { a_13 := sub(a_13, b) }
|
||||||
|
// {
|
||||||
|
// let c_4 := calldataload(a_13)
|
||||||
|
// if lt(c_4, add(b, a_13))
|
||||||
|
// {
|
||||||
|
// c_11 := c_4
|
||||||
|
// leave
|
||||||
|
// }
|
||||||
|
// c_1_12 := c_4
|
||||||
|
// }
|
||||||
|
// c_11 := calldataload(c_1_12)
|
||||||
|
// }
|
||||||
|
// sstore(2, f(calldataload(2), calldataload(3)))
|
||||||
|
// }
|
15
test/libyul/yulOptimizerTests/fullSSAandBack/if.yul
Normal file
15
test/libyul/yulOptimizerTests/fullSSAandBack/if.yul
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
let a := 1
|
||||||
|
if calldataload(42) {
|
||||||
|
a := 2
|
||||||
|
}
|
||||||
|
sstore(0, a)
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSAandBack
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// let a_3 := 1
|
||||||
|
// if calldataload(42) { a_3 := 2 }
|
||||||
|
// sstore(0, a_3)
|
||||||
|
// }
|
20
test/libyul/yulOptimizerTests/fullSSAandBack/if_twice.yul
Normal file
20
test/libyul/yulOptimizerTests/fullSSAandBack/if_twice.yul
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
let a := 1
|
||||||
|
if calldataload(42) {
|
||||||
|
a := 2
|
||||||
|
}
|
||||||
|
if calldataload(a) {
|
||||||
|
a := add(a, 4)
|
||||||
|
}
|
||||||
|
sstore(0, a)
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSAandBack
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// let a_5 := 1
|
||||||
|
// if calldataload(42) { a_5 := 2 }
|
||||||
|
// let a_2_6 := a_5
|
||||||
|
// if calldataload(a_5) { a_2_6 := add(a_5, 4) }
|
||||||
|
// sstore(0, a_2_6)
|
||||||
|
// }
|
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
let a := calldataload(0)
|
||||||
|
a := calldataload(1)
|
||||||
|
sstore(0, a)
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: fullSSAandBack
|
||||||
|
//
|
||||||
|
// { sstore(0, calldataload(1)) }
|
@ -138,7 +138,7 @@ BOOST_AUTO_TEST_CASE(output_operator_should_create_concise_and_unambiguous_strin
|
|||||||
|
|
||||||
BOOST_TEST(chromosome.length() == allSteps.size());
|
BOOST_TEST(chromosome.length() == allSteps.size());
|
||||||
BOOST_TEST(chromosome.optimisationSteps() == allSteps);
|
BOOST_TEST(chromosome.optimisationSteps() == allSteps);
|
||||||
BOOST_TEST(toString(chromosome) == "flcCUnDvejsxIOoighTLMRrmVatpud");
|
BOOST_TEST(toString(chromosome) == "flcCUnDvejsxIOoiZzghTLMRrmVatpud");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(optimisationSteps_should_translate_chromosomes_genes_to_optimisation_step_names)
|
BOOST_AUTO_TEST_CASE(optimisationSteps_should_translate_chromosomes_genes_to_optimisation_step_names)
|
||||||
|
Loading…
Reference in New Issue
Block a user