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));
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
{
AbstractAssembly::LabelID returnLabel = m_assembly.newLabelId();

View File

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

View File

@ -52,9 +52,10 @@ struct BuiltinFunctionForEVM: public BuiltinFunction
{
std::optional<evmasm::Instruction> instruction;
/// 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
/// argument.
std::function<void(FunctionCall const&, AbstractAssembly&, BuiltinContext&, std::function<void(Expression const&)>)> generateCode;
/// assembly. Expects all non-literal arguments of the call to be on stack in reverse order
/// (i.e. right-most argument pushed first).
/// 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 <range/v3/view/iota.hpp>
using namespace std;
using namespace solidity;
@ -135,21 +136,11 @@ NoOutputEVMDialect::NoOutputEVMDialect(EVMDialect const& _copyFrom):
for (auto& fun: m_functions)
{
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 j = 0; j < _call.arguments.size(); j++)
{
size_t const i = _call.arguments.size() - j - 1;
for (size_t i: ranges::views::iota(0u, _call.arguments.size()))
if (!fun.second.literalArgument(i))
{
_visitExpression(_call.arguments[i]);
visited++;
}
}
for (size_t i = 0; i < visited; i++)
_assembly.appendInstruction(evmasm::Instruction::POP);
_assembly.appendInstruction(evmasm::Instruction::POP);
for (size_t i = 0; i < returns; i++)
_assembly.appendConstant(u256(0));