From 346c512cd7a4b8355b7aa2b9f82c721c8d68ea21 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Thu, 20 Jun 2019 14:50:39 +0200 Subject: [PATCH 1/2] [Sol->Yul] Implement _slot/_offset suffix for storage variables --- .../codegen/ir/IRGeneratorForStatements.cpp | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 80a09ccbd..db5b9813f 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -56,6 +56,35 @@ struct CopyTranslate: public yul::ASTCopier using ASTCopier::operator(); + yul::Expression operator()(yul::Identifier const& _identifier) override + { + if (m_references.count(&_identifier)) + { + auto const& reference = m_references.at(&_identifier); + auto const varDecl = dynamic_cast(reference.declaration); + solUnimplementedAssert(varDecl, ""); + + if (reference.isOffset || reference.isSlot) + { + solAssert(reference.isOffset != reference.isSlot, ""); + + pair slot_offset = m_context.storageLocationOfVariable(*varDecl); + + string const value = reference.isSlot ? + slot_offset.first.str() : + to_string(slot_offset.second); + + return yul::Literal{ + _identifier.location, + yul::LiteralKind::Number, + yul::YulString{value}, + yul::YulString{"uint256"} + }; + } + } + return ASTCopier::operator()(_identifier); + } + yul::YulString translateIdentifier(yul::YulString _name) override { // Strictly, the dialect used by inline assembly (m_dialect) could be different @@ -76,9 +105,10 @@ struct CopyTranslate: public yul::ASTCopier auto const& reference = m_references.at(&_identifier); auto const varDecl = dynamic_cast(reference.declaration); solUnimplementedAssert(varDecl, ""); - solUnimplementedAssert( + + solAssert( reference.isOffset == false && reference.isSlot == false, - "" + "Should not be called for offset/slot" ); return yul::Identifier{ From 1dd63f416edd0af9cea3edb68fb80d4dcb6c5813 Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Thu, 13 Jun 2019 17:22:24 +0200 Subject: [PATCH 2/2] [Sol->Yul] Implement index access for storage arrays --- libsolidity/codegen/YulUtilFunctions.cpp | 30 +++++++++++ libsolidity/codegen/YulUtilFunctions.h | 6 +++ .../codegen/ir/IRGeneratorForStatements.cpp | 40 ++++++++++++++- libsolidity/codegen/ir/IRLValue.cpp | 3 +- .../viaYul/array_storage_index_access.sol | 26 ++++++++++ .../array_storage_index_boundery_test.sol | 21 ++++++++ .../array_storage_index_zeroed_test.sol | 51 +++++++++++++++++++ 7 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 test/libsolidity/semanticTests/viaYul/array_storage_index_access.sol create mode 100644 test/libsolidity/semanticTests/viaYul/array_storage_index_boundery_test.sol create mode 100644 test/libsolidity/semanticTests/viaYul/array_storage_index_zeroed_test.sol diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 4c8b20636..643de4c7c 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -690,6 +690,36 @@ string YulUtilFunctions::arrayDataAreaFunction(ArrayType const& _type) }); } +string YulUtilFunctions::storageArrayIndexAccessFunction(ArrayType const& _type) +{ + solUnimplementedAssert(_type.baseType()->storageBytes() > 16, ""); + + string functionName = "storage_array_index_access_" + _type.identifier(); + return m_functionCollector->createFunction(functionName, [&]() { + return Whiskers(R"( + function (array, index) -> slot, offset { + if iszero(lt(index, (array))) { + invalid() + } + + let data := (array) + + + + slot := add(data, mul(index, )) + offset := 0 + + } + )") + ("functionName", functionName) + ("arrayLen", arrayLengthFunction(_type)) + ("dataAreaFunc", arrayDataAreaFunction(_type)) + ("multipleItemsPerSlot", _type.baseType()->storageBytes() <= 16) + ("storageSize", _type.baseType()->storageSize().str()) + .render(); + }); +} + string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type) { solAssert(!_type.isByteArray(), ""); diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index ec828ce4f..fd3bef809 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -136,6 +136,12 @@ public: /// a memory pointer or a calldata pointer to the slot number / memory pointer / calldata pointer /// for the data position of an array which is stored in that slot / memory area / calldata area. std::string arrayDataAreaFunction(ArrayType const& _type); + + /// @returns the name of a function that returns the slot and offset for the + /// given array and index + /// signature: (array, index) -> slot, offset + std::string storageArrayIndexAccessFunction(ArrayType const& _type); + /// @returns the name of a function that advances an array data pointer to the next element. /// Only works for memory arrays, calldata arrays and storage arrays that store one item per slot. std::string nextArrayElementFunction(ArrayType const& _type); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index db5b9813f..5ddb9f29b 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -820,7 +820,45 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) )); } else if (baseType.category() == Type::Category::Array) - solUnimplementedAssert(false, ""); + { + ArrayType const& arrayType = dynamic_cast(baseType); + solAssert(_indexAccess.indexExpression(), "Index expression expected."); + + switch (arrayType.location()) + { + case DataLocation::Storage: + { + string slot = m_context.newYulVariable(); + string offset = m_context.newYulVariable(); + + m_code << Whiskers(R"( + let , := (, ) + )") + ("slot", slot) + ("offset", offset) + ("indexFunc", m_utils.storageArrayIndexAccessFunction(arrayType)) + ("array", m_context.variable(_indexAccess.baseExpression())) + ("index", m_context.variable(*_indexAccess.indexExpression())) + .render(); + + setLValue(_indexAccess, make_unique( + m_context, + slot, + offset, + *_indexAccess.annotation().type + )); + + break; + } + case DataLocation::Memory: + solUnimplementedAssert(false, ""); + break; + case DataLocation::CallData: + solUnimplementedAssert(false, ""); + break; + } + + } else if (baseType.category() == Type::Category::FixedBytes) solUnimplementedAssert(false, ""); else if (baseType.category() == Type::Category::TypeType) diff --git a/libsolidity/codegen/ir/IRLValue.cpp b/libsolidity/codegen/ir/IRLValue.cpp index 010eecb75..f1e5cbc7c 100644 --- a/libsolidity/codegen/ir/IRLValue.cpp +++ b/libsolidity/codegen/ir/IRLValue.cpp @@ -135,7 +135,8 @@ string IRStorageItem::storeValue(string const& _value, Type const& _sourceType) string IRStorageItem::setToZero() const { - solUnimplemented("Delete for storage location not yet implemented"); + solUnimplementedAssert(m_type->isValueType(), ""); + return storeValue(m_context.utils().zeroValueFunction(*m_type) + "()", *m_type); } IRStorageArrayLength::IRStorageArrayLength(IRGenerationContext& _context, string _slot, Type const& _type, ArrayType const& _arrayType): diff --git a/test/libsolidity/semanticTests/viaYul/array_storage_index_access.sol b/test/libsolidity/semanticTests/viaYul/array_storage_index_access.sol new file mode 100644 index 000000000..ca79ea742 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_storage_index_access.sol @@ -0,0 +1,26 @@ +contract C { + uint[] storageArray; + function test_indicies(uint256 len) public + { + storageArray.length = len; + + for (uint i = 0; i < len; i++) + storageArray[i] = i + 1; + + for (uint i = 0; i < len; i++) + require(storageArray[i] == i + 1); + } +} +// ==== +// compileViaYul: true +// ---- +// test_indicies(uint256): 1 -> +// test_indicies(uint256): 129 -> +// test_indicies(uint256): 5 -> +// test_indicies(uint256): 10 -> +// test_indicies(uint256): 15 -> +// test_indicies(uint256): 0xFF -> +// test_indicies(uint256): 1000 -> +// test_indicies(uint256): 129 -> +// test_indicies(uint256): 128 -> +// test_indicies(uint256): 1 -> diff --git a/test/libsolidity/semanticTests/viaYul/array_storage_index_boundery_test.sol b/test/libsolidity/semanticTests/viaYul/array_storage_index_boundery_test.sol new file mode 100644 index 000000000..8c5ee6845 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_storage_index_boundery_test.sol @@ -0,0 +1,21 @@ +contract C { + uint[] storageArray; + function test_boundery_check(uint256 len, uint256 access) public returns +(uint256) + { + storageArray.length = len; + return storageArray[access]; + } +} +// ==== +// compileViaYul: true +// ---- +// test_boundery_check(uint256, uint256): 10, 11 -> FAILURE +// test_boundery_check(uint256, uint256): 10, 9 -> 0 +// test_boundery_check(uint256, uint256): 1, 9 -> FAILURE +// test_boundery_check(uint256, uint256): 1, 1 -> FAILURE +// test_boundery_check(uint256, uint256): 10, 10 -> FAILURE +// test_boundery_check(uint256, uint256): 256, 256 -> FAILURE +// test_boundery_check(uint256, uint256): 256, 255 -> 0 +// test_boundery_check(uint256, uint256): 256, 0xFFFF -> FAILURE +// test_boundery_check(uint256, uint256): 256, 2 -> 0 diff --git a/test/libsolidity/semanticTests/viaYul/array_storage_index_zeroed_test.sol b/test/libsolidity/semanticTests/viaYul/array_storage_index_zeroed_test.sol new file mode 100644 index 000000000..344c0add5 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_storage_index_zeroed_test.sol @@ -0,0 +1,51 @@ +contract C { + uint[] storageArray; + function test_zeroed_indicies(uint256 len) public + { + storageArray.length = len; + + for (uint i = 0; i < len; i++) + storageArray[i] = i + 1; + + if (len > 3) + { + storageArray.length = 3; + + for (uint i = 3; i < len; i++) + { + assembly { + mstore(0, storageArray_slot) + let pos := add(keccak256(0, 0x20), i) + + if iszero(eq(sload(pos), 0)) { + revert(0, 0) + } + } + } + + } + + storageArray.length = 0; + storageArray.length = len; + + for (uint i = 0; i < len; i++) + { + require(storageArray[i] == 0); + + uint256 val = storageArray[i]; + uint256 check; + + assembly { check := iszero(val) } + + require(check == 1); + } + } +} +// ==== +// compileViaYul: true +// ---- +// test_zeroed_indicies(uint256): 1 -> +// test_zeroed_indicies(uint256): 5 -> +// test_zeroed_indicies(uint256): 10 -> +// test_zeroed_indicies(uint256): 15 -> +// test_zeroed_indicies(uint256): 0xFF ->