Merge pull request #8951 from ethereum/sol-yul-refactor-split-internal-dispatch

[Sol->Yul] Split internal dispatch into separate enumeration and code generation (refactor)
This commit is contained in:
chriseth 2020-05-20 16:28:24 +02:00 committed by GitHub
commit 29405c223b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 51 additions and 27 deletions

View File

@ -53,6 +53,13 @@ string IRNames::runtimeObject(ContractDefinition const& _contract)
return _contract.name() + "_" + toString(_contract.id()) + "_deployed"; return _contract.name() + "_" + toString(_contract.id()) + "_deployed";
} }
string IRNames::internalDispatch(YulArity const& _arity)
{
return "dispatch_internal"
"_in_" + to_string(_arity.in) +
"_out_" + to_string(_arity.out);
}
string IRNames::implicitConstructor(ContractDefinition const& _contract) string IRNames::implicitConstructor(ContractDefinition const& _contract)
{ {
return "constructor_" + _contract.name() + "_" + to_string(_contract.id()); return "constructor_" + _contract.name() + "_" + to_string(_contract.id());

View File

@ -50,6 +50,7 @@ struct IRNames
static std::string function(VariableDeclaration const& _varDecl); static std::string function(VariableDeclaration const& _varDecl);
static std::string creationObject(ContractDefinition const& _contract); static std::string creationObject(ContractDefinition const& _contract);
static std::string runtimeObject(ContractDefinition const& _contract); static std::string runtimeObject(ContractDefinition const& _contract);
static std::string internalDispatch(YulArity const& _arity);
static std::string implicitConstructor(ContractDefinition const& _contract); static std::string implicitConstructor(ContractDefinition const& _contract);
static std::string constantValueFunction(VariableDeclaration const& _constant); static std::string constantValueFunction(VariableDeclaration const& _constant);
static std::string localVariable(VariableDeclaration const& _declaration); static std::string localVariable(VariableDeclaration const& _declaration);

View File

@ -121,9 +121,9 @@ string IRGenerationContext::newYulVariable()
return "_" + to_string(++m_varCounter); return "_" + to_string(++m_varCounter);
} }
string IRGenerationContext::internalDispatch(YulArity const& _arity) string IRGenerationContext::generateInternalDispatchFunction(YulArity const& _arity)
{ {
string funName = "dispatch_internal_in_" + to_string(_arity.in) + "_out_" + to_string(_arity.out); string funName = IRNames::internalDispatch(_arity);
return m_functions.createFunction(funName, [&]() { return m_functions.createFunction(funName, [&]() {
Whiskers templ(R"( Whiskers templ(R"(
function <functionName>(fun <comma> <in>) <arrow> <out> { function <functionName>(fun <comma> <in>) <arrow> <out> {
@ -139,35 +139,32 @@ string IRGenerationContext::internalDispatch(YulArity const& _arity)
)"); )");
templ("functionName", funName); templ("functionName", funName);
templ("comma", _arity.in > 0 ? "," : ""); templ("comma", _arity.in > 0 ? "," : "");
YulUtilFunctions utils(m_evmVersion, m_revertStrings, m_functions);
templ("in", suffixedVariableNameList("in_", 0, _arity.in)); templ("in", suffixedVariableNameList("in_", 0, _arity.in));
templ("arrow", _arity.out > 0 ? "->" : ""); templ("arrow", _arity.out > 0 ? "->" : "");
templ("assignment_op", _arity.out > 0 ? ":=" : ""); templ("assignment_op", _arity.out > 0 ? ":=" : "");
templ("out", suffixedVariableNameList("out_", 0, _arity.out)); templ("out", suffixedVariableNameList("out_", 0, _arity.out));
// UNIMPLEMENTED: Internal library calls via pointers are not implemented yet. vector<map<string, string>> cases;
// We're not generating code for internal library functions here even though it's possible for (FunctionDefinition const* function: collectFunctionsOfArity(_arity))
// to call them via pointers. Right now such calls end up triggering the `default` case in {
// the switch above. solAssert(function, "");
vector<map<string, string>> functions; solAssert(
for (auto const& contract: mostDerivedContract().annotation().linearizedBaseContracts) YulArity::fromType(*TypeProvider::function(*function, FunctionType::Kind::Internal)) == _arity,
for (FunctionDefinition const* function: contract->definedFunctions()) "A single dispatch function can only handle functions of one arity"
if ( );
!function->isConstructor() && solAssert(!function->isConstructor(), "");
YulArity::fromType(*TypeProvider::function(*function, FunctionType::Kind::Internal)) == _arity // 0 is reserved for uninitialized function pointers
) solAssert(function->id() != 0, "Unexpected function ID: 0");
{
// 0 is reserved for uninitialized function pointers
solAssert(function->id() != 0, "Unexpected function ID: 0");
functions.emplace_back(map<string, string> { cases.emplace_back(map<string, string>{
{ "funID", to_string(function->id()) }, {"funID", to_string(function->id())},
{ "name", IRNames::function(*function)} {"name", IRNames::function(*function)}
}); });
enqueueFunctionForCodeGeneration(*function); enqueueFunctionForCodeGeneration(*function);
} }
templ("cases", move(functions));
templ("cases", move(cases));
return templ.render(); return templ.render();
}); });
} }
@ -187,3 +184,20 @@ std::string IRGenerationContext::revertReasonIfDebug(std::string const& _message
return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message); return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message);
} }
set<FunctionDefinition const*> IRGenerationContext::collectFunctionsOfArity(YulArity const& _arity)
{
// UNIMPLEMENTED: Internal library calls via pointers are not implemented yet.
// We're not returning any internal library functions here even though it's possible
// to call them via pointers. Right now such calls end will up triggering the `default` case in
// the switch in the generated dispatch function.
set<FunctionDefinition const*> functions;
for (auto const& contract: mostDerivedContract().annotation().linearizedBaseContracts)
for (FunctionDefinition const* function: contract->definedFunctions())
if (
!function->isConstructor() &&
YulArity::fromType(*TypeProvider::function(*function, FunctionType::Kind::Internal)) == _arity
)
functions.insert(function);
return functions;
}

