Merge pull request #8863 from ethereum/nonReversedEncoder

Introduce non-reversed version of tupleEncoder.
This commit is contained in:
chriseth 2020-05-07 15:47:32 +02:00 committed by GitHub
commit f42dc70c9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 63 additions and 30 deletions

View File

@ -37,7 +37,8 @@ using namespace solidity::frontend;
string ABIFunctions::tupleEncoder( string ABIFunctions::tupleEncoder(
TypePointers const& _givenTypes, TypePointers const& _givenTypes,
TypePointers const& _targetTypes, TypePointers const& _targetTypes,
bool _encodeAsLibraryTypes bool _encodeAsLibraryTypes,
bool _reversed
) )
{ {
EncodingOptions options; EncodingOptions options;
@ -53,6 +54,8 @@ string ABIFunctions::tupleEncoder(
for (auto const& t: _targetTypes) for (auto const& t: _targetTypes)
functionName += t->identifier() + "_"; functionName += t->identifier() + "_";
functionName += options.toFunctionNameSuffix(); functionName += options.toFunctionNameSuffix();
if (_reversed)
functionName += "_reversed";
return createFunction(functionName, [&]() { return createFunction(functionName, [&]() {
// Note that the values are in reverse due to the difference in calling semantics. // Note that the values are in reverse due to the difference in calling semantics.
@ -93,7 +96,10 @@ string ABIFunctions::tupleEncoder(
stackPos += sizeOnStack; stackPos += sizeOnStack;
} }
solAssert(headPos == headSize_, ""); solAssert(headPos == headSize_, "");
string valueParams = suffixedVariableNameList("value", stackPos, 0); string valueParams =
_reversed ?
suffixedVariableNameList("value", stackPos, 0) :
suffixedVariableNameList("value", 0, stackPos);
templ("valueParams", valueParams.empty() ? "" : ", " + valueParams); templ("valueParams", valueParams.empty() ? "" : ", " + valueParams);
templ("encodeElements", encodeElements); templ("encodeElements", encodeElements);
@ -103,7 +109,8 @@ string ABIFunctions::tupleEncoder(
string ABIFunctions::tupleEncoderPacked( string ABIFunctions::tupleEncoderPacked(
TypePointers const& _givenTypes, TypePointers const& _givenTypes,
TypePointers const& _targetTypes TypePointers const& _targetTypes,
bool _reversed
) )
{ {
EncodingOptions options; EncodingOptions options;
@ -119,6 +126,8 @@ string ABIFunctions::tupleEncoderPacked(
for (auto const& t: _targetTypes) for (auto const& t: _targetTypes)
functionName += t->identifier() + "_"; functionName += t->identifier() + "_";
functionName += options.toFunctionNameSuffix(); functionName += options.toFunctionNameSuffix();
if (_reversed)
functionName += "_reversed";
return createFunction(functionName, [&]() { return createFunction(functionName, [&]() {
solAssert(!_givenTypes.empty(), ""); solAssert(!_givenTypes.empty(), "");
@ -157,7 +166,10 @@ string ABIFunctions::tupleEncoderPacked(
encodeElements += elementTempl.render(); encodeElements += elementTempl.render();
stackPos += sizeOnStack; stackPos += sizeOnStack;
} }
string valueParams = suffixedVariableNameList("value", stackPos, 0); string valueParams =
_reversed ?
suffixedVariableNameList("value", stackPos, 0) :
suffixedVariableNameList("value", 0, stackPos);
templ("valueParams", valueParams.empty() ? "" : ", " + valueParams); templ("valueParams", valueParams.empty() ? "" : ", " + valueParams);
templ("encodeElements", encodeElements); templ("encodeElements", encodeElements);

View File

@ -67,31 +67,53 @@ public:
/// @returns name of an assembly function to ABI-encode values of @a _givenTypes /// @returns name of an assembly function to ABI-encode values of @a _givenTypes
/// into memory, converting the types to @a _targetTypes on the fly. /// into memory, converting the types to @a _targetTypes on the fly.
/// Parameters are: <headStart> <value_n> ... <value_1>, i.e. /// Parameters are: <headStart> <value_1> ... <value_n>, i.e.
/// the layout on the stack is <value_1> ... <value_n> <headStart> with /// the layout on the stack is <value_n> ... <value_1> <headStart> with
/// the top of the stack on the right. /// the top of the stack on the right.
/// The values represent stack slots. If a type occupies more or less than one /// The values represent stack slots. If a type occupies more or less than one
/// stack slot, it takes exactly that number of values. /// stack slot, it takes exactly that number of values.
/// Returns a pointer to the end of the area written in memory. /// Returns a pointer to the end of the area written in memory.
/// Does not allocate memory (does not change the free memory pointer), but writes /// Does not allocate memory (does not change the free memory pointer), but writes
/// to memory starting at $headStart and an unrestricted amount after that. /// to memory starting at $headStart and an unrestricted amount after that.
/// If @reversed is true, the order of the variables after <headStart> is reversed.
std::string tupleEncoder( std::string tupleEncoder(
TypePointers const& _givenTypes, TypePointers const& _givenTypes,
TypePointers const& _targetTypes, TypePointers const& _targetTypes,
bool _encodeAsLibraryTypes = false bool _encodeAsLibraryTypes = false,
bool _reversed = false
); );
/// Specialization of tupleEncoder to _reversed = true
std::string tupleEncoderReversed(
TypePointers const& _givenTypes,
TypePointers const& _targetTypes,
bool _encodeAsLibraryTypes = false
) {
return tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes, true);
}
/// @returns name of an assembly function to encode values of @a _givenTypes /// @returns name of an assembly function to encode values of @a _givenTypes
/// with packed encoding into memory, converting the types to @a _targetTypes on the fly. /// with packed encoding into memory, converting the types to @a _targetTypes on the fly.
/// Parameters are: <memPos> <value_n> ... <value_1>, i.e. /// Parameters are: <memPos> <value_1> ... <value_n>, i.e.
/// the layout on the stack is <value_1> ... <value_n> <memPos> with /// the layout on the stack is <value_n> ... <value_1> <memPos> with
/// the top of the stack on the right. /// the top of the stack on the right.
/// The values represent stack slots. If a type occupies more or less than one /// The values represent stack slots. If a type occupies more or less than one
/// stack slot, it takes exactly that number of values. /// stack slot, it takes exactly that number of values.
/// Returns a pointer to the end of the area written in memory. /// Returns a pointer to the end of the area written in memory.
/// Does not allocate memory (does not change the free memory pointer), but writes /// Does not allocate memory (does not change the free memory pointer), but writes
/// to memory starting at memPos and an unrestricted amount after that. /// to memory starting at memPos and an unrestricted amount after that.
std::string tupleEncoderPacked(TypePointers const& _givenTypes, TypePointers const& _targetTypes); /// If @reversed is true, the order of the variables after <headStart> is reversed.
std::string tupleEncoderPacked(
TypePointers const& _givenTypes,
TypePointers const& _targetTypes,
bool _reversed = false
);
/// Specialization of tupleEncoderPacked to _reversed = true
std::string tupleEncoderPackedReversed(TypePointers const& _givenTypes, TypePointers const& _targetTypes)
{
return tupleEncoderPacked(_givenTypes, _targetTypes, true);
}
/// @returns name of an assembly function to ABI-decode values of @a _types /// @returns name of an assembly function to ABI-decode values of @a _types
/// into memory. If @a _fromMemory is true, decodes from memory instead of /// into memory. If @a _fromMemory is true, decodes from memory instead of

View File

@ -559,8 +559,8 @@ void CompilerUtils::abiEncodeV2(
string encoderName = string encoderName =
_padToWordBoundaries ? _padToWordBoundaries ?
m_context.abiFunctions().tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes) : m_context.abiFunctions().tupleEncoderReversed(_givenTypes, _targetTypes, _encodeAsLibraryTypes) :
m_context.abiFunctions().tupleEncoderPacked(_givenTypes, _targetTypes); m_context.abiFunctions().tupleEncoderPackedReversed(_givenTypes, _targetTypes);
m_context.callYulFunction(encoderName, sizeOnStack(_givenTypes) + 1, 1); m_context.callYulFunction(encoderName, sizeOnStack(_givenTypes) + 1, 1);
} }

View File

@ -1122,13 +1122,12 @@ string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingT
return m_functionCollector.createFunction(functionName, [&]() { return m_functionCollector.createFunction(functionName, [&]() {
if (_mappingType.keyType()->isDynamicallySized()) if (_mappingType.keyType()->isDynamicallySized())
return Whiskers(R"( return Whiskers(R"(
function <functionName>(slot <comma> <key>) -> dataSlot { function <functionName>(slot <?+key>,</+key> <key>) -> dataSlot {
dataSlot := <hash>(slot <comma> <key>) dataSlot := <hash>(<key> <?+key>,</+key> slot)
} }
)") )")
("functionName", functionName) ("functionName", functionName)
("key", _keyType.sizeOnStack() > 0 ? "key" : "") ("key", _keyType.sizeOnStack() > 0 ? "key" : "")
("comma", _keyType.sizeOnStack() > 0 ? "," : "")
("hash", packedHashFunction( ("hash", packedHashFunction(
{&_keyType, TypeProvider::uint256()}, {&_keyType, TypeProvider::uint256()},
{_mappingType.keyType(), TypeProvider::uint256()} {_mappingType.keyType(), TypeProvider::uint256()}

View File

@ -479,10 +479,10 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
{ {
// <functionName> // <functionName>
<callValueCheck> <callValueCheck>
<assignToParams> <abiDecode>(4, calldatasize()) <?+params>let <params> := </+params> <abiDecode>(4, calldatasize())
<assignToRetParams> <function>(<params>) <?+retParams>let <retParams> := </+retParams> <function>(<params>)
let memPos := <allocate>(0) let memPos := <allocate>(0)
let memEnd := <abiEncode>(memPos <comma> <retParams>) let memEnd := <abiEncode>(memPos <?+retParams>,</+retParams> <retParams>)
return(memPos, sub(memEnd, memPos)) return(memPos, sub(memEnd, memPos))
} }
</cases> </cases>
@ -504,13 +504,11 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
unsigned paramVars = make_shared<TupleType>(type->parameterTypes())->sizeOnStack(); unsigned paramVars = make_shared<TupleType>(type->parameterTypes())->sizeOnStack();
unsigned retVars = make_shared<TupleType>(type->returnParameterTypes())->sizeOnStack(); unsigned retVars = make_shared<TupleType>(type->returnParameterTypes())->sizeOnStack();
templ["assignToParams"] = paramVars == 0 ? "" : "let " + suffixedVariableNameList("param_", 0, paramVars) + " := ";
templ["assignToRetParams"] = retVars == 0 ? "" : "let " + suffixedVariableNameList("ret_", 0, retVars) + " := ";
ABIFunctions abiFunctions(m_evmVersion, m_context.revertStrings(), m_context.functionCollector()); ABIFunctions abiFunctions(m_evmVersion, m_context.revertStrings(), m_context.functionCollector());
templ["abiDecode"] = abiFunctions.tupleDecoder(type->parameterTypes()); templ["abiDecode"] = abiFunctions.tupleDecoder(type->parameterTypes());
templ["params"] = suffixedVariableNameList("param_", 0, paramVars); templ["params"] = suffixedVariableNameList("param_", 0, paramVars);
templ["retParams"] = suffixedVariableNameList("ret_", retVars, 0); templ["retParams"] = suffixedVariableNameList("ret_", 0, retVars);
if (FunctionDefinition const* funDef = dynamic_cast<FunctionDefinition const*>(&type->declaration())) if (FunctionDefinition const* funDef = dynamic_cast<FunctionDefinition const*>(&type->declaration()))
templ["function"] = m_context.enqueueFunctionForCodeGeneration(*funDef); templ["function"] = m_context.enqueueFunctionForCodeGeneration(*funDef);
@ -521,7 +519,6 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
templ["allocate"] = m_utils.allocationFunction(); templ["allocate"] = m_utils.allocationFunction();
templ["abiEncode"] = abiFunctions.tupleEncoder(type->returnParameterTypes(), type->returnParameterTypes(), false); templ["abiEncode"] = abiFunctions.tupleEncoder(type->returnParameterTypes(), type->returnParameterTypes(), false);
templ["comma"] = retVars == 0 ? "" : ", ";
} }
t("cases", functions); t("cases", functions);
if (FunctionDefinition const* fallback = _contract.fallbackFunction()) if (FunctionDefinition const* fallback = _contract.fallbackFunction())

View File

@ -741,8 +741,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
{ {
string vars = IRVariable(arg).commaSeparatedList(); string vars = IRVariable(arg).commaSeparatedList();
if (!vars.empty()) if (!vars.empty())
// In reverse because abi_encode expects it like that. nonIndexedArgs += ", " + move(vars);
nonIndexedArgs = ", " + move(vars) + nonIndexedArgs;
nonIndexedArgTypes.push_back(arg.annotation().type); nonIndexedArgTypes.push_back(arg.annotation().type);
nonIndexedParamTypes.push_back(paramTypes[i]); nonIndexedParamTypes.push_back(paramTypes[i]);
} }
@ -1022,7 +1021,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
t("releaseTemporaryMemory", m_utils.releaseTemporaryMemoryFunction()); t("releaseTemporaryMemory", m_utils.releaseTemporaryMemoryFunction());
t("object", m_context.creationObjectName(*contract)); t("object", m_context.creationObjectName(*contract));
t("abiEncode", t("abiEncode",
m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(),false) m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(), false)
); );
t("constructorParams", constructorParams); t("constructorParams", constructorParams);
t("value", functionType->valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0"); t("value", functionType->valueSet() ? IRVariable(_functionCall.expression()).part("value").name() : "0");

View File

@ -38,7 +38,7 @@ object \"C_10\" {
abi_decode_tuple_(4, calldatasize()) abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_9() let ret_0 := fun_f_9()
let memPos := allocateMemory(0) let memPos := allocateMemory(0)
let memEnd := abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(memPos , ret_0) let memEnd := abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos)) return(memPos, sub(memEnd, memPos))
} }

View File

@ -38,7 +38,7 @@ object \"C_10\" {
abi_decode_tuple_(4, calldatasize()) abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_9() let ret_0 := fun_f_9()
let memPos := allocateMemory(0) let memPos := allocateMemory(0)
let memEnd := abi_encode_tuple_t_bytes32__to_t_bytes32__fromStack(memPos , ret_0) let memEnd := abi_encode_tuple_t_bytes32__to_t_bytes32__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos)) return(memPos, sub(memEnd, memPos))
} }

View File

@ -38,7 +38,7 @@ object \"C_10\" {
abi_decode_tuple_(4, calldatasize()) abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_9() let ret_0 := fun_f_9()
let memPos := allocateMemory(0) let memPos := allocateMemory(0)
let memEnd := abi_encode_tuple_t_bytes4__to_t_bytes4__fromStack(memPos , ret_0) let memEnd := abi_encode_tuple_t_bytes4__to_t_bytes4__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos)) return(memPos, sub(memEnd, memPos))
} }

View File

@ -38,7 +38,7 @@ object \"C_10\" {
abi_decode_tuple_(4, calldatasize()) abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_9() let ret_0 := fun_f_9()
let memPos := allocateMemory(0) let memPos := allocateMemory(0)
let memEnd := abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(memPos , ret_0) let memEnd := abi_encode_tuple_t_string_memory_ptr__to_t_string_memory_ptr__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos)) return(memPos, sub(memEnd, memPos))
} }

