-
Notifications
You must be signed in to change notification settings - Fork 6.3k
External calls returning only value types no longer allocate persistent memory in IR and legacy pipelines #16444
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -49,6 +49,7 @@ | |
| #include <libsolutil/Visitor.h> | ||
|
|
||
| #include <range/v3/algorithm/all_of.hpp> | ||
| #include <range/v3/algorithm/any_of.hpp> | ||
| #include <range/v3/view/transform.hpp> | ||
|
|
||
| using namespace solidity; | ||
|
|
@@ -2704,8 +2705,10 @@ void IRGeneratorForStatements::appendExternalFunctionCall( | |
| </supportsReturnData> | ||
| </isReturndataSizeDynamic> | ||
|
|
||
| // update freeMemoryPointer according to dynamic return size | ||
| <finalizeAllocation>(<pos>, <returnDataSizeVar>) | ||
| <?needToUpdateFreeMemoryPtr> | ||
| // update freeMemoryPointer according to dynamic return size | ||
| <finalizeAllocation>(<pos>, <returnDataSizeVar>) | ||
| </needToUpdateFreeMemoryPtr> | ||
|
|
||
| // decode return parameters from external try-call into retVars | ||
| <?+retVars> <retVars> := </+retVars> <abiDecode>(<pos>, add(<pos>, <returnDataSizeVar>)) | ||
|
|
@@ -2738,6 +2741,15 @@ void IRGeneratorForStatements::appendExternalFunctionCall( | |
| templ("success", m_context.newYulVariable()); | ||
| templ("allocateUnbounded", m_utils.allocateUnboundedFunction()); | ||
| templ("finalizeAllocation", m_utils.finalizeAllocationFunction()); | ||
|
|
||
| // Only update free memory pointer if any return type needs memory (reference types). | ||
| // Value types are decoded directly to the stack via mload. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. While this may be true now, I think that just straight out hard-coding this assumption here is fragile and can lead to bugs. The assumption here is actually a bit broader than just that we're only dealing with value types. We can safely free this memory if and only if we know there will be no other memory allocations before we're done using this piece of memory. There is no guarantee that the decoder won't in the future allocate any extra memory for other purposes, even when it's only dealing with value types. A safer approach would be to modify
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, checking |
||
| bool needToUpdateFreeMemoryPtr = ranges::any_of( | ||
| returnInfo.returnTypes, | ||
| [](auto const& t) { return !t->decodingType()->isValueType(); } | ||
| ); | ||
| templ("needToUpdateFreeMemoryPtr", needToUpdateFreeMemoryPtr); | ||
|
|
||
| templ("shl28", m_utils.shiftLeftFunction(8 * (32 - 4))); | ||
|
|
||
| templ("funSel", IRVariable(_functionCall.expression()).part("functionSelector").name()); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -569,9 +569,6 @@ object \"C_54\" { | |
| _13 := returndatasize() | ||
| } | ||
|
|
||
| // update freeMemoryPointer according to dynamic return size | ||
| finalize_allocation(_10, _13) | ||
|
|
||
| // decode return parameters from external try-call into retVars | ||
| expr_47 := abi_decode_tuple_t_int256_fromMemory(_10, add(_10, _13)) | ||
| } | ||
|
|
@@ -714,14 +711,6 @@ object \"C_54\" { | |
| let _6 := 32 | ||
| if gt(32, returndatasize()) { _6 := returndatasize() } | ||
| /// @src 0:79:510 \"contract C...\" | ||
| let newFreePtr := add(_4, and(add(_6, 31), not(31))) | ||
| if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, _4)) | ||
| { | ||
| mstore(0, shl(224, 0x4e487b71)) | ||
| mstore(4, 0x41) | ||
| revert(0, 0x24) | ||
| } | ||
| mstore(64, newFreePtr) | ||
|
Comment on lines
-717
to
-724
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm surprised how much code this simple change removes. Looks like |
||
| if slt(sub(/** @src 0:482:490 \"this.f()\" */ add(_4, _6), /** @src 0:79:510 \"contract C...\" */ _4), /** @src 0:482:490 \"this.f()\" */ 32) | ||
| /// @src 0:79:510 \"contract C...\" | ||
| { revert(0, 0) } | ||
|
|
@@ -1406,9 +1395,6 @@ object \"D_72\" { | |
| _13 := returndatasize() | ||
| } | ||
|
|
||
| // update freeMemoryPointer according to dynamic return size | ||
| finalize_allocation(_10, _13) | ||
|
|
||
| // decode return parameters from external try-call into retVars | ||
| expr_47 := abi_decode_tuple_t_int256_fromMemory(_10, add(_10, _13)) | ||
| } | ||
|
|
@@ -1558,14 +1544,6 @@ object \"D_72\" { | |
| let _6 := 32 | ||
| if gt(32, returndatasize()) { _6 := returndatasize() } | ||
| /// @src 1:91:181 \"contract D is C(3)...\" | ||
| let newFreePtr := add(_4, and(add(_6, 31), not(31))) | ||
| if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, _4)) | ||
| { | ||
| mstore(0, shl(224, 0x4e487b71)) | ||
| mstore(4, 0x41) | ||
| revert(0, 0x24) | ||
| } | ||
| mstore(64, newFreePtr) | ||
| if slt(sub(/** @src 0:482:490 \"this.f()\" */ add(_4, _6), /** @src 1:91:181 \"contract D is C(3)...\" */ _4), /** @src 0:482:490 \"this.f()\" */ 32) | ||
| /// @src 1:91:181 \"contract D is C(3)...\" | ||
| { revert(0, 0) } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you removing this? Isn't it still true? I don't see any allocation for static types in
CompilerUtils::abiDecode().Though I also do not see any decoding for such types there, but I guess it's because it does not support nested arrays and assumes that for other types the memory layout matches calldata layout? What about arrays of structs though?
Also, if that's the case then the logic checking
ReferenceTypebelow is also wrong...