Allow use of yul util functions in legacy code generation.

This commit is contained in:
Daniel Kirchner 2020-03-02 16:32:30 +01:00
parent b65a165da1
commit 90fa56c719
8 changed files with 68 additions and 29 deletions

View File

@ -240,13 +240,6 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory)
}); });
} }
pair<string, set<string>> ABIFunctions::requestedFunctions()
{
std::set<string> empty;
swap(empty, m_externallyUsedFunctions);
return make_pair(m_functionCollector->requestedFunctions(), std::move(empty));
}
string ABIFunctions::EncodingOptions::toFunctionNameSuffix() const string ABIFunctions::EncodingOptions::toFunctionNameSuffix() const
{ {
string suffix; string suffix;
@ -1504,9 +1497,7 @@ string ABIFunctions::createFunction(string const& _name, function<string ()> con
string ABIFunctions::createExternallyUsedFunction(string const& _name, function<string ()> const& _creator) string ABIFunctions::createExternallyUsedFunction(string const& _name, function<string ()> const& _creator)
{ {
string name = createFunction(_name, _creator); return m_functionCollector->createExternallyUsedFunction(_name, _creator);
m_externallyUsedFunctions.insert(name);
return name;
} }
size_t ABIFunctions::headSize(TypePointers const& _targetTypes) size_t ABIFunctions::headSize(TypePointers const& _targetTypes)

View File

@ -104,12 +104,6 @@ public:
/// stack slot, it takes exactly that number of values. /// stack slot, it takes exactly that number of values.
std::string tupleDecoder(TypePointers const& _types, bool _fromMemory = false); std::string tupleDecoder(TypePointers const& _types, bool _fromMemory = false);
/// @returns concatenation of all generated functions and a set of the
/// externally used functions.
/// Clears the internal list, i.e. calling it again will result in an
/// empty return value.
std::pair<std::string, std::set<std::string>> requestedFunctions();
private: private:
struct EncodingOptions struct EncodingOptions
{ {
@ -260,7 +254,6 @@ private:
langutil::EVMVersion m_evmVersion; langutil::EVMVersion m_evmVersion;
RevertStrings const m_revertStrings; RevertStrings const m_revertStrings;
std::shared_ptr<MultiUseYulFunctionCollector> m_functionCollector; std::shared_ptr<MultiUseYulFunctionCollector> m_functionCollector;
std::set<std::string> m_externallyUsedFunctions;
YulUtilFunctions m_utils; YulUtilFunctions m_utils;
}; };

View File

@ -94,6 +94,20 @@ void CompilerContext::callLowLevelFunction(
*this << retTag.tag(); *this << retTag.tag();
} }
void CompilerContext::callYulUtilFunction(
string const& _name,
unsigned _inArgs,
unsigned _outArgs
)
{
m_functionCollector->markAsExternallyUsed(_name);
auto retTag = pushNewTag();
CompilerUtils(*this).moveIntoStack(_inArgs);
appendJumpTo(namedTag(_name));
adjustStackOffset(int(_outArgs) - 1 - _inArgs);
*this << retTag.tag();
}
evmasm::AssemblyItem CompilerContext::lowLevelFunctionTag( evmasm::AssemblyItem CompilerContext::lowLevelFunctionTag(
string const& _name, string const& _name,
unsigned _inArgs, unsigned _inArgs,

View File

@ -65,7 +65,8 @@ public:
m_evmVersion(_evmVersion), m_evmVersion(_evmVersion),
m_revertStrings(_revertStrings), m_revertStrings(_revertStrings),
m_runtimeContext(_runtimeContext), m_runtimeContext(_runtimeContext),
m_abiFunctions(m_evmVersion, m_revertStrings) m_abiFunctions(m_evmVersion, m_revertStrings, m_functionCollector),
m_yulUtilFunctions(m_evmVersion, m_revertStrings, m_functionCollector)
{ {
if (m_runtimeContext) if (m_runtimeContext)
m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data()); m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data());
@ -131,6 +132,14 @@ public:
unsigned _outArgs, unsigned _outArgs,
std::function<void(CompilerContext&)> const& _generator std::function<void(CompilerContext&)> const& _generator
); );
/// Appends a call to a yul util function and registers the function as externally used.
void callYulUtilFunction(
std::string const& _name,
unsigned _inArgs,
unsigned _outArgs
);
/// Returns the tag of the named low-level function and inserts the generator into the /// Returns the tag of the named low-level function and inserts the generator into the
/// list of low-level-functions to be generated, unless it already exists. /// list of low-level-functions to be generated, unless it already exists.
/// Note that the generator should not assume that objects are still alive when it is called, /// Note that the generator should not assume that objects are still alive when it is called,
@ -144,6 +153,11 @@ public:
/// Generates the code for missing low-level functions, i.e. calls the generators passed above. /// Generates the code for missing low-level functions, i.e. calls the generators passed above.
void appendMissingLowLevelFunctions(); void appendMissingLowLevelFunctions();
ABIFunctions& abiFunctions() { return m_abiFunctions; } ABIFunctions& abiFunctions() { return m_abiFunctions; }
YulUtilFunctions& utilFunctions() { return m_yulUtilFunctions; }
std::pair<std::string, std::set<std::string>> requestedYulFunctions()
{
return m_functionCollector->requestedFunctions();
}
ModifierDefinition const& resolveVirtualFunctionModifier(ModifierDefinition const& _modifier) const; ModifierDefinition const& resolveVirtualFunctionModifier(ModifierDefinition const& _modifier) const;
/// Returns the distance of the given local variable from the bottom of the stack (of the current function). /// Returns the distance of the given local variable from the bottom of the stack (of the current function).
@ -355,8 +369,12 @@ private:
size_t m_runtimeSub = -1; size_t m_runtimeSub = -1;
/// An index of low-level function labels by name. /// An index of low-level function labels by name.
std::map<std::string, evmasm::AssemblyItem> m_lowLevelFunctions; std::map<std::string, evmasm::AssemblyItem> m_lowLevelFunctions;
// Collector for yul functions.
std::shared_ptr<MultiUseYulFunctionCollector> m_functionCollector = std::make_shared<MultiUseYulFunctionCollector>();
/// Container for ABI functions to be generated. /// Container for ABI functions to be generated.
ABIFunctions m_abiFunctions; ABIFunctions m_abiFunctions;
/// Container for Yul Util functions to be generated.
YulUtilFunctions m_yulUtilFunctions;
/// The queue of low-level functions to generate. /// The queue of low-level functions to generate.
std::queue<std::tuple<std::string, unsigned, unsigned, std::function<void(CompilerContext&)>>> m_lowLevelFunctionGenerationQueue; std::queue<std::tuple<std::string, unsigned, unsigned, std::function<void(CompilerContext&)>>> m_lowLevelFunctionGenerationQueue;
}; };

View File

@ -1267,12 +1267,12 @@ void ContractCompiler::appendMissingFunctions()
solAssert(m_context.nextFunctionToCompile() != function, "Compiled the wrong function?"); solAssert(m_context.nextFunctionToCompile() != function, "Compiled the wrong function?");
} }
m_context.appendMissingLowLevelFunctions(); m_context.appendMissingLowLevelFunctions();
auto abiFunctions = m_context.abiFunctions().requestedFunctions(); auto yulFunctions = m_context.requestedYulFunctions();
if (!abiFunctions.first.empty()) if (!yulFunctions.first.empty())
m_context.appendInlineAssembly( m_context.appendInlineAssembly(
"{" + move(abiFunctions.first) + "}", "{" + move(yulFunctions.first) + "}",
{}, {},
abiFunctions.second, yulFunctions.second,
true, true,
m_optimiserSettings m_optimiserSettings
); );

View File

@ -30,13 +30,15 @@ using namespace std;
using namespace solidity; using namespace solidity;
using namespace solidity::frontend; using namespace solidity::frontend;
string MultiUseYulFunctionCollector::requestedFunctions() pair<string, set<string>> MultiUseYulFunctionCollector::requestedFunctions()
{ {
string result; string result;
for (auto const& f: m_requestedFunctions) for (auto const& f: m_requestedFunctions)
result += f.second; result += f.second;
m_requestedFunctions.clear(); m_requestedFunctions.clear();
return result; std::set<string> empty;
swap(empty, m_externallyUsedFunctions);
return make_pair(result, std::move(empty));
} }
string MultiUseYulFunctionCollector::createFunction(string const& _name, function<string ()> const& _creator) string MultiUseYulFunctionCollector::createFunction(string const& _name, function<string ()> const& _creator)
@ -50,3 +52,9 @@ string MultiUseYulFunctionCollector::createFunction(string const& _name, functio
} }
return _name; return _name;
} }
string MultiUseYulFunctionCollector::createExternallyUsedFunction(string const& _name, function<string ()> const& _creator)
{
m_externallyUsedFunctions.insert(_name);
return createFunction(_name, _creator);
}