View File

@ -38,7 +38,7 @@ object \"C_10\" {
abi_decode_tuple_(4, calldatasize()) abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_9() let ret_0 := fun_f_9()
let memPos := allocateMemory(0) let memPos := allocateMemory(0)
let memEnd := abi_encode_tuple_t_bytes4__to_t_bytes4__fromStack(memPos , ret_0) let memEnd := abi_encode_tuple_t_bytes4__to_t_bytes4__fromStack(memPos , ret_0)
return(memPos, sub(memEnd, memPos)) return(memPos, sub(memEnd, memPos))
} }

View File

@ -7,6 +7,8 @@ contract C {
return this.f(x); return this.f(x);
} }
} }
// ====
// compileViaYul: also
// ---- // ----
// g(uint256[][2][]): 0x20, 0x01, 0x20, 0x40, 0x60, 0x00, 0x00 -> 42 // g(uint256[][2][]): 0x20, 0x01, 0x20, 0x40, 0x60, 0x00, 0x00 -> 42
// g(uint256[][2][]): 0x20, 0x01, 0x20, 0x00, 0x00 -> 42 // g(uint256[][2][]): 0x20, 0x01, 0x20, 0x00, 0x00 -> 42

View File

@ -16,6 +16,8 @@ contract C {
} }
} }
// ====
// compileViaYul: also
// ---- // ----
// f(uint256): 7 -> 8 // f(uint256): 7 -> 8
// f2(uint256): 7 -> 8 // f2(uint256): 7 -> 8