diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index e8b433782..31feffc47 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -2351,6 +2351,60 @@ string YulUtilFunctions::copyArrayFromStorageToMemoryFunction(ArrayType const& _ }); } +string YulUtilFunctions::bytesConcatFunction(vector const& _argumentTypes) +{ + string functionName = "bytes_concat"; + size_t totalParams = 0; + vector 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(argumentType); + literalType && literalType->value().size() <= 32 + ) + targetTypes.emplace_back(TypeProvider::fixedBytes(static_cast(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 () -> outPtr { + outPtr := () + let dataStart := add(outPtr, 0x20) + let dataEnd := (dataStart, ) + mstore(outPtr, sub(dataEnd, dataStart)) + (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(); diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 9c5aaef3e..13fa173a4 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -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 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 diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 2d72e04b8..b7d0f483b 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -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 argumentVars; + for (ASTPointer 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(&actualType)) define(_memberAccess) << to_string(enumType->memberValue(_memberAccess.memberName())) << "\n"; + else if (auto const* arrayType = dynamic_cast(&actualType)) + solAssert(arrayType->isByteArray() && member == "concat", ""); else // The old code generator had a generic "else" case here // without any specific code being generated,