diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 95087aa02..97d72fbde 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1046,12 +1046,14 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // stack: ArrayReference 1 newLength m_context << Instruction::SUB; // stack: ArrayReference (newLength-1) - ArrayUtils(m_context).accessIndex(*arrayType, false); if (arrayType->isByteArrayOrString()) setLValue(_functionCall); else + { + ArrayUtils(m_context).accessIndex(*arrayType, false); setLValueToStorageItem(_functionCall); + } } else { @@ -1072,7 +1074,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // stack: argValue ArrayReference newLength m_context << u256(1) << Instruction::SWAP1 << Instruction::SUB; // stack: argValue ArrayReference (newLength-1) - ArrayUtils(m_context).accessIndex(*arrayType, false); + if (!arrayType->isByteArrayOrString()) + ArrayUtils(m_context).accessIndex(*arrayType, false); // stack: argValue storageSlot slotOffset utils().moveToStackTop(2, argType->sizeOnStack()); // stack: storageSlot slotOffset argValue @@ -2108,14 +2111,16 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) switch (arrayType.location()) { case DataLocation::Storage: - ArrayUtils(m_context).accessIndex(arrayType); if (arrayType.isByteArrayOrString()) { solAssert(!arrayType.isString(), "Index access to string is not allowed."); setLValue(_indexAccess); } else + { + ArrayUtils(m_context).accessIndex(arrayType); setLValueToStorageItem(_indexAccess); + } break; case DataLocation::Memory: ArrayUtils(m_context).accessIndex(arrayType); diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index aa7524c93..9e5b19b8b 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -508,6 +508,8 @@ StorageByteArrayElement::StorageByteArrayElement(CompilerContext& _compilerConte void StorageByteArrayElement::retrieveValue(SourceLocation const&, bool _remove) const { + ArrayUtils(m_context).accessIndex(*TypeProvider::bytesStorage()); + // stack: ref byte_number if (_remove) m_context << Instruction::SWAP1 << Instruction::SLOAD @@ -520,6 +522,8 @@ void StorageByteArrayElement::retrieveValue(SourceLocation const&, bool _remove) void StorageByteArrayElement::storeValue(Type const&, SourceLocation const&, bool _move) const { + ArrayUtils(m_context).accessIndex(*TypeProvider::bytesStorage()); + // stack: value ref byte_number m_context << u256(31) << Instruction::SUB << u256(0x100) << Instruction::EXP; // stack: value ref (1<<(8*(31-byte_number))) @@ -540,6 +544,8 @@ void StorageByteArrayElement::storeValue(Type const&, SourceLocation const&, boo void StorageByteArrayElement::setToZero(SourceLocation const&, bool _removeReference) const { + ArrayUtils(m_context).accessIndex(*TypeProvider::bytesStorage()); + // stack: ref byte_number solAssert(_removeReference, ""); m_context << u256(31) << Instruction::SUB << u256(0x100) << Instruction::EXP; diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 23f41efd2..27df2c32d 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -1634,12 +1634,14 @@ string YulUtilFunctions::storageArrayPushZeroFunction(ArrayType const& _type) let data := sload(array) let oldLen := (data) (array, data, oldLen, add(oldLen, 1)) + slot := array + offset := oldLen let oldLen := (array) if iszero(lt(oldLen, )) { () } sstore(array, add(oldLen, 1)) + slot, offset := (array, oldLen) - slot, offset := (array, oldLen) })") ("functionName", functionName) ("isBytes", _type.isByteArrayOrString()) diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 5eaeed743..68718e7cc 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -1377,13 +1377,22 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) appendCode() << "let " << slotName << ", " << offsetName << " := " << m_utils.storageArrayPushZeroFunction(*arrayType) << "(" << IRVariable(_functionCall.expression()).commaSeparatedList() << ")\n"; - setLValue(_functionCall, IRLValue{ - *arrayType->baseType(), - IRLValue::Storage{ - slotName, - offsetName, - } - }); + if (arrayType->isByteArrayOrString()) + setLValue(_functionCall, IRLValue{ + *arrayType->baseType(), + IRLValue::StorageBytesElement{ + slotName, + offsetName + } + }); + else + setLValue(_functionCall, IRLValue{ + *arrayType->baseType(), + IRLValue::Storage{ + slotName, + offsetName, + } + }); } else { @@ -2220,24 +2229,33 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) { case DataLocation::Storage: { - string slot = m_context.newYulVariable(); - string offset = m_context.newYulVariable(); - - appendCode() << Whiskers(R"( - let , := (, ) - )") - ("slot", slot) - ("offset", offset) - ("indexFunc", m_utils.storageArrayIndexAccessFunction(arrayType)) - ("array", IRVariable(_indexAccess.baseExpression()).part("slot").name()) - ("index", IRVariable(*_indexAccess.indexExpression()).name()) - .render(); - - setLValue(_indexAccess, IRLValue{ - *_indexAccess.annotation().type, - IRLValue::Storage{slot, offset} - }); + if (arrayType.isByteArrayOrString()) + setLValue(_indexAccess, IRLValue{ + *_indexAccess.annotation().type, + IRLValue::StorageBytesElement{ + IRVariable(_indexAccess.baseExpression()).part("slot").name(), + IRVariable(*_indexAccess.indexExpression()).name() + } + }); + else + { + string slot = m_context.newYulVariable(); + string offset = m_context.newYulVariable(); + appendCode() << Whiskers(R"( + let , := (, ) + )") + ("slot", slot) + ("offset", offset) + ("indexFunc", m_utils.storageArrayIndexAccessFunction(arrayType)) + ("array", IRVariable(_indexAccess.baseExpression()).part("slot").name()) + ("index", IRVariable(*_indexAccess.indexExpression()).name()) + .render(); + setLValue(_indexAccess, IRLValue{ + *_indexAccess.annotation().type, + IRLValue::Storage{slot, offset} + }); + } break; } case DataLocation::Memory: @@ -2994,6 +3012,27 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable ")\n"; }, + [&](IRLValue::StorageBytesElement const& _storage) { + string offset = m_context.newYulVariable(); + string slot = m_context.newYulVariable(); + appendCode() << Whiskers(R"( + let , := (, ) + )") + ("slot", slot) + ("offset", offset) + ("indexFunc", m_utils.storageArrayIndexAccessFunction(*TypeProvider::bytesStorage())) + ("array", _storage.baseref) + ("index", _storage.index) + .render(); + appendCode() << + m_utils.updateStorageValueFunction(_value.type(), _lvalue.type) << + "(" << + slot << + ", " << + offset << + _value.commaSeparatedListPrefixed() << + ")\n"; + }, [&](IRLValue::Memory const& _memory) { if (_lvalue.type.isValueType()) { @@ -3084,6 +3123,26 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue) _storage.slot << ")\n"; }, + [&](IRLValue::StorageBytesElement const& _storage) { + string offset = m_context.newYulVariable(); + string slot = m_context.newYulVariable(); + appendCode() << Whiskers(R"( + let , := (, ) + )") + ("slot", slot) + ("offset", offset) + ("indexFunc", m_utils.storageArrayIndexAccessFunction(*TypeProvider::bytesStorage())) + ("array", _storage.baseref) + ("index", _storage.index) + .render(); + define(result) << + m_utils.readFromStorageDynamic(_lvalue.type, true) << + "(" << + slot << + ", " << + offset << + ")\n"; + }, [&](IRLValue::Memory const& _memory) { if (_lvalue.type.isValueType()) define(result) << diff --git a/libsolidity/codegen/ir/IRLValue.h b/libsolidity/codegen/ir/IRLValue.h index fef390d4c..9e55a1805 100644 --- a/libsolidity/codegen/ir/IRLValue.h +++ b/libsolidity/codegen/ir/IRLValue.h @@ -40,6 +40,11 @@ struct IRLValue { VariableDeclaration const* variable = nullptr; }; + struct StorageBytesElement + { + std::string const baseref; + std::string const index; + }; struct Storage { std::string const slot; @@ -64,7 +69,7 @@ struct IRLValue { std::vector> components; }; - std::variant kind; + std::variant kind; }; } diff --git a/test/libsolidity/semanticTests/array/byte_array_dangling_reference_to_length.sol b/test/libsolidity/semanticTests/array/byte_array_dangling_reference_to_length.sol new file mode 100644 index 000000000..ceaa0bd4a --- /dev/null +++ b/test/libsolidity/semanticTests/array/byte_array_dangling_reference_to_length.sol @@ -0,0 +1,10 @@ +contract C { + bytes x = "012345678901234567890123456789A"; + function test() external returns(uint) { + (x[0], x.push()) = (0x80,0x42); + return x.length; + // used to return 0x4000000000000000000000000000000000000000000000000000000000000020 + } +} +// ---- +// test() -> 0x20 diff --git a/test/libsolidity/semanticTests/array/byte_array_storage_layout.sol b/test/libsolidity/semanticTests/array/byte_array_storage_layout.sol index 72fa6860f..bab1ff376 100644 --- a/test/libsolidity/semanticTests/array/byte_array_storage_layout.sol +++ b/test/libsolidity/semanticTests/array/byte_array_storage_layout.sol @@ -46,8 +46,8 @@ contract c { // storageEmpty -> 0 // test_long() -> 67 // gas irOptimized: 89148 -// gas legacy: 103039 -// gas legacyOptimized: 100493 +// gas legacy: 111195 +// gas legacyOptimized: 107824 // storageEmpty -> 0 // test_pop() -> 1780731860627700044960722568376592200742329637303199754547598369979433020 // gas legacy: 61930 diff --git a/test/libsolidity/semanticTests/array/byte_array_transitional_2.sol b/test/libsolidity/semanticTests/array/byte_array_transitional_2.sol index fad9647ec..4f16ffe3f 100644 --- a/test/libsolidity/semanticTests/array/byte_array_transitional_2.sol +++ b/test/libsolidity/semanticTests/array/byte_array_transitional_2.sol @@ -18,5 +18,5 @@ contract c { // ---- // test() -> 0 // gas irOptimized: 157200 -// gas legacy: 188576 -// gas legacyOptimized: 183333 +// gas legacy: 196732 +// gas legacyOptimized: 190664 diff --git a/test/libsolidity/semanticTests/array/delete/bytes_delete_element.sol b/test/libsolidity/semanticTests/array/delete/bytes_delete_element.sol index b9e2aa839..ebd8ca1c4 100644 --- a/test/libsolidity/semanticTests/array/delete/bytes_delete_element.sol +++ b/test/libsolidity/semanticTests/array/delete/bytes_delete_element.sol @@ -16,6 +16,6 @@ contract c { } // ---- // test1() -> true -// gas irOptimized: 207928 +// gas irOptimized: 207933 // gas legacy: 254650 // gas legacyOptimized: 247384 diff --git a/test/libsolidity/semanticTests/array/pop/byte_array_pop_copy_long.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_copy_long.sol index 743a848d9..ad86f99ac 100644 --- a/test/libsolidity/semanticTests/array/pop/byte_array_pop_copy_long.sol +++ b/test/libsolidity/semanticTests/array/pop/byte_array_pop_copy_long.sol @@ -11,5 +11,5 @@ contract c { // ---- // test() -> 0x20, 29, 0x0303030303030303030303030303030303030303030303030303030303000000 // gas irOptimized: 109341 -// gas legacy: 126728 -// gas legacyOptimized: 123444 +// gas legacy: 134884 +// gas legacyOptimized: 131171 diff --git a/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty.sol index 0753535e5..622e39ac7 100644 --- a/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty.sol +++ b/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty.sol @@ -17,6 +17,6 @@ contract c { // ---- // test() -> true // gas irOptimized: 180539 -// gas legacy: 228685 -// gas legacyOptimized: 209662 +// gas legacy: 238713 +// gas legacyOptimized: 218665 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty_garbage_ref.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty_garbage_ref.sol index 66fb16775..99473d604 100644 --- a/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty_garbage_ref.sol +++ b/test/libsolidity/semanticTests/array/pop/byte_array_pop_long_storage_empty_garbage_ref.sol @@ -16,6 +16,6 @@ contract c { // ---- // test() -> // gas irOptimized: 142639 -// gas legacy: 164430 -// gas legacyOptimized: 158513 +// gas legacy: 174458 +// gas legacyOptimized: 168008 // storageEmpty -> 1 diff --git a/test/libsolidity/semanticTests/array/pop/byte_array_pop_masking_long.sol b/test/libsolidity/semanticTests/array/pop/byte_array_pop_masking_long.sol index 0be102f17..4af2822bf 100644 --- a/test/libsolidity/semanticTests/array/pop/byte_array_pop_masking_long.sol +++ b/test/libsolidity/semanticTests/array/pop/byte_array_pop_masking_long.sol @@ -11,5 +11,5 @@ contract c { // ---- // test() -> 0x20, 33, 0x303030303030303030303030303030303030303030303030303030303030303, 0x0300000000000000000000000000000000000000000000000000000000000000 // gas irOptimized: 108146 -// gas legacy: 125610 -// gas legacyOptimized: 122582 +// gas legacy: 134000 +// gas legacyOptimized: 130530 diff --git a/test/libsolidity/semanticTests/array/push/byte_array_push_transition.sol b/test/libsolidity/semanticTests/array/push/byte_array_push_transition.sol index 6dff7da41..4c7491877 100644 --- a/test/libsolidity/semanticTests/array/push/byte_array_push_transition.sol +++ b/test/libsolidity/semanticTests/array/push/byte_array_push_transition.sol @@ -16,5 +16,5 @@ contract c { // ---- // test() -> 0 // gas irOptimized: 173650 -// gas legacy: 216790 -// gas legacyOptimized: 203886 +// gas legacy: 226350 +// gas legacyOptimized: 212471 diff --git a/test/libsolidity/semanticTests/array/push/push_no_args_bytes.sol b/test/libsolidity/semanticTests/array/push/push_no_args_bytes.sol index 3c7b8b44b..85df2d890 100644 --- a/test/libsolidity/semanticTests/array/push/push_no_args_bytes.sol +++ b/test/libsolidity/semanticTests/array/push/push_no_args_bytes.sol @@ -21,9 +21,9 @@ contract C { // ---- // l() -> 0 // g(uint256): 70 -> -// gas irOptimized: 183587 -// gas legacy: 183811 -// gas legacyOptimized: 179218 +// gas irOptimized: 187549 +// gas legacy: 200625 +// gas legacyOptimized: 195542 // l() -> 70 // a(uint256): 69 -> left(69) // f() -> diff --git a/test/libsolidity/semanticTests/events/event_indexed_string.sol b/test/libsolidity/semanticTests/events/event_indexed_string.sol index 1ef41edb7..7337d392c 100644 --- a/test/libsolidity/semanticTests/events/event_indexed_string.sol +++ b/test/libsolidity/semanticTests/events/event_indexed_string.sol @@ -18,5 +18,5 @@ contract C { // deposit() -> // ~ emit E(string,uint256[4]): #0xa7fb06bb999a5eb9aff9e0779953f4e1e4ce58044936c2f51c7fb879b85c08bd, #0xe755d8cc1a8cde16a2a31160dcd8017ac32d7e2f13215b29a23cdae40a78aa81 // gas irOptimized: 333479 -// gas legacy: 388679 -// gas legacyOptimized: 374441 +// gas legacy: 410173 +// gas legacyOptimized: 394765