View File

@ -102,7 +102,7 @@ public:
std::string newYulVariable(); std::string newYulVariable();
std::string internalDispatch(YulArity const& _arity); std::string generateInternalDispatchFunction(YulArity const& _arity);
/// @returns a new copy of the utility function generator (but using the same function set). /// @returns a new copy of the utility function generator (but using the same function set).
YulUtilFunctions utils(); YulUtilFunctions utils();
@ -120,6 +120,8 @@ public:
std::set<ContractDefinition const*, ASTNode::CompareByID>& subObjectsCreated() { return m_subObjects; } std::set<ContractDefinition const*, ASTNode::CompareByID>& subObjectsCreated() { return m_subObjects; }
private: private:
std::set<FunctionDefinition const*> collectFunctionsOfArity(YulArity const& _arity);
langutil::EVMVersion m_evmVersion; langutil::EVMVersion m_evmVersion;
RevertStrings m_revertStrings; RevertStrings m_revertStrings;
OptimiserSettings m_optimiserSettings; OptimiserSettings m_optimiserSettings;

View File

@ -695,8 +695,8 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
{ {
YulArity arity = YulArity::fromType(*functionType); YulArity arity = YulArity::fromType(*functionType);
define(_functionCall) << define(_functionCall) <<
// NOTE: internalDispatch() takes care of adding the function to function generation queue // NOTE: generateInternalDispatchFunction() takes care of adding the function to function generation queue
m_context.internalDispatch(arity) << m_context.generateInternalDispatchFunction(arity) <<
"(" << "(" <<
IRVariable(_functionCall.expression()).part("functionIdentifier").name() << IRVariable(_functionCall.expression()).part("functionIdentifier").name() <<
joinHumanReadablePrefixed(args) << joinHumanReadablePrefixed(args) <<