Skip to content
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

Optimize resizeDynamicArray #15176

Open
molly-ting opened this issue Jun 4, 2024 · 0 comments
Open

Optimize resizeDynamicArray #15176

molly-ting opened this issue Jun 4, 2024 · 0 comments
Labels

Comments

@molly-ting
Copy link

Description

void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const
{
Type const* type = &_typeIn;
m_context.callLowLevelFunction(
"$resizeDynamicArray_" + _typeIn.identifier(),
2,
0,
[type](CompilerContext& _context)
{
ArrayType const& _type = dynamic_cast<ArrayType const&>(*type);
solAssert(_type.location() == DataLocation::Storage, "");
solAssert(_type.isDynamicallySized(), "");
if (!_type.isByteArrayOrString() && _type.baseType()->storageBytes() < 32)
solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type.");
unsigned stackHeightStart = _context.stackHeight();
evmasm::AssemblyItem resizeEnd = _context.newTag();
// stack: ref new_length
// fetch old length
ArrayUtils(_context).retrieveLength(_type, 1);
// stack: ref new_length old_length
solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "2");
// Special case for short byte arrays, they are stored together with their length
if (_type.isByteArrayOrString())
{
evmasm::AssemblyItem regularPath = _context.newTag();
// We start by a large case-distinction about the old and new length of the byte array.
_context << Instruction::DUP3 << Instruction::SLOAD;
// stack: ref new_length current_length ref_value
solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3");
_context << Instruction::DUP2 << u256(31) << Instruction::LT;
evmasm::AssemblyItem currentIsLong = _context.appendConditionalJump();
_context << Instruction::DUP3 << u256(31) << Instruction::LT;
evmasm::AssemblyItem newIsLong = _context.appendConditionalJump();
// Here: short -> short
// Compute 1 << (256 - 8 * new_size)
evmasm::AssemblyItem shortToShort = _context.newTag();
_context << shortToShort;
_context << Instruction::DUP3 << u256(8) << Instruction::MUL;
_context << u256(0x100) << Instruction::SUB;
_context << u256(2) << Instruction::EXP;
// Divide and multiply by that value, clearing bits.
_context << Instruction::DUP1 << Instruction::SWAP2;
_context << Instruction::DIV << Instruction::MUL;
// Insert 2*length.
_context << Instruction::DUP3 << Instruction::DUP1 << Instruction::ADD;
_context << Instruction::OR;
// Store.
_context << Instruction::DUP4 << Instruction::SSTORE;
solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "3");
_context.appendJumpTo(resizeEnd);
_context.adjustStackOffset(1); // we have to do that because of the jumps
// Here: short -> long
_context << newIsLong;
// stack: ref new_length current_length ref_value
solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3");
// Zero out lower-order byte.
_context << u256(0xff) << Instruction::NOT << Instruction::AND;
// Store at data location.
_context << Instruction::DUP4;
CompilerUtils(_context).computeHashStatic();
_context << Instruction::SSTORE;
// stack: ref new_length current_length
// Store new length: Compule 2*length + 1 and store it.
_context << Instruction::DUP2 << Instruction::DUP1 << Instruction::ADD;
_context << u256(1) << Instruction::ADD;
// stack: ref new_length current_length 2*new_length+1
_context << Instruction::DUP4 << Instruction::SSTORE;
solAssert(_context.stackHeight() - stackHeightStart == 3 - 2, "3");
_context.appendJumpTo(resizeEnd);
_context.adjustStackOffset(1); // we have to do that because of the jumps
_context << currentIsLong;
_context << Instruction::DUP3 << u256(31) << Instruction::LT;
_context.appendConditionalJumpTo(regularPath);
// Here: long -> short
// Read the first word of the data and store it on the stack. Clear the data location and
// then jump to the short -> short case.
// stack: ref new_length current_length ref_value
solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3");
_context << Instruction::POP << Instruction::DUP3;
CompilerUtils(_context).computeHashStatic();
_context << Instruction::DUP1 << Instruction::SLOAD << Instruction::SWAP1;
// stack: ref new_length current_length first_word data_location
_context << Instruction::DUP3;
ArrayUtils(_context).convertLengthToSize(_type);
_context << Instruction::DUP2 << Instruction::ADD << Instruction::SWAP1;
// stack: ref new_length current_length first_word data_location_end data_location
ArrayUtils(_context).clearStorageLoop(TypeProvider::uint256());
_context << Instruction::POP;
// stack: ref new_length current_length first_word
solAssert(_context.stackHeight() - stackHeightStart == 4 - 2, "3");
_context.appendJumpTo(shortToShort);
_context << regularPath;
// stack: ref new_length current_length ref_value
_context << Instruction::POP;
}

The two branches of the conditional jump at line 725 both pop the top elements, indicating that the top element is redundant. I noticed that the top element was introduced by the sload at line 672. It seems that this sloaded value is only used in the two branches of the conditional jump at line 679. Why not move this dup3 and sload before line 678?
Besides, I tried pushing and popping to a dynamic string array, using new to resize the dynamic array, and using sstore to set the length slot. None of these reached this function. After searching the whole program, I found that this function is not invoked anywhere. Is this function still in use? If not, why not delete it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant