[Sol->Yul] Enabling creation function call

This commit is contained in:
Djordje Mijovic
2020-04-27 16:15:57 +02:00
parent 99aa821410
commit f3f729549d
36 changed files with 420 additions and 28 deletions
+28
View File
@@ -1337,6 +1337,34 @@ string YulUtilFunctions::allocationFunction()
});
}
string YulUtilFunctions::allocationTemporaryMemoryFunction()
{
string functionName = "allocateTemporaryMemory";
return m_functionCollector.createFunction(functionName, [&]() {
return Whiskers(R"(
function <functionName>() -> memPtr {
memPtr := mload(<freeMemoryPointer>)
}
)")
("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer))
("functionName", functionName)
.render();
});
}
string YulUtilFunctions::releaseTemporaryMemoryFunction()
{
string functionName = "releaseTemporaryMemory";
return m_functionCollector.createFunction(functionName, [&](){
return Whiskers(R"(
function <functionName>() {
}
)")
("functionName", functionName)
.render();
});
}
string YulUtilFunctions::zeroMemoryArrayFunction(ArrayType const& _type)
{
if (_type.baseType()->hasSimpleZeroValueInMemory())
+7
View File
@@ -253,6 +253,13 @@ public:
/// Return value: pointer
std::string allocationFunction();
/// @returns the name of the function that allocates temporary memory with predefined size
/// Return value: pointer
std::string allocationTemporaryMemoryFunction();
/// @returns the name of the function that releases previously allocated temporary memory
std::string releaseTemporaryMemoryFunction();
/// @returns the name of a function that zeroes an array.
/// signature: (dataStart, dataSizeInBytes) ->
std::string zeroMemoryArrayFunction(ArrayType const& _type);
@@ -21,6 +21,7 @@
#include <libsolidity/codegen/ir/IRGenerationContext.h>
#include <libsolidity/codegen/YulUtilFunctions.h>
#include <libsolidity/codegen/ABIFunctions.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/TypeProvider.h>
@@ -96,6 +97,15 @@ string IRGenerationContext::functionName(VariableDeclaration const& _varDecl)
return "getter_fun_" + _varDecl.name() + "_" + to_string(_varDecl.id());
}
string IRGenerationContext::creationObjectName(ContractDefinition const& _contract) const
{
return _contract.name() + "_" + toString(_contract.id());
}
string IRGenerationContext::runtimeObjectName(ContractDefinition const& _contract) const
{
return _contract.name() + "_" + toString(_contract.id()) + "_deployed";
}
string IRGenerationContext::newYulVariable()
{
return "_" + to_string(++m_varCounter);
@@ -170,6 +180,11 @@ YulUtilFunctions IRGenerationContext::utils()
return YulUtilFunctions(m_evmVersion, m_revertStrings, m_functions);
}
ABIFunctions IRGenerationContext::abiFunctions()
{
return ABIFunctions(m_evmVersion, m_revertStrings, m_functions);
}
std::string IRGenerationContext::revertReasonIfDebug(std::string const& _message)
{
return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message);
+11 -4
View File
@@ -20,6 +20,7 @@
#pragma once
#include <libsolidity/ast/AST.h>
#include <libsolidity/codegen/ir/IRVariable.h>
#include <libsolidity/interface/OptimiserSettings.h>
#include <libsolidity/interface/DebugSettings.h>
@@ -38,11 +39,8 @@
namespace solidity::frontend
{
class ContractDefinition;
class VariableDeclaration;
class FunctionDefinition;
class Expression;
class YulUtilFunctions;
class ABIFunctions;
/**
* Class that contains contextual information during IR generation.
@@ -93,6 +91,9 @@ public:
std::string functionName(FunctionDefinition const& _function);
std::string functionName(VariableDeclaration const& _varDecl);
std::string creationObjectName(ContractDefinition const& _contract) const;
std::string runtimeObjectName(ContractDefinition const& _contract) const;
std::string newYulVariable();
std::string internalDispatch(size_t _in, size_t _out);
@@ -102,6 +103,8 @@ public:
langutil::EVMVersion evmVersion() const { return m_evmVersion; };
ABIFunctions abiFunctions();
/// @returns code that stores @param _message for revert reason
/// if m_revertStrings is debug.
std::string revertReasonIfDebug(std::string const& _message = "");
@@ -112,6 +115,8 @@ public:
/// function call that was invoked as part of the try statement.
std::string trySuccessConditionVariable(Expression const& _expression) const;
std::set<ContractDefinition const*, ASTNode::CompareByID>& subObjectsCreated() { return m_subObjects; }
private:
langutil::EVMVersion m_evmVersion;
RevertStrings m_revertStrings;
@@ -131,6 +136,8 @@ private:
/// long as the order of Yul functions in the generated code is deterministic and the same on
/// all platforms - which is a property guaranteed by MultiUseYulFunctionCollector.
std::set<FunctionDefinition const*> m_functionGenerationQueue;
std::set<ContractDefinition const*, ASTNode::CompareByID> m_subObjects;
};
}
+26 -17
View File
@@ -48,9 +48,12 @@ using namespace solidity;
using namespace solidity::util;
using namespace solidity::frontend;
pair<string, string> IRGenerator::run(ContractDefinition const& _contract)
pair<string, string> IRGenerator::run(
ContractDefinition const& _contract,
map<ContractDefinition const*, string const> const& _otherYulSources
)
{
string const ir = yul::reindent(generate(_contract));
string const ir = yul::reindent(generate(_contract, _otherYulSources));
yul::AssemblyStack asmStack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, m_optimiserSettings);
if (!asmStack.parseAndAnalyze("", ir))
@@ -73,8 +76,19 @@ pair<string, string> IRGenerator::run(ContractDefinition const& _contract)
return {warning + ir, warning + asmStack.print()};
}
string IRGenerator::generate(ContractDefinition const& _contract)
string IRGenerator::generate(
ContractDefinition const& _contract,
map<ContractDefinition const*, string const> const& _otherYulSources
)
{
auto subObjectSources = [&_otherYulSources](std::set<ContractDefinition const*, ASTNode::CompareByID> const& subObjects) -> string
{
std::string subObjectsSources;
for (ContractDefinition const* subObject: subObjects)
subObjectsSources += _otherYulSources.at(subObject);
return subObjectsSources;
};
Whiskers t(R"(
object "<CreationObject>" {
code {
@@ -93,13 +107,15 @@ string IRGenerator::generate(ContractDefinition const& _contract)
<dispatch>
<runtimeFunctions>
}
<runtimeSubObjects>
}
<subObjects>
}
)");
resetContext(_contract);
t("CreationObject", creationObjectName(_contract));
t("CreationObject", m_context.creationObjectName(_contract));
t("memoryInit", memoryInit());
t("notLibrary", !_contract.isLibrary());
@@ -112,7 +128,7 @@ string IRGenerator::generate(ContractDefinition const& _contract)
constructorParams.emplace_back(m_context.newYulVariable());
t(
"copyConstructorArguments",
m_utils.copyConstructorArgumentsToMemoryFunction(_contract, creationObjectName(_contract))
m_utils.copyConstructorArgumentsToMemoryFunction(_contract, m_context.creationObjectName(_contract))
);
}
t("constructorParams", joinHumanReadable(constructorParams));
@@ -123,12 +139,14 @@ string IRGenerator::generate(ContractDefinition const& _contract)
generateImplicitConstructors(_contract);
generateQueuedFunctions();
t("functions", m_context.functionCollector().requestedFunctions());
t("subObjects", subObjectSources(m_context.subObjectsCreated()));
resetContext(_contract);
t("RuntimeObject", runtimeObjectName(_contract));
t("RuntimeObject", m_context.runtimeObjectName(_contract));
t("dispatch", dispatchRoutine(_contract));
generateQueuedFunctions();
t("runtimeFunctions", m_context.functionCollector().requestedFunctions());
t("runtimeSubObjects", subObjectSources(m_context.subObjectsCreated()));
return t.render();
}
@@ -313,6 +331,7 @@ string IRGenerator::initStateVariables(ContractDefinition const& _contract)
return generator.code();
}
void IRGenerator::generateImplicitConstructors(ContractDefinition const& _contract)
{
auto listAllParams = [&](
@@ -375,7 +394,7 @@ string IRGenerator::deployCode(ContractDefinition const& _contract)
codecopy(0, dataoffset("<object>"), datasize("<object>"))
return(0, datasize("<object>"))
)X");
t("object", runtimeObjectName(_contract));
t("object", m_context.runtimeObjectName(_contract));
return t.render();
}
@@ -384,16 +403,6 @@ string IRGenerator::callValueCheck()
return "if callvalue() { revert(0, 0) }";
}
string IRGenerator::creationObjectName(ContractDefinition const& _contract)
{
return _contract.name() + "_" + to_string(_contract.id());
}
string IRGenerator::runtimeObjectName(ContractDefinition const& _contract)
{
return _contract.name() + "_" + to_string(_contract.id()) + "_deployed";
}
string IRGenerator::implicitConstructorName(ContractDefinition const& _contract)
{
return "constructor_" + _contract.name() + "_" + to_string(_contract.id());
+8 -4
View File
@@ -50,10 +50,16 @@ public:
/// Generates and returns the IR code, in unoptimized and optimized form
/// (or just pretty-printed, depending on the optimizer settings).
std::pair<std::string, std::string> run(ContractDefinition const& _contract);
std::pair<std::string, std::string> run(
ContractDefinition const& _contract,
std::map<ContractDefinition const*, std::string const> const& _otherYulSources
);
private:
std::string generate(ContractDefinition const& _contract);
std::string generate(
ContractDefinition const& _contract,
std::map<ContractDefinition const*, std::string const> const& _otherYulSources
);
std::string generate(Block const& _block);
/// Generates code for all the functions from the function generation queue.
@@ -87,8 +93,6 @@ private:
std::string deployCode(ContractDefinition const& _contract);
std::string callValueCheck();
std::string creationObjectName(ContractDefinition const& _contract);
std::string runtimeObjectName(ContractDefinition const& _contract);
std::string implicitConstructorName(ContractDefinition const& _contract);
std::string dispatchRoutine(ContractDefinition const& _contract);
@@ -849,11 +849,63 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
templ("pos", m_context.newYulVariable());
templ("end", m_context.newYulVariable());
templ("freeMemory", freeMemory());
templ("encode", abi.tupleEncoder({arguments.front()->annotation().type},{parameterTypes.front()}));
templ("encode", abi.tupleEncoder({arguments.front()->annotation().type}, {parameterTypes.front()}));
templ("nonIndexedArgs", IRVariable(*arguments.front()).commaSeparatedList());
templ("log", "log" + to_string(logNumber));
templ("indexedArgs", indexedArgs);
m_code << templ.render();
break;
}
case FunctionType::Kind::Creation:
{
solAssert(!functionType->gasSet(), "Gas limit set for contract creation.");
solAssert(
functionType->returnParameterTypes().size() == 1,
"Constructor should return only one type"
);
TypePointers argumentTypes;
string constructorParams;
for (ASTPointer<Expression const> const& arg: arguments)
{
argumentTypes.push_back(arg->annotation().type);
constructorParams += ", " + IRVariable{*arg}.commaSeparatedList();
}
ContractDefinition const* contract =
&dynamic_cast<ContractType const&>(*functionType->returnParameterTypes().front()).contractDefinition();
m_context.subObjectsCreated().insert(contract);
Whiskers t(R"(
let <memPos> := <allocateTemporaryMemory>()
let <memEnd> := add(<memPos>, datasize("<object>"))
if or(gt(<memEnd>, 0xffffffffffffffff), lt(<memEnd>, <memPos>)) { revert(0, 0) }
datacopy(<memPos>, dataoffset("<object>"), datasize("<object>"))
<memEnd> := <abiEncode>(<memEnd><constructorParams>)
<?saltSet>
let <retVars> := create2(<value>, <memPos>, sub(<memEnd>, <memPos>), <salt>)
<!saltSet>
let <retVars> := create(<value>, <memPos>, sub(<memEnd>, <memPos>))
</saltSet>
<releaseTemporaryMemory>()
)");
t("memPos", m_context.newYulVariable());
t("memEnd", m_context.newYulVariable());
t("allocateTemporaryMemory", m_utils.allocationTemporaryMemoryFunction());
t("releaseTemporaryMemory", m_utils.releaseTemporaryMemoryFunction());
t("object", m_context.creationObjectName(*contract));
t("abiEncode",
m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(),false)
);
t("constructorParams", constructorParams);
t("value", functionType->valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");
t("saltSet", functionType->saltSet());
if (functionType->saltSet())
t("salt", IRVariable(_functionCall.expression()).part("salt").name());
t("retVars", IRVariable(_functionCall).commaSeparatedList());
m_code << t.render();
break;
}
default:
+7 -1
View File
@@ -1129,15 +1129,21 @@ void CompilerStack::generateIR(ContractDefinition const& _contract)
if (!_contract.canBeDeployed())
return;
map<ContractDefinition const*, string const> otherYulSources;
Contract& compiledContract = m_contracts.at(_contract.fullyQualifiedName());
if (!compiledContract.yulIR.empty())
return;
string dependenciesSource;
for (auto const* dependency: _contract.annotation().contractDependencies)
{
generateIR(*dependency);
otherYulSources.emplace(dependency, m_contracts.at(dependency->fullyQualifiedName()).yulIR);
}
IRGenerator generator(m_evmVersion, m_revertStrings, m_optimiserSettings);
tie(compiledContract.yulIR, compiledContract.yulIROptimized) = generator.run(_contract);
tie(compiledContract.yulIR, compiledContract.yulIROptimized) = generator.run(_contract, otherYulSources);
}
void CompilerStack::generateEwasm(ContractDefinition const& _contract)