Remove the expression callback from the code generator functions of Yul builtins.

This commit is contained in:
Daniel Kirchner 2021-09-03 15:55:17 +02:00
parent be95a8172b
commit a683ea7646
4 changed files with 38 additions and 79 deletions

View File

@ -239,9 +239,13 @@ void CodeTransform::operator()(FunctionCall const& _call)
m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_call.debugData)); m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_call.debugData));
if (BuiltinFunctionForEVM const* builtin = m_dialect.builtin(_call.functionName.name)) if (BuiltinFunctionForEVM const* builtin = m_dialect.builtin(_call.functionName.name))
builtin->generateCode(_call, m_assembly, m_builtinContext, [&](Expression const& _expression) { {
visitExpression(_expression); for (auto&& [i, arg]: _call.arguments | ranges::views::enumerate | ranges::views::reverse)
}); if (!builtin->literalArgument(i))
visitExpression(arg);
m_assembly.setSourceLocation(extractSourceLocationFromDebugData(_call.debugData));
builtin->generateCode(_call, m_assembly, m_builtinContext);
}
else else
{ {
AbstractAssembly::LabelID returnLabel = m_assembly.newLabelId(); AbstractAssembly::LabelID returnLabel = m_assembly.newLabelId();

View File

@ -26,8 +26,8 @@
#include <libyul/Object.h> #include <libyul/Object.h>
#include <libyul/Exceptions.h> #include <libyul/Exceptions.h>
#include <libyul/AsmParser.h> #include <libyul/AsmParser.h>
#include <libyul/Utilities.h>
#include <libyul/backends/evm/AbstractAssembly.h> #include <libyul/backends/evm/AbstractAssembly.h>
#include <libevmasm/SemanticInformation.h> #include <libevmasm/SemanticInformation.h>
#include <libevmasm/Instruction.h> #include <libevmasm/Instruction.h>
@ -46,19 +46,6 @@ using namespace solidity::util;
namespace namespace
{ {
void visitArguments(
AbstractAssembly& _assembly,
FunctionCall const& _call,
function<void(Expression const&)> _visitExpression
)
{
for (auto const& arg: _call.arguments | ranges::views::reverse)
_visitExpression(arg);
_assembly.setSourceLocation(_call.debugData->location);
}
pair<YulString, BuiltinFunctionForEVM> createEVMFunction( pair<YulString, BuiltinFunctionForEVM> createEVMFunction(
string const& _name, string const& _name,
evmasm::Instruction _instruction evmasm::Instruction _instruction
@ -76,12 +63,10 @@ pair<YulString, BuiltinFunctionForEVM> createEVMFunction(
f.literalArguments.clear(); f.literalArguments.clear();
f.instruction = _instruction; f.instruction = _instruction;
f.generateCode = [_instruction]( f.generateCode = [_instruction](
FunctionCall const& _call, FunctionCall const&,
AbstractAssembly& _assembly, AbstractAssembly& _assembly,
BuiltinContext&, BuiltinContext&
std::function<void(Expression const&)> _visitExpression
) { ) {
visitArguments(_assembly, _call, _visitExpression);
_assembly.appendInstruction(_instruction); _assembly.appendInstruction(_instruction);
}; };
@ -94,7 +79,7 @@ pair<YulString, BuiltinFunctionForEVM> createFunction(
size_t _returns, size_t _returns,
SideEffects _sideEffects, SideEffects _sideEffects,
vector<optional<LiteralKind>> _literalArguments, vector<optional<LiteralKind>> _literalArguments,
std::function<void(FunctionCall const&, AbstractAssembly&, BuiltinContext&, std::function<void(Expression const&)>)> _generateCode std::function<void(FunctionCall const&, AbstractAssembly&, BuiltinContext&)> _generateCode
) )
{ {
yulAssert(_literalArguments.size() == _params || _literalArguments.empty(), ""); yulAssert(_literalArguments.size() == _params || _literalArguments.empty(), "");
@ -166,12 +151,10 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
builtins.emplace(createFunction("linkersymbol", 1, 1, SideEffects{}, {LiteralKind::String}, []( builtins.emplace(createFunction("linkersymbol", 1, 1, SideEffects{}, {LiteralKind::String}, [](
FunctionCall const& _call, FunctionCall const& _call,
AbstractAssembly& _assembly, AbstractAssembly& _assembly,
BuiltinContext&, BuiltinContext&
function<void(Expression const&)>
) { ) {
yulAssert(_call.arguments.size() == 1, ""); yulAssert(_call.arguments.size() == 1, "");
Expression const& arg = _call.arguments.front(); Expression const& arg = _call.arguments.front();
_assembly.setSourceLocation(_call.debugData->location);
_assembly.appendLinkerSymbol(std::get<Literal>(arg).value.str()); _assembly.appendLinkerSymbol(std::get<Literal>(arg).value.str());
})); }));
@ -184,24 +167,24 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
[]( [](
FunctionCall const& _call, FunctionCall const& _call,
AbstractAssembly& _assembly, AbstractAssembly& _assembly,
BuiltinContext&, BuiltinContext&
function<void(Expression const&)> _visitExpression
) { ) {
visitArguments(_assembly, _call, _visitExpression); yulAssert(_call.arguments.size() == 1, "");
Literal const* literal = get_if<Literal>(&_call.arguments.front());
yulAssert(literal, "");
_assembly.appendConstant(valueOfLiteral(*literal));
}) })
); );
builtins.emplace(createFunction("datasize", 1, 1, SideEffects{}, {LiteralKind::String}, []( builtins.emplace(createFunction("datasize", 1, 1, SideEffects{}, {LiteralKind::String}, [](
FunctionCall const& _call, FunctionCall const& _call,
AbstractAssembly& _assembly, AbstractAssembly& _assembly,
BuiltinContext& _context, BuiltinContext& _context
std::function<void(Expression const&)> const&
) { ) {
yulAssert(_context.currentObject, "No object available."); yulAssert(_context.currentObject, "No object available.");
yulAssert(_call.arguments.size() == 1, ""); yulAssert(_call.arguments.size() == 1, "");
Expression const& arg = _call.arguments.front(); Expression const& arg = _call.arguments.front();
YulString dataName = std::get<Literal>(arg).value; YulString dataName = std::get<Literal>(arg).value;
_assembly.setSourceLocation(_call.debugData->location);
if (_context.currentObject->name == dataName) if (_context.currentObject->name == dataName)
_assembly.appendAssemblySize(); _assembly.appendAssemblySize();
else else
@ -217,14 +200,12 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
builtins.emplace(createFunction("dataoffset", 1, 1, SideEffects{}, {LiteralKind::String}, []( builtins.emplace(createFunction("dataoffset", 1, 1, SideEffects{}, {LiteralKind::String}, [](
FunctionCall const& _call, FunctionCall const& _call,
AbstractAssembly& _assembly, AbstractAssembly& _assembly,
BuiltinContext& _context, BuiltinContext& _context
std::function<void(Expression const&)> const&
) { ) {
yulAssert(_context.currentObject, "No object available."); yulAssert(_context.currentObject, "No object available.");
yulAssert(_call.arguments.size() == 1, ""); yulAssert(_call.arguments.size() == 1, "");
Expression const& arg = _call.arguments.front(); Expression const& arg = _call.arguments.front();
YulString dataName = std::get<Literal>(arg).value; YulString dataName = std::get<Literal>(arg).value;
_assembly.setSourceLocation(_call.debugData->location);
if (_context.currentObject->name == dataName) if (_context.currentObject->name == dataName)
_assembly.appendConstant(0); _assembly.appendConstant(0);
else else
@ -244,12 +225,10 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
SideEffects{false, true, false, false, true, SideEffects::None, SideEffects::None, SideEffects::Write}, SideEffects{false, true, false, false, true, SideEffects::None, SideEffects::None, SideEffects::Write},
{}, {},
[]( [](
FunctionCall const& _call, FunctionCall const&,
AbstractAssembly& _assembly, AbstractAssembly& _assembly,
BuiltinContext&, BuiltinContext&
std::function<void(Expression const&)> _visitExpression
) { ) {
visitArguments(_assembly, _call, _visitExpression);
_assembly.appendInstruction(evmasm::Instruction::CODECOPY); _assembly.appendInstruction(evmasm::Instruction::CODECOPY);
} }
)); ));
@ -262,15 +241,10 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
[]( [](
FunctionCall const& _call, FunctionCall const& _call,
AbstractAssembly& _assembly, AbstractAssembly& _assembly,
BuiltinContext&, BuiltinContext&
std::function<void(Expression const&)> _visitExpression
) { ) {
yulAssert(_call.arguments.size() == 3, ""); yulAssert(_call.arguments.size() == 3, "");
_visitExpression(_call.arguments[2]);
YulString identifier = std::get<Literal>(_call.arguments[1]).value; YulString identifier = std::get<Literal>(_call.arguments[1]).value;
_visitExpression(_call.arguments[0]);
_assembly.setSourceLocation(_call.debugData->location);
_assembly.appendImmutableAssignment(identifier.str()); _assembly.appendImmutableAssignment(identifier.str());
} }
)); ));
@ -283,11 +257,9 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
[]( [](
FunctionCall const& _call, FunctionCall const& _call,
AbstractAssembly& _assembly, AbstractAssembly& _assembly,
BuiltinContext&, BuiltinContext&
std::function<void(Expression const&)>
) { ) {
yulAssert(_call.arguments.size() == 1, ""); yulAssert(_call.arguments.size() == 1, "");
_assembly.setSourceLocation(_call.debugData->location);
_assembly.appendImmutable(std::get<Literal>(_call.arguments.front()).value.str()); _assembly.appendImmutable(std::get<Literal>(_call.arguments.front()).value.str());
} }
)); ));
@ -387,15 +359,11 @@ BuiltinFunctionForEVM const* EVMDialect::verbatimFunction(size_t _arguments, siz
[=]( [=](
FunctionCall const& _call, FunctionCall const& _call,
AbstractAssembly& _assembly, AbstractAssembly& _assembly,
BuiltinContext&, BuiltinContext&
std::function<void(Expression const&)> _visitExpression
) { ) {
yulAssert(_call.arguments.size() == (1 + _arguments), ""); yulAssert(_call.arguments.size() == (1 + _arguments), "");
for (Expression const& arg: _call.arguments | ranges::views::tail | ranges::views::reverse)
_visitExpression(arg);
Expression const& bytecode = _call.arguments.front(); Expression const& bytecode = _call.arguments.front();
_assembly.setSourceLocation(_call.debugData->location);
_assembly.appendVerbatim( _assembly.appendVerbatim(
asBytes(std::get<Literal>(bytecode).value.str()), asBytes(std::get<Literal>(bytecode).value.str()),
_arguments, _arguments,
@ -456,24 +424,19 @@ EVMDialectTyped::EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectA
m_functions["popbool"_yulstring].name = "popbool"_yulstring; m_functions["popbool"_yulstring].name = "popbool"_yulstring;
m_functions["popbool"_yulstring].parameters = {"bool"_yulstring}; m_functions["popbool"_yulstring].parameters = {"bool"_yulstring};
m_functions.insert(createFunction("bool_to_u256", 1, 1, {}, {}, []( m_functions.insert(createFunction("bool_to_u256", 1, 1, {}, {}, [](
FunctionCall const& _call, FunctionCall const&,
AbstractAssembly& _assembly, AbstractAssembly&,
BuiltinContext&, BuiltinContext&
std::function<void(Expression const&)> _visitExpression ) {}));
) {
visitArguments(_assembly, _call, _visitExpression);
}));
m_functions["bool_to_u256"_yulstring].parameters = {"bool"_yulstring}; m_functions["bool_to_u256"_yulstring].parameters = {"bool"_yulstring};
m_functions["bool_to_u256"_yulstring].returns = {"u256"_yulstring}; m_functions["bool_to_u256"_yulstring].returns = {"u256"_yulstring};
m_functions.insert(createFunction("u256_to_bool", 1, 1, {}, {}, []( m_functions.insert(createFunction("u256_to_bool", 1, 1, {}, {}, [](
FunctionCall const& _call, FunctionCall const&,
AbstractAssembly& _assembly, AbstractAssembly& _assembly,
BuiltinContext&, BuiltinContext&
std::function<void(Expression const&)> _visitExpression
) { ) {
// TODO this should use a Panic. // TODO this should use a Panic.
// A value larger than 1 causes an invalid instruction. // A value larger than 1 causes an invalid instruction.
visitArguments(_assembly, _call, _visitExpression);
_assembly.appendConstant(2); _assembly.appendConstant(2);
_assembly.appendInstruction(evmasm::Instruction::DUP2); _assembly.appendInstruction(evmasm::Instruction::DUP2);
_assembly.appendInstruction(evmasm::Instruction::LT); _assembly.appendInstruction(evmasm::Instruction::LT);

View File

@ -52,9 +52,10 @@ struct BuiltinFunctionForEVM: public BuiltinFunction
{ {
std::optional<evmasm::Instruction> instruction; std::optional<evmasm::Instruction> instruction;
/// Function to generate code for the given function call and append it to the abstract /// Function to generate code for the given function call and append it to the abstract
/// assembly. The fourth parameter is called to visit (and generate code for) the given /// assembly. Expects all non-literal arguments of the call to be on stack in reverse order
/// argument. /// (i.e. right-most argument pushed first).
std::function<void(FunctionCall const&, AbstractAssembly&, BuiltinContext&, std::function<void(Expression const&)>)> generateCode; /// Expects the caller to set the source location.
std::function<void(FunctionCall const&, AbstractAssembly&, BuiltinContext&)> generateCode;
}; };

View File

@ -26,6 +26,7 @@
#include <libevmasm/Instruction.h> #include <libevmasm/Instruction.h>
#include <range/v3/view/iota.hpp>
using namespace std; using namespace std;
using namespace solidity; using namespace solidity;
@ -135,21 +136,11 @@ NoOutputEVMDialect::NoOutputEVMDialect(EVMDialect const& _copyFrom):
for (auto& fun: m_functions) for (auto& fun: m_functions)
{ {
size_t returns = fun.second.returns.size(); size_t returns = fun.second.returns.size();
fun.second.generateCode = [=](FunctionCall const& _call, AbstractAssembly& _assembly, BuiltinContext&, std::function<void(Expression const&)> _visitExpression) fun.second.generateCode = [=](FunctionCall const& _call, AbstractAssembly& _assembly, BuiltinContext&)
{ {
size_t visited = 0; for (size_t i: ranges::views::iota(0u, _call.arguments.size()))
for (size_t j = 0; j < _call.arguments.size(); j++)
{
size_t const i = _call.arguments.size() - j - 1;
if (!fun.second.literalArgument(i)) if (!fun.second.literalArgument(i))
{ _assembly.appendInstruction(evmasm::Instruction::POP);
_visitExpression(_call.arguments[i]);
visited++;
}
}
for (size_t i = 0; i < visited; i++)
_assembly.appendInstruction(evmasm::Instruction::POP);
for (size_t i = 0; i < returns; i++) for (size_t i = 0; i < returns; i++)
_assembly.appendConstant(u256(0)); _assembly.appendConstant(u256(0));