View File

@ -23,6 +23,7 @@
#include <functional> #include <functional>
#include <map> #include <map>
#include <set>
#include <string> #include <string>
namespace solidity::frontend namespace solidity::frontend
@ -40,14 +41,28 @@ public:
/// cases. /// cases.
std::string createFunction(std::string const& _name, std::function<std::string()> const& _creator); std::string createFunction(std::string const& _name, std::function<std::string()> const& _creator);
/// @returns concatenation of all generated functions. /// Helper function that uses @a _creator to create a function and add it to
/// @a m_requestedFunctions if it has not been created yet and returns @a _name in both
/// cases.
std::string createExternallyUsedFunction(std::string const& _name, std::function<std::string()> const& _creator);
/// Manually mark a function as externally used.
void markAsExternallyUsed(std::string const& _name)
{
m_externallyUsedFunctions.insert(_name);
}
/// @returns concatenation of all generated functions and a set of the
/// externally used functions.
/// Clears the internal list, i.e. calling it again will result in an /// Clears the internal list, i.e. calling it again will result in an
/// empty return value. /// empty return value.
std::string requestedFunctions(); std::pair<std::string, std::set<std::string>> requestedFunctions();
private: private:
/// Map from function name to code for a multi-use function. /// Map from function name to code for a multi-use function.
std::map<std::string, std::string> m_requestedFunctions; std::map<std::string, std::string> m_requestedFunctions;
// Set of externally used functions.
std::set<std::string> m_externallyUsedFunctions;
}; };
} }

View File

@ -107,7 +107,7 @@ string IRGenerator::generate(ContractDefinition const& _contract)
for (auto const* contract: _contract.annotation().linearizedBaseContracts) for (auto const* contract: _contract.annotation().linearizedBaseContracts)
for (auto const* fun: contract->definedFunctions()) for (auto const* fun: contract->definedFunctions())
generateFunction(*fun); generateFunction(*fun);
t("functions", m_context.functionCollector()->requestedFunctions()); t("functions", m_context.functionCollector()->requestedFunctions().first);
resetContext(_contract); resetContext(_contract);
m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts);
@ -116,7 +116,7 @@ string IRGenerator::generate(ContractDefinition const& _contract)
for (auto const* contract: _contract.annotation().linearizedBaseContracts) for (auto const* contract: _contract.annotation().linearizedBaseContracts)
for (auto const* fun: contract->definedFunctions()) for (auto const* fun: contract->definedFunctions())
generateFunction(*fun); generateFunction(*fun);
t("runtimeFunctions", m_context.functionCollector()->requestedFunctions()); t("runtimeFunctions", m_context.functionCollector()->requestedFunctions().first);
return t.render(); return t.render();
} }
@ -383,7 +383,7 @@ string IRGenerator::memoryInit()
void IRGenerator::resetContext(ContractDefinition const& _contract) void IRGenerator::resetContext(ContractDefinition const& _contract)
{ {
solAssert( solAssert(
m_context.functionCollector()->requestedFunctions().empty(), m_context.functionCollector()->requestedFunctions().first.empty(),
"Reset context while it still had functions." "Reset context while it still had functions."
); );
m_context = IRGenerationContext(m_evmVersion, m_context.revertStrings(), m_optimiserSettings); m_context = IRGenerationContext(m_evmVersion, m_context.revertStrings(), m_optimiserSettings);