mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Add global variables and support multi-return and multi-assignment.
This commit is contained in:
parent
61e36cbfaa
commit
c3705f268c
@ -30,24 +30,32 @@ namespace wasm
|
|||||||
{
|
{
|
||||||
|
|
||||||
struct Literal;
|
struct Literal;
|
||||||
struct Identifier;
|
struct LocalVariable;
|
||||||
|
struct GlobalVariable;
|
||||||
struct Label;
|
struct Label;
|
||||||
struct FunctionCall;
|
struct FunctionCall;
|
||||||
struct BuiltinCall;
|
struct BuiltinCall;
|
||||||
struct LocalAssignment;
|
struct LocalAssignment;
|
||||||
|
struct GlobalAssignment;
|
||||||
struct Block;
|
struct Block;
|
||||||
struct If;
|
struct If;
|
||||||
struct Loop;
|
struct Loop;
|
||||||
struct Break;
|
struct Break;
|
||||||
struct Continue;
|
struct Continue;
|
||||||
using Expression = boost::variant<Literal, Identifier, Label, FunctionCall, BuiltinCall, LocalAssignment, Block, If, Loop, Break, Continue>;
|
using Expression = boost::variant<
|
||||||
|
Literal, LocalVariable, GlobalVariable, Label,
|
||||||
|
FunctionCall, BuiltinCall, LocalAssignment, GlobalAssignment,
|
||||||
|
Block, If, Loop, Break, Continue
|
||||||
|
>;
|
||||||
|
|
||||||
struct Literal { uint64_t value; };
|
struct Literal { uint64_t value; };
|
||||||
struct Identifier { std::string name; };
|
struct LocalVariable { std::string name; };
|
||||||
|
struct GlobalVariable { std::string name; };
|
||||||
struct Label { std::string name; };
|
struct Label { std::string name; };
|
||||||
struct FunctionCall { std::string functionName; std::vector<Expression> arguments; };
|
struct FunctionCall { std::string functionName; std::vector<Expression> arguments; };
|
||||||
struct BuiltinCall { std::string functionName; std::vector<Expression> arguments; };
|
struct BuiltinCall { std::string functionName; std::vector<Expression> arguments; };
|
||||||
struct LocalAssignment { std::string variableName; std::unique_ptr<Expression> value; };
|
struct LocalAssignment { std::string variableName; std::unique_ptr<Expression> value; };
|
||||||
|
struct GlobalAssignment { std::string variableName; std::unique_ptr<Expression> value; };
|
||||||
struct Block { std::string labelName; std::vector<Expression> statements; };
|
struct Block { std::string labelName; std::vector<Expression> statements; };
|
||||||
struct If { std::unique_ptr<Expression> condition; std::vector<Expression> statements; };
|
struct If { std::unique_ptr<Expression> condition; std::vector<Expression> statements; };
|
||||||
struct Loop { std::string labelName; std::vector<Expression> statements; };
|
struct Loop { std::string labelName; std::vector<Expression> statements; };
|
||||||
@ -55,6 +63,8 @@ struct Break { Label label; };
|
|||||||
struct Continue { Label label; };
|
struct Continue { Label label; };
|
||||||
|
|
||||||
struct VariableDeclaration { std::string variableName; };
|
struct VariableDeclaration { std::string variableName; };
|
||||||
|
struct GlobalVariableDeclaration { std::string variableName; };
|
||||||
|
|
||||||
struct FunctionDefinition
|
struct FunctionDefinition
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include <libyul/backends/wasm/EWasmCodeTransform.h>
|
#include <libyul/backends/wasm/EWasmCodeTransform.h>
|
||||||
|
|
||||||
#include <libyul/backends/wasm/EWasmToText.h>
|
#include <libyul/backends/wasm/EWasmToText.h>
|
||||||
|
#include <libyul/optimiser/NameCollector.h>
|
||||||
|
|
||||||
#include <libyul/AsmData.h>
|
#include <libyul/AsmData.h>
|
||||||
#include <libyul/Dialect.h>
|
#include <libyul/Dialect.h>
|
||||||
@ -30,50 +31,68 @@
|
|||||||
#include <liblangutil/Exceptions.h>
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
|
#include <boost/range/adaptor/transformed.hpp>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace dev;
|
||||||
using namespace yul;
|
using namespace yul;
|
||||||
|
|
||||||
string EWasmCodeTransform::run(yul::Block const& _ast)
|
string EWasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _ast)
|
||||||
{
|
{
|
||||||
|
EWasmCodeTransform transform(_dialect, _ast);
|
||||||
vector<wasm::FunctionDefinition> functions;
|
vector<wasm::FunctionDefinition> functions;
|
||||||
|
|
||||||
for (auto const& statement: _ast.statements)
|
for (auto const& statement: _ast.statements)
|
||||||
{
|
{
|
||||||
yulAssert(statement.type() == typeid(yul::FunctionDefinition), "");
|
yulAssert(statement.type() == typeid(yul::FunctionDefinition), "");
|
||||||
functions.emplace_back(translateFunction(boost::get<yul::FunctionDefinition>(statement)));
|
functions.emplace_back(transform.translateFunction(boost::get<yul::FunctionDefinition>(statement)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return EWasmToText{}.run(functions);
|
return EWasmToText{}.run(transform.m_globalVariables, functions);
|
||||||
|
}
|
||||||
|
|
||||||
|
wasm::Expression EWasmCodeTransform::generateMultiAssignment(
|
||||||
|
vector<string> _variableNames,
|
||||||
|
unique_ptr<wasm::Expression> _firstValue
|
||||||
|
)
|
||||||
|
{
|
||||||
|
yulAssert(!_variableNames.empty(), "");
|
||||||
|
wasm::LocalAssignment assignment{std::move(_variableNames.front()), std::move(_firstValue)};
|
||||||
|
|
||||||
|
if (_variableNames.size() == 1)
|
||||||
|
return move(assignment);
|
||||||
|
|
||||||
|
wasm::Block block;
|
||||||
|
block.statements.emplace_back(std::move(assignment));
|
||||||
|
for (size_t i = 1; i < _variableNames.size(); ++i)
|
||||||
|
block.statements.emplace_back(wasm::LocalAssignment{
|
||||||
|
std::move(_variableNames.at(i)),
|
||||||
|
make_unique<wasm::Expression>(wasm::GlobalVariable{m_globalVariables.at(i - 1).variableName})
|
||||||
|
});
|
||||||
|
return std::move(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm::Expression EWasmCodeTransform::operator()(VariableDeclaration const& _varDecl)
|
wasm::Expression EWasmCodeTransform::operator()(VariableDeclaration const& _varDecl)
|
||||||
{
|
{
|
||||||
|
vector<string> variableNames;
|
||||||
for (auto const& var: _varDecl.variables)
|
for (auto const& var: _varDecl.variables)
|
||||||
m_localVariables.emplace_back(wasm::VariableDeclaration{var.name.str()});
|
{
|
||||||
|
variableNames.emplace_back(var.name.str());
|
||||||
|
m_localVariables.emplace_back(wasm::VariableDeclaration{variableNames.back()});
|
||||||
|
}
|
||||||
|
|
||||||
if (_varDecl.value)
|
if (_varDecl.value)
|
||||||
{
|
return generateMultiAssignment(std::move(variableNames), visit(*_varDecl.value));
|
||||||
// TODO otherwise, we have to work with globals.
|
|
||||||
solUnimplementedAssert(_varDecl.variables.size() == 1, "Only single-variable assignments supported.");
|
|
||||||
return wasm::LocalAssignment{
|
|
||||||
_varDecl.variables.front().name.str(),
|
|
||||||
visit(*_varDecl.value)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
// TODO this could be handled better.
|
|
||||||
return wasm::BuiltinCall{"nop", {}};
|
return wasm::BuiltinCall{"nop", {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm::Expression EWasmCodeTransform::operator()(Assignment const& _assignment)
|
wasm::Expression EWasmCodeTransform::operator()(Assignment const& _assignment)
|
||||||
{
|
{
|
||||||
solUnimplementedAssert(_assignment.variableNames.size() == 1, "Only single-variable assignments supported.");
|
vector<string> variableNames;
|
||||||
return wasm::LocalAssignment{
|
for (auto const& var: _assignment.variableNames)
|
||||||
_assignment.variableNames.front().name.str(),
|
variableNames.emplace_back(var.name.str());
|
||||||
visit(*_assignment.value)
|
return generateMultiAssignment(std::move(variableNames), visit(*_assignment.value));
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm::Expression EWasmCodeTransform::operator()(StackAssignment const&)
|
wasm::Expression EWasmCodeTransform::operator()(StackAssignment const&)
|
||||||
@ -104,12 +123,16 @@ wasm::Expression EWasmCodeTransform::operator()(FunctionCall const& _call)
|
|||||||
if (m_dialect.builtin(_call.functionName.name))
|
if (m_dialect.builtin(_call.functionName.name))
|
||||||
return wasm::BuiltinCall{_call.functionName.name.str(), visit(_call.arguments)};
|
return wasm::BuiltinCall{_call.functionName.name.str(), visit(_call.arguments)};
|
||||||
else
|
else
|
||||||
|
// If this function returns multiple values, then the first one will
|
||||||
|
// be returned in the expression itself and the others in global variables.
|
||||||
|
// The values have to be used right away in an assignment or variable declaration,
|
||||||
|
// so it is handled there.
|
||||||
return wasm::FunctionCall{_call.functionName.name.str(), visit(_call.arguments)};
|
return wasm::FunctionCall{_call.functionName.name.str(), visit(_call.arguments)};
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm::Expression EWasmCodeTransform::operator()(Identifier const& _identifier)
|
wasm::Expression EWasmCodeTransform::operator()(Identifier const& _identifier)
|
||||||
{
|
{
|
||||||
return wasm::Identifier{_identifier.name.str()};
|
return wasm::LocalVariable{_identifier.name.str()};
|
||||||
}
|
}
|
||||||
|
|
||||||
wasm::Expression EWasmCodeTransform::operator()(Literal const& _literal)
|
wasm::Expression EWasmCodeTransform::operator()(Literal const& _literal)
|
||||||
@ -121,7 +144,7 @@ wasm::Expression EWasmCodeTransform::operator()(Literal const& _literal)
|
|||||||
|
|
||||||
wasm::Expression EWasmCodeTransform::operator()(yul::Instruction const&)
|
wasm::Expression EWasmCodeTransform::operator()(yul::Instruction const&)
|
||||||
{
|
{
|
||||||
yulAssert(false, "");
|
yulAssert(false, "EVM instruction used for Wasm code generation.");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,14 +252,31 @@ wasm::FunctionDefinition EWasmCodeTransform::translateFunction(yul::FunctionDefi
|
|||||||
fun.locals += m_localVariables;
|
fun.locals += m_localVariables;
|
||||||
|
|
||||||
m_localVariables.clear();
|
m_localVariables.clear();
|
||||||
yulAssert(_fun.returnVariables.size() <= 1, "");
|
|
||||||
if (_fun.returnVariables.size() == 1)
|
if (!_fun.returnVariables.empty())
|
||||||
fun.body.emplace_back(wasm::Identifier{_fun.returnVariables.front().name.str()});
|
{
|
||||||
|
// First return variable is returned directly, the others are stored
|
||||||
|
// in globals.
|
||||||
|
allocateGlobals(_fun.returnVariables.size() - 1);
|
||||||
|
for (size_t i = 1; i < _fun.returnVariables.size(); ++i)
|
||||||
|
fun.body.emplace_back(wasm::GlobalAssignment{
|
||||||
|
m_globalVariables.at(i - 1).variableName,
|
||||||
|
make_unique<wasm::Expression>(wasm::LocalVariable{_fun.returnVariables.at(i).name.str()})
|
||||||
|
});
|
||||||
|
fun.body.emplace_back(wasm::LocalVariable{_fun.returnVariables.front().name.str()});
|
||||||
|
}
|
||||||
return fun;
|
return fun;
|
||||||
}
|
}
|
||||||
|
|
||||||
string EWasmCodeTransform::newLabel()
|
string EWasmCodeTransform::newLabel()
|
||||||
{
|
{
|
||||||
// TODO this should not clash with other identifiers!
|
return m_nameDispenser.newName("label_"_yulstring).str();
|
||||||
return "label_" + to_string(++m_labelCounter);
|
}
|
||||||
|
|
||||||
|
void EWasmCodeTransform::allocateGlobals(size_t _amount)
|
||||||
|
{
|
||||||
|
while (m_globalVariables.size() < _amount)
|
||||||
|
m_globalVariables.emplace_back(wasm::GlobalVariableDeclaration{
|
||||||
|
m_nameDispenser.newName("global_"_yulstring).str()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <libyul/backends/wasm/EWasmAST.h>
|
#include <libyul/backends/wasm/EWasmAST.h>
|
||||||
#include <libyul/AsmDataForward.h>
|
#include <libyul/AsmDataForward.h>
|
||||||
#include <libyul/Dialect.h>
|
#include <libyul/Dialect.h>
|
||||||
|
#include <libyul/optimiser/NameDispenser.h>
|
||||||
|
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
|
||||||
@ -33,14 +34,7 @@ struct AsmAnalysisInfo;
|
|||||||
class EWasmCodeTransform: public boost::static_visitor<wasm::Expression>
|
class EWasmCodeTransform: public boost::static_visitor<wasm::Expression>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
EWasmCodeTransform(
|
static std::string run(Dialect const& _dialect, yul::Block const& _ast);
|
||||||
AsmAnalysisInfo&,
|
|
||||||
Dialect const& _dialect
|
|
||||||
):
|
|
||||||
m_dialect(_dialect)
|
|
||||||
{}
|
|
||||||
|
|
||||||
std::string run(yul::Block const& _ast);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
wasm::Expression operator()(yul::Instruction const& _instruction);
|
wasm::Expression operator()(yul::Instruction const& _instruction);
|
||||||
@ -62,21 +56,41 @@ public:
|
|||||||
wasm::Expression operator()(yul::Block const& _block);
|
wasm::Expression operator()(yul::Block const& _block);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
EWasmCodeTransform(
|
||||||
|
Dialect const& _dialect,
|
||||||
|
Block const& _ast
|
||||||
|
):
|
||||||
|
m_dialect(_dialect),
|
||||||
|
m_nameDispenser(_dialect, _ast)
|
||||||
|
{}
|
||||||
|
|
||||||
std::unique_ptr<wasm::Expression> visit(yul::Expression const& _expression);
|
std::unique_ptr<wasm::Expression> visit(yul::Expression const& _expression);
|
||||||
wasm::Expression visitReturnByValue(yul::Expression const& _expression);
|
wasm::Expression visitReturnByValue(yul::Expression const& _expression);
|
||||||
std::vector<wasm::Expression> visit(std::vector<yul::Expression> const& _expressions);
|
std::vector<wasm::Expression> visit(std::vector<yul::Expression> const& _expressions);
|
||||||
wasm::Expression visit(yul::Statement const& _statement);
|
wasm::Expression visit(yul::Statement const& _statement);
|
||||||
std::vector<wasm::Expression> visit(std::vector<yul::Statement> const& _statements);
|
std::vector<wasm::Expression> visit(std::vector<yul::Statement> const& _statements);
|
||||||
|
|
||||||
|
/// Returns an assignment or a block containing multiple assignments.
|
||||||
|
/// @param _variableNames the names of the variables to assign to
|
||||||
|
/// @param _firstValue the value to be assigned to the first variable. If there
|
||||||
|
/// is more than one variable, the values are taken from m_globalVariables.
|
||||||
|
wasm::Expression generateMultiAssignment(
|
||||||
|
std::vector<std::string> _variableNames,
|
||||||
|
std::unique_ptr<wasm::Expression> _firstValue
|
||||||
|
);
|
||||||
|
|
||||||
wasm::FunctionDefinition translateFunction(yul::FunctionDefinition const& _funDef);
|
wasm::FunctionDefinition translateFunction(yul::FunctionDefinition const& _funDef);
|
||||||
|
|
||||||
std::string newLabel();
|
std::string newLabel();
|
||||||
|
/// Makes sure that there are at least @a _amount global variables.
|
||||||
std::vector<wasm::VariableDeclaration> m_localVariables;
|
void allocateGlobals(size_t _amount);
|
||||||
size_t m_labelCounter = 0;
|
|
||||||
std::stack<std::pair<std::string, std::string>> m_breakContinueLabelNames;
|
|
||||||
|
|
||||||
Dialect const& m_dialect;
|
Dialect const& m_dialect;
|
||||||
|
NameDispenser m_nameDispenser;
|
||||||
|
|
||||||
|
std::vector<wasm::VariableDeclaration> m_localVariables;
|
||||||
|
std::vector<wasm::GlobalVariableDeclaration> m_globalVariables;
|
||||||
|
std::stack<std::pair<std::string, std::string>> m_breakContinueLabelNames;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ string EWasmObjectCompiler::run(Object& _object)
|
|||||||
|
|
||||||
yulAssert(_object.analysisInfo, "No analysis info.");
|
yulAssert(_object.analysisInfo, "No analysis info.");
|
||||||
yulAssert(_object.code, "No code.");
|
yulAssert(_object.code, "No code.");
|
||||||
ret += EWasmCodeTransform{*_object.analysisInfo, m_dialect}.run(*_object.code);
|
ret += EWasmCodeTransform::run(m_dialect, *_object.code);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -29,9 +29,14 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace yul;
|
using namespace yul;
|
||||||
|
|
||||||
string EWasmToText::run(vector<wasm::FunctionDefinition> const& _functions)
|
string EWasmToText::run(
|
||||||
|
vector<wasm::GlobalVariableDeclaration> const& _globals,
|
||||||
|
vector<wasm::FunctionDefinition> const& _functions
|
||||||
|
)
|
||||||
{
|
{
|
||||||
string ret = "(module\n\n";
|
string ret = "(module\n\n";
|
||||||
|
for (auto const& g: _globals)
|
||||||
|
ret += " (global $" + g.variableName + " (mut i64) (i64.const 0))\n";
|
||||||
for (auto const& f: _functions)
|
for (auto const& f: _functions)
|
||||||
ret += transform(f) + "\n";
|
ret += transform(f) + "\n";
|
||||||
return move(ret) + ")\n";
|
return move(ret) + ")\n";
|
||||||
@ -42,11 +47,16 @@ string EWasmToText::operator()(wasm::Literal const& _literal)
|
|||||||
return "(i64.const " + to_string(_literal.value) + ")";
|
return "(i64.const " + to_string(_literal.value) + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
string EWasmToText::operator()(wasm::Identifier const& _identifier)
|
string EWasmToText::operator()(wasm::LocalVariable const& _identifier)
|
||||||
{
|
{
|
||||||
return "(get_local $" + _identifier.name + ")";
|
return "(get_local $" + _identifier.name + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string EWasmToText::operator()(wasm::GlobalVariable const& _identifier)
|
||||||
|
{
|
||||||
|
return "(get_global $" + _identifier.name + ")";
|
||||||
|
}
|
||||||
|
|
||||||
string EWasmToText::operator()(wasm::Label const& _label)
|
string EWasmToText::operator()(wasm::Label const& _label)
|
||||||
{
|
{
|
||||||
return "$" + _label.name;
|
return "$" + _label.name;
|
||||||
@ -67,6 +77,11 @@ string EWasmToText::operator()(wasm::LocalAssignment const& _assignment)
|
|||||||
return "(set_local $" + _assignment.variableName + " " + visit(*_assignment.value) + ")\n";
|
return "(set_local $" + _assignment.variableName + " " + visit(*_assignment.value) + ")\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string EWasmToText::operator()(wasm::GlobalAssignment const& _assignment)
|
||||||
|
{
|
||||||
|
return "(set_global $" + _assignment.variableName + " " + visit(*_assignment.value) + ")\n";
|
||||||
|
}
|
||||||
|
|
||||||
string EWasmToText::operator()(wasm::If const& _if)
|
string EWasmToText::operator()(wasm::If const& _if)
|
||||||
{
|
{
|
||||||
return "(if " + visit(*_if.condition) + " (then\n" + indented(joinTransformed(_if.statements)) + "\n))\n";
|
return "(if " + visit(*_if.condition) + " (then\n" + indented(joinTransformed(_if.statements)) + "\n))\n";
|
||||||
|
@ -31,15 +31,20 @@ struct AsmAnalysisInfo;
|
|||||||
class EWasmToText: public boost::static_visitor<std::string>
|
class EWasmToText: public boost::static_visitor<std::string>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::string run(std::vector<wasm::FunctionDefinition> const& _functions);
|
std::string run(
|
||||||
|
std::vector<wasm::GlobalVariableDeclaration> const& _globals,
|
||||||
|
std::vector<wasm::FunctionDefinition> const& _functions
|
||||||
|
);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::string operator()(wasm::Literal const& _literal);
|
std::string operator()(wasm::Literal const& _literal);
|
||||||
std::string operator()(wasm::Identifier const& _identifier);
|
std::string operator()(wasm::LocalVariable const& _identifier);
|
||||||
|
std::string operator()(wasm::GlobalVariable const& _identifier);
|
||||||
std::string operator()(wasm::Label const& _label);
|
std::string operator()(wasm::Label const& _label);
|
||||||
std::string operator()(wasm::BuiltinCall const& _builinCall);
|
std::string operator()(wasm::BuiltinCall const& _builinCall);
|
||||||
std::string operator()(wasm::FunctionCall const& _functionCall);
|
std::string operator()(wasm::FunctionCall const& _functionCall);
|
||||||
std::string operator()(wasm::LocalAssignment const& _assignment);
|
std::string operator()(wasm::LocalAssignment const& _assignment);
|
||||||
|
std::string operator()(wasm::GlobalAssignment const& _assignment);
|
||||||
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);
|
||||||
|
@ -2,3 +2,4 @@ iff
|
|||||||
nd
|
nd
|
||||||
assignend
|
assignend
|
||||||
uint
|
uint
|
||||||
|
mut
|
||||||
|
Loading…
Reference in New Issue
Block a user