[Sol->Yul] Implementing bytes.concat in IR codegen.

Co-authored-by: Daniel Kirchner <daniel@ekpyron.org>
This commit is contained in:
Djordje Mijovic 2021-02-23 14:40:46 +01:00
parent e7da9f3d52
commit 80866d3ee4
3 changed files with 73 additions and 1 deletions

View File

@ -2351,6 +2351,60 @@ string YulUtilFunctions::copyArrayFromStorageToMemoryFunction(ArrayType const& _
});
}
string YulUtilFunctions::bytesConcatFunction(vector<Type const*> const& _argumentTypes)
{
string functionName = "bytes_concat";
size_t totalParams = 0;
vector<Type const*> targetTypes;
for (Type const* argumentType: _argumentTypes)
{
solAssert(
argumentType->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory()) ||
argumentType->isImplicitlyConvertibleTo(*TypeProvider::fixedBytes(32)),
""
);
if (argumentType->category() == Type::Category::FixedBytes)
targetTypes.emplace_back(argumentType);
else if (
auto const* literalType = dynamic_cast<StringLiteralType const*>(argumentType);
literalType && literalType->value().size() <= 32
)
targetTypes.emplace_back(TypeProvider::fixedBytes(static_cast<unsigned>(literalType->value().size())));
else
{
solAssert(argumentType->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory()), "");
targetTypes.emplace_back(TypeProvider::bytesMemory());
}
totalParams += argumentType->sizeOnStack();
functionName += "_" + argumentType->identifier();
}
return m_functionCollector.createFunction(functionName, [&]() {
Whiskers templ(R"(
function <functionName>(<parameters>) -> outPtr {
outPtr := <allocateUnbounded>()
let dataStart := add(outPtr, 0x20)
let dataEnd := <encodePacked>(dataStart<?+parameters>, <parameters></+parameters>)
mstore(outPtr, sub(dataEnd, dataStart))
<finalizeAllocation>(outPtr, sub(dataEnd, outPtr))
}
)");
templ("functionName", functionName);
templ("parameters", suffixedVariableNameList("param_", 0, totalParams));
templ("allocateUnbounded", allocateUnboundedFunction());
templ("finalizeAllocation", finalizeAllocationFunction());
templ(
"encodePacked",
ABIFunctions{m_evmVersion, m_revertStrings, m_functionCollector}.tupleEncoderPacked(
_argumentTypes,
targetTypes
)
);
return templ.render();
});
}
string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingType, Type const& _keyType)
{
string functionName = "mapping_index_access_" + _mappingType.identifier() + "_of_" + _keyType.identifier();

View File

@ -292,6 +292,10 @@ public:
/// of the storage array into it.
std::string copyArrayFromStorageToMemoryFunction(ArrayType const& _from, ArrayType const& _to);
/// @returns the name of a function that does concatenation of variadic number of bytes
/// or fixed bytes
std::string bytesConcatFunction(std::vector<Type const*> const& _argumentTypes);
/// @returns the name of a function that performs index access for mappings.
/// @param _mappingType the type of the mapping
/// @param _keyType the type of the value provided

View File

@ -1325,7 +1325,19 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
}
case FunctionType::Kind::BytesConcat:
{
solUnimplementedAssert(false, "bytes.concat not yet implemented in codegen.");
TypePointers argumentTypes;
vector<string> argumentVars;
for (ASTPointer<Expression const> const& argument: arguments)
{
argumentTypes.emplace_back(&type(*argument));
argumentVars += IRVariable(*argument).stackSlots();
}
define(IRVariable(_functionCall)) <<
m_utils.bytesConcatFunction(argumentTypes) <<
"(" <<
joinHumanReadable(argumentVars) <<
")\n";
break;
}
case FunctionType::Kind::MetaType:
@ -1998,6 +2010,8 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
}
else if (EnumType const* enumType = dynamic_cast<EnumType const*>(&actualType))
define(_memberAccess) << to_string(enumType->memberValue(_memberAccess.memberName())) << "\n";
else if (auto const* arrayType = dynamic_cast<ArrayType const*>(&actualType))
solAssert(arrayType->isByteArray() && member == "concat", "");
else
// The old code generator had a generic "else" case here
// without any specific code being generated,