mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
[Sol->Yul] Implement uint256[] memory arrays
This commit is contained in:
parent
035fde2932
commit
0f24fcea17
@ -754,6 +754,36 @@ string YulUtilFunctions::storageArrayIndexAccessFunction(ArrayType const& _type)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::memoryArrayIndexAccessFunction(ArrayType const& _type)
|
||||||
|
{
|
||||||
|
string functionName = "memory_array_index_access_" + _type.identifier();
|
||||||
|
return m_functionCollector->createFunction(functionName, [&]() {
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(baseRef, index) -> addr {
|
||||||
|
if iszero(lt(index, <arrayLen>(baseRef))) {
|
||||||
|
invalid()
|
||||||
|
}
|
||||||
|
|
||||||
|
let offset := mul(index, <stride>)
|
||||||
|
<?dynamicallySized>
|
||||||
|
offset := add(offset, 32)
|
||||||
|
</dynamicallySized>
|
||||||
|
addr := add(baseRef, offset)
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("arrayLen", arrayLengthFunction(_type))
|
||||||
|
("stride", to_string(_type.memoryStride()))
|
||||||
|
("dynamicallySized", _type.isDynamicallySized())
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::calldataArrayIndexAccessFunction(ArrayType const& /*_type*/)
|
||||||
|
{
|
||||||
|
solUnimplemented("Calldata arrays not yet implemented!");
|
||||||
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
|
string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
|
||||||
{
|
{
|
||||||
solAssert(!_type.isByteArray(), "");
|
solAssert(!_type.isByteArray(), "");
|
||||||
@ -881,6 +911,16 @@ string YulUtilFunctions::readFromStorageDynamic(Type const& _type, bool _splitFu
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::readFromMemory(Type const& _type)
|
||||||
|
{
|
||||||
|
return readFromMemoryOrCalldata(_type, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::readFromCalldata(Type const& _type)
|
||||||
|
{
|
||||||
|
return readFromMemoryOrCalldata(_type, true);
|
||||||
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::updateStorageValueFunction(Type const& _type, boost::optional<unsigned> const _offset)
|
string YulUtilFunctions::updateStorageValueFunction(Type const& _type, boost::optional<unsigned> const _offset)
|
||||||
{
|
{
|
||||||
string const functionName =
|
string const functionName =
|
||||||
@ -922,6 +962,64 @@ string YulUtilFunctions::updateStorageValueFunction(Type const& _type, boost::op
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::writeToMemoryFunction(Type const& _type)
|
||||||
|
{
|
||||||
|
string const functionName =
|
||||||
|
string("write_to_memory_") +
|
||||||
|
_type.identifier();
|
||||||
|
|
||||||
|
return m_functionCollector->createFunction(functionName, [&] {
|
||||||
|
solAssert(!dynamic_cast<StringLiteralType const*>(&_type), "");
|
||||||
|
if (auto ref = dynamic_cast<ReferenceType const*>(&_type))
|
||||||
|
{
|
||||||
|
solAssert(
|
||||||
|
ref->location() == DataLocation::Memory,
|
||||||
|
"Can only update types with location memory."
|
||||||
|
);
|
||||||
|
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(memPtr, value) {
|
||||||
|
mstore(memPtr, value)
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
.render();
|
||||||
|
}
|
||||||
|
else if (
|
||||||
|
_type.category() == Type::Category::Function &&
|
||||||
|
dynamic_cast<FunctionType const&>(_type).kind() == FunctionType::Kind::External
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(memPtr, addr, selector) {
|
||||||
|
mstore(memPtr, <combine>(addr, selector))
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("combine", combineExternalFunctionIdFunction())
|
||||||
|
.render();
|
||||||
|
}
|
||||||
|
else if (_type.isValueType())
|
||||||
|
{
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(memPtr, value) {
|
||||||
|
mstore(memPtr, <cleanup>(value))
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("cleanup", cleanupFunction(_type))
|
||||||
|
.render();
|
||||||
|
}
|
||||||
|
else // Should never happen
|
||||||
|
{
|
||||||
|
solAssert(
|
||||||
|
false,
|
||||||
|
"Memory store of type " + _type.toString(true) + " not allowed."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::extractFromStorageValueDynamic(Type const& _type, bool _splitFunctionTypes)
|
string YulUtilFunctions::extractFromStorageValueDynamic(Type const& _type, bool _splitFunctionTypes)
|
||||||
{
|
{
|
||||||
solUnimplementedAssert(!_splitFunctionTypes, "");
|
solUnimplementedAssert(!_splitFunctionTypes, "");
|
||||||
@ -1038,6 +1136,28 @@ string YulUtilFunctions::allocationFunction()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::allocateMemoryArrayFunction(ArrayType const& _type)
|
||||||
|
{
|
||||||
|
solUnimplementedAssert(!_type.isByteArray(), "");
|
||||||
|
|
||||||
|
string functionName = "allocate_memory_array_" + _type.identifier();
|
||||||
|
return m_functionCollector->createFunction(functionName, [&]() {
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(length) -> memPtr {
|
||||||
|
memPtr := <alloc>(<allocSize>(length))
|
||||||
|
<?dynamic>
|
||||||
|
mstore(memPtr, length)
|
||||||
|
</dynamic>
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("alloc", allocationFunction())
|
||||||
|
("allocSize", arrayAllocationSizeFunction(_type))
|
||||||
|
("dynamic", _type.isDynamicallySized())
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
||||||
{
|
{
|
||||||
if (_from.sizeOnStack() != 1 || _to.sizeOnStack() != 1)
|
if (_from.sizeOnStack() != 1 || _to.sizeOnStack() != 1)
|
||||||
@ -1146,8 +1266,25 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
|||||||
solUnimplemented("Fixed point types not implemented.");
|
solUnimplemented("Fixed point types not implemented.");
|
||||||
break;
|
break;
|
||||||
case Type::Category::Array:
|
case Type::Category::Array:
|
||||||
solUnimplementedAssert(false, "Array conversion not implemented.");
|
{
|
||||||
|
bool equal = _from == _to;
|
||||||
|
|
||||||
|
if (!equal)
|
||||||
|
{
|
||||||
|
ArrayType const& from = dynamic_cast<decltype(from)>(_from);
|
||||||
|
ArrayType const& to = dynamic_cast<decltype(to)>(_to);
|
||||||
|
|
||||||
|
if (*from.mobileType() == *to.mobileType())
|
||||||
|
equal = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (equal)
|
||||||
|
body = "converted := value";
|
||||||
|
else
|
||||||
|
solUnimplementedAssert(false, "Array conversion not implemented.");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case Type::Category::Struct:
|
case Type::Category::Struct:
|
||||||
solUnimplementedAssert(false, "Struct conversion not implemented.");
|
solUnimplementedAssert(false, "Struct conversion not implemented.");
|
||||||
break;
|
break;
|
||||||
@ -1579,3 +1716,60 @@ string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromCalldata)
|
||||||
|
{
|
||||||
|
string functionName =
|
||||||
|
string("read_from_") +
|
||||||
|
(_fromCalldata ? "calldata" : "memory") +
|
||||||
|
_type.identifier();
|
||||||
|
|
||||||
|
// TODO use ABI functions for handling calldata
|
||||||
|
if (_fromCalldata)
|
||||||
|
solAssert(!_type.isDynamicallyEncoded(), "");
|
||||||
|
|
||||||
|
return m_functionCollector->createFunction(functionName, [&] {
|
||||||
|
if (auto refType = dynamic_cast<ReferenceType const*>(&_type))
|
||||||
|
{
|
||||||
|
solAssert(refType->sizeOnStack() == 1, "");
|
||||||
|
solAssert(!_fromCalldata, "");
|
||||||
|
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(memPtr) -> value {
|
||||||
|
value := mload(memPtr)
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
solAssert(_type.isValueType(), "");
|
||||||
|
|
||||||
|
if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
|
||||||
|
if (funType->kind() == FunctionType::Kind::External)
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(memPtr) -> addr, selector {
|
||||||
|
let combined := <load>(memPtr)
|
||||||
|
addr, selector := <splitFunction>(combined)
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("load", _fromCalldata ? "calldataload" : "mload")
|
||||||
|
("splitFunction", splitExternalFunctionIdFunction())
|
||||||
|
.render();
|
||||||
|
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(memPtr) -> value {
|
||||||
|
value := <load>(memPtr)
|
||||||
|
<?needsValidation>
|
||||||
|
value := <validate>(value)
|
||||||
|
</needsValidation>
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("load", _fromCalldata ? "calldataload" : "mload")
|
||||||
|
("needsValidation", _fromCalldata)
|
||||||
|
("validate", _fromCalldata ? validatorFunction(_type) : "")
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -146,6 +146,17 @@ public:
|
|||||||
/// signature: (array, index) -> slot, offset
|
/// signature: (array, index) -> slot, offset
|
||||||
std::string storageArrayIndexAccessFunction(ArrayType const& _type);
|
std::string storageArrayIndexAccessFunction(ArrayType const& _type);
|
||||||
|
|
||||||
|
/// @returns the name of a function that returns the memory address for the
|
||||||
|
/// given array base ref and index.
|
||||||
|
/// Causes invalid opcode on out of range access.
|
||||||
|
/// signature: (baseRef, index) -> address
|
||||||
|
std::string memoryArrayIndexAccessFunction(ArrayType const& _type);
|
||||||
|
|
||||||
|
/// @returns the name of a function that returns the calldata address for the
|
||||||
|
/// given array base ref and index.
|
||||||
|
/// signature: (baseRef, index) -> address
|
||||||
|
std::string calldataArrayIndexAccessFunction(ArrayType const& _type);
|
||||||
|
|
||||||
/// @returns the name of a function that advances an array data pointer to the next element.
|
/// @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 every item occupies one or multiple full slots.
|
/// Only works for memory arrays, calldata arrays and storage arrays that every item occupies one or multiple full slots.
|
||||||
std::string nextArrayElementFunction(ArrayType const& _type);
|
std::string nextArrayElementFunction(ArrayType const& _type);
|
||||||
@ -162,6 +173,14 @@ public:
|
|||||||
std::string readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes);
|
std::string readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes);
|
||||||
std::string readFromStorageDynamic(Type const& _type, bool _splitFunctionTypes);
|
std::string readFromStorageDynamic(Type const& _type, bool _splitFunctionTypes);
|
||||||
|
|
||||||
|
/// @returns a function that reads a value type from memory.
|
||||||
|
/// signature: (addr) -> value
|
||||||
|
std::string readFromMemory(Type const& _type);
|
||||||
|
/// @returns a function that reads a value type from calldata.
|
||||||
|
/// Reverts on invalid input.
|
||||||
|
/// signature: (addr) -> value
|
||||||
|
std::string readFromCalldata(Type const& _type);
|
||||||
|
|
||||||
/// @returns a function that extracts a value type from storage slot that has been
|
/// @returns a function that extracts a value type from storage slot that has been
|
||||||
/// retrieved already.
|
/// retrieved already.
|
||||||
/// Performs bit mask/sign extend cleanup and appropriate left / right shift, but not validation.
|
/// Performs bit mask/sign extend cleanup and appropriate left / right shift, but not validation.
|
||||||
@ -176,6 +195,12 @@ public:
|
|||||||
/// signature: (slot, [offset,] value)
|
/// signature: (slot, [offset,] value)
|
||||||
std::string updateStorageValueFunction(Type const& _type, boost::optional<unsigned> const _offset = boost::optional<unsigned>());
|
std::string updateStorageValueFunction(Type const& _type, boost::optional<unsigned> const _offset = boost::optional<unsigned>());
|
||||||
|
|
||||||
|
/// Returns the name of a function that will write the given value to
|
||||||
|
/// the specified address.
|
||||||
|
/// Performs a cleanup before writing for value types.
|
||||||
|
/// signature: (memPtr, value) ->
|
||||||
|
std::string writeToMemoryFunction(Type const& _type);
|
||||||
|
|
||||||
/// Performs cleanup after reading from a potentially compressed storage slot.
|
/// Performs cleanup after reading from a potentially compressed storage slot.
|
||||||
/// The function does not perform any validation, it just masks or sign-extends
|
/// The function does not perform any validation, it just masks or sign-extends
|
||||||
/// higher order bytes or left-aligns (in case of bytesNN).
|
/// higher order bytes or left-aligns (in case of bytesNN).
|
||||||
@ -197,6 +222,11 @@ public:
|
|||||||
/// Return value: pointer
|
/// Return value: pointer
|
||||||
std::string allocationFunction();
|
std::string allocationFunction();
|
||||||
|
|
||||||
|
/// @returns the name of a function that allocates a memory array.
|
||||||
|
/// For dynamic arrays it adds space for length and stores it.
|
||||||
|
/// signature: (length) -> memPtr
|
||||||
|
std::string allocateMemoryArrayFunction(ArrayType const& _type);
|
||||||
|
|
||||||
/// @returns the name of the function that converts a value of type @a _from
|
/// @returns the name of the function that converts a value of type @a _from
|
||||||
/// to a value of type @a _to. The resulting vale is guaranteed to be in range
|
/// to a value of type @a _to. The resulting vale is guaranteed to be in range
|
||||||
/// (i.e. "clean"). Asserts on failure.
|
/// (i.e. "clean"). Asserts on failure.
|
||||||
@ -244,6 +274,8 @@ private:
|
|||||||
/// use exactly one variable to hold the value.
|
/// use exactly one variable to hold the value.
|
||||||
std::string conversionFunctionSpecial(Type const& _from, Type const& _to);
|
std::string conversionFunctionSpecial(Type const& _from, Type const& _to);
|
||||||
|
|
||||||
|
std::string readFromMemoryOrCalldata(Type const& _type, bool _fromCalldata);
|
||||||
|
|
||||||
langutil::EVMVersion m_evmVersion;
|
langutil::EVMVersion m_evmVersion;
|
||||||
std::shared_ptr<MultiUseYulFunctionCollector> m_functionCollector;
|
std::shared_ptr<MultiUseYulFunctionCollector> m_functionCollector;
|
||||||
};
|
};
|
||||||
|
@ -593,8 +593,22 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Array creation using new
|
||||||
|
case FunctionType::Kind::ObjectCreation:
|
||||||
|
{
|
||||||
|
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_functionCall.annotation().type);
|
||||||
|
solAssert(arguments.size() == 1, "");
|
||||||
|
|
||||||
|
defineExpression(_functionCall) <<
|
||||||
|
m_utils.allocateMemoryArrayFunction(arrayType) <<
|
||||||
|
"(" <<
|
||||||
|
expressionAsType(*arguments[0], *TypeProvider::uint256()) <<
|
||||||
|
")\n";
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
solUnimplemented("");
|
solUnimplemented("FunctionKind " + toString(static_cast<int>(functionType->kind())) + " not yet implemented");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -756,11 +770,12 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
case DataLocation::Memory:
|
case DataLocation::Memory:
|
||||||
solUnimplementedAssert(false, "");
|
defineExpression(_memberAccess) <<
|
||||||
//m_context << Instruction::MLOAD;
|
"mload(" <<
|
||||||
|
m_context.variable(_memberAccess.expression()) <<
|
||||||
|
")\n";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::Category::FixedBytes:
|
case Type::Category::FixedBytes:
|
||||||
@ -851,13 +866,29 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DataLocation::Memory:
|
case DataLocation::Memory:
|
||||||
solUnimplementedAssert(false, "");
|
{
|
||||||
break;
|
string const memAddress =
|
||||||
case DataLocation::CallData:
|
m_utils.memoryArrayIndexAccessFunction(arrayType) +
|
||||||
solUnimplementedAssert(false, "");
|
"(" +
|
||||||
break;
|
m_context.variable(_indexAccess.baseExpression()) +
|
||||||
}
|
", " +
|
||||||
|
expressionAsType(*_indexAccess.indexExpression(), *TypeProvider::uint256()) +
|
||||||
|
")";
|
||||||
|
|
||||||
|
setLValue(_indexAccess, make_unique<IRMemoryItem>(
|
||||||
|
m_context,
|
||||||
|
memAddress,
|
||||||
|
false,
|
||||||
|
*arrayType.baseType()
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DataLocation::CallData:
|
||||||
|
{
|
||||||
|
solUnimplemented("calldata not yet implemented!");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (baseType.category() == Type::Category::FixedBytes)
|
else if (baseType.category() == Type::Category::FixedBytes)
|
||||||
solUnimplementedAssert(false, "");
|
solUnimplementedAssert(false, "");
|
||||||
|
@ -147,7 +147,7 @@ IRStorageArrayLength::IRStorageArrayLength(IRGenerationContext& _context, string
|
|||||||
|
|
||||||
string IRStorageArrayLength::retrieveValue() const
|
string IRStorageArrayLength::retrieveValue() const
|
||||||
{
|
{
|
||||||
return m_context.utils().arrayLengthFunction(m_arrayType) + "(" + m_slot + ")\n";
|
return m_context.utils().arrayLengthFunction(m_arrayType) + "(" + m_slot + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
string IRStorageArrayLength::storeValue(std::string const& _value, Type const& _type) const
|
string IRStorageArrayLength::storeValue(std::string const& _value, Type const& _type) const
|
||||||
@ -166,3 +166,82 @@ string IRStorageArrayLength::setToZero() const
|
|||||||
{
|
{
|
||||||
return storeValue("0", *TypeProvider::uint256());
|
return storeValue("0", *TypeProvider::uint256());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IRMemoryItem::IRMemoryItem(
|
||||||
|
IRGenerationContext& _context,
|
||||||
|
std::string _address,
|
||||||
|
bool _byteArrayElement,
|
||||||
|
Type const& _type
|
||||||
|
):
|
||||||
|
IRLValue(_context, &_type),
|
||||||
|
m_address(move(_address)),
|
||||||
|
m_byteArrayElement(_byteArrayElement)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
string IRMemoryItem::retrieveValue() const
|
||||||
|
{
|
||||||
|
if (m_byteArrayElement)
|
||||||
|
return m_context.utils().cleanupFunction(*m_type) +
|
||||||
|
"(mload(" +
|
||||||
|
m_address +
|
||||||
|
"))";
|
||||||
|
|
||||||
|
if (m_type->isValueType())
|
||||||
|
return m_context.utils().readFromMemory(*m_type) +
|
||||||
|
"(" +
|
||||||
|
m_address +
|
||||||
|
")";
|
||||||
|
else
|
||||||
|
return "mload(" + m_address + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRMemoryItem::storeValue(string const& _value, Type const& _type) const
|
||||||
|
{
|
||||||
|
if (!m_type->isValueType())
|
||||||
|
{
|
||||||
|
solUnimplementedAssert(_type == *m_type, "Conversion not implemented for assignment to memory.");
|
||||||
|
|
||||||
|
solAssert(m_type->sizeOnStack() == 1, "");
|
||||||
|
solAssert(dynamic_cast<ReferenceType const*>(m_type), "");
|
||||||
|
|
||||||
|
return "mstore(" + m_address + ", " + _value + ")\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
solAssert(_type.isValueType(), "");
|
||||||
|
|
||||||
|
string prepared = _value;
|
||||||
|
|
||||||
|
// Exists to see if this case ever happens
|
||||||
|
solAssert(_type == *m_type, "");
|
||||||
|
|
||||||
|
if (_type != *m_type)
|
||||||
|
prepared =
|
||||||
|
m_context.utils().conversionFunction(_type, *m_type) +
|
||||||
|
"(" +
|
||||||
|
_value +
|
||||||
|
")";
|
||||||
|
else
|
||||||
|
prepared =
|
||||||
|
m_context.utils().cleanupFunction(*m_type) +
|
||||||
|
"(" +
|
||||||
|
_value +
|
||||||
|
")";
|
||||||
|
|
||||||
|
if (m_byteArrayElement)
|
||||||
|
{
|
||||||
|
solAssert(*m_type == *TypeProvider::byte(), "");
|
||||||
|
return "mstore8(" + m_address + ", byte(0, " + prepared + "))\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return m_context.utils().writeToMemoryFunction(*m_type) +
|
||||||
|
"(" +
|
||||||
|
m_address +
|
||||||
|
", " +
|
||||||
|
prepared +
|
||||||
|
")\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
string IRMemoryItem::setToZero() const
|
||||||
|
{
|
||||||
|
return storeValue(m_context.utils().zeroValueFunction(*m_type) + "()", *m_type);
|
||||||
|
}
|
||||||
|
@ -127,5 +127,23 @@ private:
|
|||||||
std::string const m_slot;
|
std::string const m_slot;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class IRMemoryItem: public IRLValue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IRMemoryItem(
|
||||||
|
IRGenerationContext& _context,
|
||||||
|
std::string _address,
|
||||||
|
bool _byteArrayElement,
|
||||||
|
Type const& _type
|
||||||
|
);
|
||||||
|
std::string retrieveValue() const override;
|
||||||
|
std::string storeValue(std::string const& _value, Type const& _type) const override;
|
||||||
|
|
||||||
|
std::string setToZero() const override;
|
||||||
|
private:
|
||||||
|
std::string const m_address;
|
||||||
|
bool m_byteArrayElement;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
contract C {
|
||||||
|
function test(uint256 len, uint idx) public returns (uint256)
|
||||||
|
{
|
||||||
|
uint[] memory array = new uint[](len);
|
||||||
|
uint result = receiver(array, idx);
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < array.length; i++)
|
||||||
|
require(array[i] == i + 1);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
function receiver(uint[] memory array, uint idx) public returns (uint256)
|
||||||
|
{
|
||||||
|
for (uint256 i = 0; i < array.length; i++)
|
||||||
|
array[i] = i + 1;
|
||||||
|
|
||||||
|
return array[idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: true
|
||||||
|
// ----
|
||||||
|
// test(uint256,uint256): 0,0 -> FAILURE
|
||||||
|
// test(uint256,uint256): 1,0 -> 1
|
||||||
|
// test(uint256,uint256): 10,5 -> 6
|
||||||
|
// test(uint256,uint256): 10,50 -> FAILURE
|
@ -0,0 +1,13 @@
|
|||||||
|
contract C {
|
||||||
|
function create(uint256 len) public returns (uint256)
|
||||||
|
{
|
||||||
|
uint[] memory array = new uint[](len);
|
||||||
|
return array.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: true
|
||||||
|
// ----
|
||||||
|
// create(uint256): 0 -> 0
|
||||||
|
// create(uint256): 7 -> 7
|
||||||
|
// create(uint256): 10 -> 10
|
@ -0,0 +1,35 @@
|
|||||||
|
contract C {
|
||||||
|
function index(uint256 len) public returns (bool)
|
||||||
|
{
|
||||||
|
uint[] memory array = new uint[](len);
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < len; i++)
|
||||||
|
array[i] = i + 1;
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < len; i++)
|
||||||
|
require(array[i] == i + 1, "Unexpected value in array!");
|
||||||
|
|
||||||
|
return array.length == len;
|
||||||
|
}
|
||||||
|
function accessIndex(uint256 len, int256 idx) public returns (uint256)
|
||||||
|
{
|
||||||
|
uint[] memory array = new uint[](len);
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < len; i++)
|
||||||
|
array[i] = i + 1;
|
||||||
|
|
||||||
|
return array[uint256(idx)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: true
|
||||||
|
// ----
|
||||||
|
// index(uint256): 0 -> true
|
||||||
|
// index(uint256): 10 -> true
|
||||||
|
// index(uint256): 20 -> true
|
||||||
|
// index(uint256): 0xFF -> true
|
||||||
|
// accessIndex(uint256,int256): 10,1 -> 2
|
||||||
|
// accessIndex(uint256,int256): 10,0 -> 1
|
||||||
|
// accessIndex(uint256,int256): 10,11 -> FAILURE
|
||||||
|
// accessIndex(uint256,int256): 10,10 -> FAILURE
|
||||||
|
// accessIndex(uint256,int256): 10,-1 -> FAILURE
|
@ -17,10 +17,6 @@ contract C {
|
|||||||
|
|
||||||
return func() == internal_func();
|
return func() == internal_func();
|
||||||
}
|
}
|
||||||
function external_func() external pure returns (int8)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// ====
|
// ====
|
||||||
// compileViaYul: true
|
// compileViaYul: true
|
||||||
|
Loading…
Reference in New Issue
Block a user