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

View File

@ -67,31 +67,53 @@ public:
/// @returns name of an assembly function to ABI-encode values of @a _givenTypes
/// into memory, converting the types to @a _targetTypes on the fly.
/// Parameters are: <headStart> <value_n> ... <value_1>, i.e.
/// the layout on the stack is <value_1> ... <value_n> <headStart> with
/// Parameters are: <headStart> <value_1> ... <value_n>, i.e.
/// the layout on the stack is <value_n> ... <value_1> <headStart> with
/// the top of the stack on the right.
/// The values represent stack slots. If a type occupies more or less than one
/// stack slot, it takes exactly that number of values.
/// 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
/// 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(
TypePointers const& _givenTypes,
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
/// with packed encoding into memory, converting the types to @a _targetTypes on the fly.
/// Parameters are: <memPos> <value_n> ... <value_1>, i.e.
/// the layout on the stack is <value_1> ... <value_n> <memPos> with
/// Parameters are: <memPos> <value_1> ... <value_n>, i.e.
/// the layout on the stack is <value_n> ... <value_1> <memPos> with
/// the top of the stack on the right.
/// The values represent stack slots. If a type occupies more or less than one
/// stack slot, it takes exactly that number of values.
/// 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
/// 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
/// into memory. If @a _fromMemory is true, decodes from memory instead of

View File

@ -559,8 +559,8 @@ void CompilerUtils::abiEncodeV2(
string encoderName =
_padToWordBoundaries ?
m_context.abiFunctions().tupleEncoder(_givenTypes, _targetTypes, _encodeAsLibraryTypes) :
m_context.abiFunctions().tupleEncoderPacked(_givenTypes, _targetTypes);
m_context.abiFunctions().tupleEncoderReversed(_givenTypes, _targetTypes, _encodeAsLibraryTypes) :
m_context.abiFunctions().tupleEncoderPackedReversed(_givenTypes, _targetTypes);
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, [&]() {
if (_mappingType.keyType()->isDynamicallySized())
return Whiskers(R"(
function <functionName>(slot <comma> <key>) -> dataSlot {
dataSlot := <hash>(slot <comma> <key>)
function <functionName>(slot <?+key>,</+key> <key>) -> dataSlot {
dataSlot := <hash>(<key> <?+key>,</+key> slot)
}
)")
("functionName", functionName)
("key", _keyType.sizeOnStack() > 0 ? "key" : "")
("comma", _keyType.sizeOnStack() > 0 ? "," : "")
("hash", packedHashFunction(
{&_keyType, TypeProvider::uint256()},
{_mappingType.keyType(), TypeProvider::uint256()}

View File

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

View File

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

View File

@ -38,7 +38,7 @@ object \"C_10\" {
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_9()
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))
}

View File

@ -38,7 +38,7 @@ object \"C_10\" {
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_9()
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))
}

View File

@ -38,7 +38,7 @@ object \"C_10\" {
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_9()
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))
}

View File

@ -38,7 +38,7 @@ object \"C_10\" {
abi_decode_tuple_(4, calldatasize())
let ret_0 := fun_f_9()
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))
}

View File

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