mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #13118 from ethereum/improvedCopyValueArrayToStorageFunction
Improved copy value-type-array to storage function
This commit is contained in:
commit
bed4d986f9
@ -5,6 +5,8 @@ Language Features:
|
|||||||
|
|
||||||
Compiler Features:
|
Compiler Features:
|
||||||
* TypeChecker: Support using library constants in initializers of other constants.
|
* TypeChecker: Support using library constants in initializers of other constants.
|
||||||
|
* Yul IR Code Generation: Improved copy routines for arrays with packed storage layout.
|
||||||
|
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
* Commandline Interface: Disallow the following options outside of the compiler mode: ``--via-ir``,``--metadata-literal``, ``--metadata-hash``, ``--model-checker-show-unproved``, ``--model-checker-div-mod-no-slacks``, ``--model-checker-engine``, ``--model-checker-invariants``, ``--model-checker-solvers``, ``--model-checker-timeout``, ``--model-checker-contracts``, ``--model-checker-targets``.
|
* Commandline Interface: Disallow the following options outside of the compiler mode: ``--via-ir``,``--metadata-literal``, ``--metadata-hash``, ``--model-checker-show-unproved``, ``--model-checker-div-mod-no-slacks``, ``--model-checker-engine``, ``--model-checker-invariants``, ``--model-checker-solvers``, ``--model-checker-timeout``, ``--model-checker-contracts``, ``--model-checker-targets``.
|
||||||
@ -30,6 +32,7 @@ Compiler Features:
|
|||||||
Bugfixes:
|
Bugfixes:
|
||||||
* ABI Encoder: When encoding an empty string coming from storage do not add a superfluous empty slot for data.
|
* ABI Encoder: When encoding an empty string coming from storage do not add a superfluous empty slot for data.
|
||||||
* Common Subexpression Eliminator: Process assembly items in chunks with maximum size of 2000. It helps to avoid extremely time-consuming searches during code optimization.
|
* Common Subexpression Eliminator: Process assembly items in chunks with maximum size of 2000. It helps to avoid extremely time-consuming searches during code optimization.
|
||||||
|
* Yul IR Code Generation: More robust cleanup in corner cases during memory to storage copies.
|
||||||
* Yul Optimizer: Do not remove ``returndatacopy`` in cases in which it might perform out-of-bounds reads that unconditionally revert as out-of-gas. Previously, any ``returndatacopy`` that wrote to memory that was never read from was removed without accounting for the out-of-bounds condition.
|
* Yul Optimizer: Do not remove ``returndatacopy`` in cases in which it might perform out-of-bounds reads that unconditionally revert as out-of-gas. Previously, any ``returndatacopy`` that wrote to memory that was never read from was removed without accounting for the out-of-bounds condition.
|
||||||
|
|
||||||
|
|
||||||
|
@ -1797,8 +1797,11 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
|
|||||||
|
|
||||||
if (_fromType.isByteArrayOrString())
|
if (_fromType.isByteArrayOrString())
|
||||||
return copyByteArrayToStorageFunction(_fromType, _toType);
|
return copyByteArrayToStorageFunction(_fromType, _toType);
|
||||||
if (_fromType.dataStoredIn(DataLocation::Storage) && _toType.baseType()->isValueType())
|
if (_toType.baseType()->isValueType())
|
||||||
return copyValueArrayStorageToStorageFunction(_fromType, _toType);
|
return copyValueArrayToStorageFunction(_fromType, _toType);
|
||||||
|
|
||||||
|
solAssert(_toType.storageStride() == 32);
|
||||||
|
solAssert(!_fromType.baseType()->isValueType());
|
||||||
|
|
||||||
string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
|
string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
|
||||||
return m_functionCollector.createFunction(functionName, [&](){
|
return m_functionCollector.createFunction(functionName, [&](){
|
||||||
@ -1812,43 +1815,30 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
|
|||||||
let srcPtr := <srcDataLocation>(value)
|
let srcPtr := <srcDataLocation>(value)
|
||||||
|
|
||||||
let elementSlot := <dstDataLocation>(slot)
|
let elementSlot := <dstDataLocation>(slot)
|
||||||
let elementOffset := 0
|
|
||||||
|
|
||||||
for { let i := 0 } lt(i, length) {i := add(i, 1)} {
|
for { let i := 0 } lt(i, length) {i := add(i, 1)} {
|
||||||
<?fromCalldata>
|
<?fromCalldata>
|
||||||
let <elementValues> :=
|
let <stackItems> :=
|
||||||
<?dynamicallyEncodedBase>
|
<?dynamicallyEncodedBase>
|
||||||
<accessCalldataTail>(value, srcPtr)
|
<accessCalldataTail>(value, srcPtr)
|
||||||
<!dynamicallyEncodedBase>
|
<!dynamicallyEncodedBase>
|
||||||
srcPtr
|
srcPtr
|
||||||
</dynamicallyEncodedBase>
|
</dynamicallyEncodedBase>
|
||||||
|
|
||||||
<?isValueType>
|
|
||||||
<elementValues> := <readFromCalldataOrMemory>(<elementValues>)
|
|
||||||
</isValueType>
|
|
||||||
</fromCalldata>
|
</fromCalldata>
|
||||||
|
|
||||||
<?fromMemory>
|
<?fromMemory>
|
||||||
let <elementValues> := <readFromCalldataOrMemory>(srcPtr)
|
let <stackItems> := <readFromMemoryOrCalldata>(srcPtr)
|
||||||
</fromMemory>
|
</fromMemory>
|
||||||
|
|
||||||
<?fromStorage>
|
<?fromStorage>
|
||||||
let <elementValues> := srcPtr
|
let <stackItems> := srcPtr
|
||||||
</fromStorage>
|
</fromStorage>
|
||||||
|
|
||||||
<updateStorageValue>(elementSlot, elementOffset, <elementValues>)
|
<updateStorageValue>(elementSlot, <stackItems>)
|
||||||
|
|
||||||
srcPtr := add(srcPtr, <srcStride>)
|
srcPtr := add(srcPtr, <srcStride>)
|
||||||
|
|
||||||
<?multipleItemsPerSlot>
|
elementSlot := add(elementSlot, <storageSize>)
|
||||||
elementOffset := add(elementOffset, <storageStride>)
|
|
||||||
if gt(elementOffset, sub(32, <storageStride>)) {
|
|
||||||
elementOffset := 0
|
|
||||||
elementSlot := add(elementSlot, 1)
|
|
||||||
}
|
|
||||||
<!multipleItemsPerSlot>
|
|
||||||
elementSlot := add(elementSlot, <storageSize>)
|
|
||||||
</multipleItemsPerSlot>
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
@ -1870,16 +1860,15 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
|
|||||||
}
|
}
|
||||||
templ("resizeArray", resizeArrayFunction(_toType));
|
templ("resizeArray", resizeArrayFunction(_toType));
|
||||||
templ("arrayLength",arrayLengthFunction(_fromType));
|
templ("arrayLength",arrayLengthFunction(_fromType));
|
||||||
templ("isValueType", _fromType.baseType()->isValueType());
|
|
||||||
templ("dstDataLocation", arrayDataAreaFunction(_toType));
|
templ("dstDataLocation", arrayDataAreaFunction(_toType));
|
||||||
if (fromMemory || (fromCalldata && _fromType.baseType()->isValueType()))
|
if (fromMemory || (fromCalldata && _fromType.baseType()->isValueType()))
|
||||||
templ("readFromCalldataOrMemory", readFromMemoryOrCalldata(*_fromType.baseType(), fromCalldata));
|
templ("readFromMemoryOrCalldata", readFromMemoryOrCalldata(*_fromType.baseType(), fromCalldata));
|
||||||
templ("elementValues", suffixedVariableNameList(
|
templ("stackItems", suffixedVariableNameList(
|
||||||
"elementValue_",
|
"stackItem_",
|
||||||
0,
|
0,
|
||||||
_fromType.baseType()->stackItems().size()
|
_fromType.baseType()->stackItems().size()
|
||||||
));
|
));
|
||||||
templ("updateStorageValue", updateStorageValueFunction(*_fromType.baseType(), *_toType.baseType()));
|
templ("updateStorageValue", updateStorageValueFunction(*_fromType.baseType(), *_toType.baseType(), 0));
|
||||||
templ("srcStride",
|
templ("srcStride",
|
||||||
fromCalldata ?
|
fromCalldata ?
|
||||||
to_string(_fromType.calldataStride()) :
|
to_string(_fromType.calldataStride()) :
|
||||||
@ -1887,8 +1876,6 @@ string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType,
|
|||||||
to_string(_fromType.memoryStride()) :
|
to_string(_fromType.memoryStride()) :
|
||||||
formatNumber(_fromType.baseType()->storageSize())
|
formatNumber(_fromType.baseType()->storageSize())
|
||||||
);
|
);
|
||||||
templ("multipleItemsPerSlot", _toType.storageStride() <= 16);
|
|
||||||
templ("storageStride", to_string(_toType.storageStride()));
|
|
||||||
templ("storageSize", _toType.baseType()->storageSize().str());
|
templ("storageSize", _toType.baseType()->storageSize().str());
|
||||||
|
|
||||||
return templ.render();
|
return templ.render();
|
||||||
@ -1973,8 +1960,7 @@ string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromTy
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::copyValueArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType)
|
||||||
string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType)
|
|
||||||
{
|
{
|
||||||
solAssert(_fromType.baseType()->isValueType(), "");
|
solAssert(_fromType.baseType()->isValueType(), "");
|
||||||
solAssert(_toType.baseType()->isValueType(), "");
|
solAssert(_toType.baseType()->isValueType(), "");
|
||||||
@ -1982,7 +1968,6 @@ string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const&
|
|||||||
|
|
||||||
solAssert(!_fromType.isByteArrayOrString(), "");
|
solAssert(!_fromType.isByteArrayOrString(), "");
|
||||||
solAssert(!_toType.isByteArrayOrString(), "");
|
solAssert(!_toType.isByteArrayOrString(), "");
|
||||||
solAssert(_fromType.dataStoredIn(DataLocation::Storage), "");
|
|
||||||
solAssert(_toType.dataStoredIn(DataLocation::Storage), "");
|
solAssert(_toType.dataStoredIn(DataLocation::Storage), "");
|
||||||
|
|
||||||
solAssert(_fromType.storageStride() <= _toType.storageStride(), "");
|
solAssert(_fromType.storageStride() <= _toType.storageStride(), "");
|
||||||
@ -1991,32 +1976,41 @@ string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const&
|
|||||||
string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
|
string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
|
||||||
return m_functionCollector.createFunction(functionName, [&](){
|
return m_functionCollector.createFunction(functionName, [&](){
|
||||||
Whiskers templ(R"(
|
Whiskers templ(R"(
|
||||||
function <functionName>(dst, src) {
|
function <functionName>(dst, src<?isFromDynamicCalldata>, len</isFromDynamicCalldata>) {
|
||||||
|
<?isFromStorage>
|
||||||
if eq(dst, src) { leave }
|
if eq(dst, src) { leave }
|
||||||
let length := <arrayLength>(src)
|
</isFromStorage>
|
||||||
|
let length := <arrayLength>(src<?isFromDynamicCalldata>, len</isFromDynamicCalldata>)
|
||||||
// Make sure array length is sane
|
// Make sure array length is sane
|
||||||
if gt(length, 0xffffffffffffffff) { <panic>() }
|
if gt(length, 0xffffffffffffffff) { <panic>() }
|
||||||
<resizeArray>(dst, length)
|
<resizeArray>(dst, length)
|
||||||
|
|
||||||
let srcSlot := <srcDataLocation>(src)
|
let srcPtr := <srcDataLocation>(src)
|
||||||
let dstSlot := <dstDataLocation>(dst)
|
let dstSlot := <dstDataLocation>(dst)
|
||||||
|
|
||||||
let fullSlots := div(length, <itemsPerSlot>)
|
let fullSlots := div(length, <itemsPerSlot>)
|
||||||
|
|
||||||
let srcSlotValue := sload(srcSlot)
|
<?isFromStorage>
|
||||||
|
let srcSlotValue := sload(srcPtr)
|
||||||
let srcItemIndexInSlot := 0
|
let srcItemIndexInSlot := 0
|
||||||
|
</isFromStorage>
|
||||||
|
|
||||||
for { let i := 0 } lt(i, fullSlots) { i := add(i, 1) } {
|
for { let i := 0 } lt(i, fullSlots) { i := add(i, 1) } {
|
||||||
let dstSlotValue := 0
|
let dstSlotValue := 0
|
||||||
<?sameType>
|
<?sameTypeFromStorage>
|
||||||
dstSlotValue := <maskFull>(srcSlotValue)
|
dstSlotValue := <maskFull>(srcSlotValue)
|
||||||
<updateSrcSlotValue>
|
<updateSrcPtr>
|
||||||
<!sameType>
|
<!sameTypeFromStorage>
|
||||||
<?multipleItemsPerSlotDst>for { let j := 0 } lt(j, <itemsPerSlot>) { j := add(j, 1) } </multipleItemsPerSlotDst>
|
<?multipleItemsPerSlotDst>for { let j := 0 } lt(j, <itemsPerSlot>) { j := add(j, 1) } </multipleItemsPerSlotDst>
|
||||||
{
|
{
|
||||||
let itemValue := <convert>(
|
<?isFromStorage>
|
||||||
|
let <stackItems> := <convert>(
|
||||||
<extractFromSlot>(srcSlotValue, mul(<srcStride>, srcItemIndexInSlot))
|
<extractFromSlot>(srcSlotValue, mul(<srcStride>, srcItemIndexInSlot))
|
||||||
)
|
)
|
||||||
itemValue := <prepareStore>(itemValue)
|
<!isFromStorage>
|
||||||
|
let <stackItems> := <readFromMemoryOrCalldata>(srcPtr)
|
||||||
|
</isFromStorage>
|
||||||
|
let itemValue := <prepareStore>(<stackItems>)
|
||||||
dstSlotValue :=
|
dstSlotValue :=
|
||||||
<?multipleItemsPerSlotDst>
|
<?multipleItemsPerSlotDst>
|
||||||
<updateByteSlice>(dstSlotValue, mul(<dstStride>, j), itemValue)
|
<updateByteSlice>(dstSlotValue, mul(<dstStride>, j), itemValue)
|
||||||
@ -2024,9 +2018,9 @@ string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const&
|
|||||||
itemValue
|
itemValue
|
||||||
</multipleItemsPerSlotDst>
|
</multipleItemsPerSlotDst>
|
||||||
|
|
||||||
<updateSrcSlotValue>
|
<updateSrcPtr>
|
||||||
}
|
}
|
||||||
</sameType>
|
</sameTypeFromStorage>
|
||||||
|
|
||||||
sstore(add(dstSlot, i), dstSlotValue)
|
sstore(add(dstSlot, i), dstSlotValue)
|
||||||
}
|
}
|
||||||
@ -2035,20 +2029,24 @@ string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const&
|
|||||||
let spill := sub(length, mul(fullSlots, <itemsPerSlot>))
|
let spill := sub(length, mul(fullSlots, <itemsPerSlot>))
|
||||||
if gt(spill, 0) {
|
if gt(spill, 0) {
|
||||||
let dstSlotValue := 0
|
let dstSlotValue := 0
|
||||||
<?sameType>
|
<?sameTypeFromStorage>
|
||||||
dstSlotValue := <maskBytes>(srcSlotValue, mul(spill, <srcStride>))
|
dstSlotValue := <maskBytes>(srcSlotValue, mul(spill, <srcStride>))
|
||||||
<updateSrcSlotValue>
|
<updateSrcPtr>
|
||||||
<!sameType>
|
<!sameTypeFromStorage>
|
||||||
for { let j := 0 } lt(j, spill) { j := add(j, 1) } {
|
for { let j := 0 } lt(j, spill) { j := add(j, 1) } {
|
||||||
let itemValue := <convert>(
|
<?isFromStorage>
|
||||||
|
let <stackItems> := <convert>(
|
||||||
<extractFromSlot>(srcSlotValue, mul(<srcStride>, srcItemIndexInSlot))
|
<extractFromSlot>(srcSlotValue, mul(<srcStride>, srcItemIndexInSlot))
|
||||||
)
|
)
|
||||||
itemValue := <prepareStore>(itemValue)
|
<!isFromStorage>
|
||||||
|
let <stackItems> := <readFromMemoryOrCalldata>(srcPtr)
|
||||||
|
</isFromStorage>
|
||||||
|
let itemValue := <prepareStore>(<stackItems>)
|
||||||
dstSlotValue := <updateByteSlice>(dstSlotValue, mul(<dstStride>, j), itemValue)
|
dstSlotValue := <updateByteSlice>(dstSlotValue, mul(<dstStride>, j), itemValue)
|
||||||
|
|
||||||
<updateSrcSlotValue>
|
<updateSrcPtr>
|
||||||
}
|
}
|
||||||
</sameType>
|
</sameTypeFromStorage>
|
||||||
sstore(add(dstSlot, fullSlots), dstSlotValue)
|
sstore(add(dstSlot, fullSlots), dstSlotValue)
|
||||||
}
|
}
|
||||||
</multipleItemsPerSlotDst>
|
</multipleItemsPerSlotDst>
|
||||||
@ -2056,26 +2054,37 @@ string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const&
|
|||||||
)");
|
)");
|
||||||
if (_fromType.dataStoredIn(DataLocation::Storage))
|
if (_fromType.dataStoredIn(DataLocation::Storage))
|
||||||
solAssert(!_fromType.isValueType(), "");
|
solAssert(!_fromType.isValueType(), "");
|
||||||
|
|
||||||
|
bool fromCalldata = _fromType.dataStoredIn(DataLocation::CallData);
|
||||||
|
bool fromStorage = _fromType.dataStoredIn(DataLocation::Storage);
|
||||||
templ("functionName", functionName);
|
templ("functionName", functionName);
|
||||||
templ("resizeArray", resizeArrayFunction(_toType));
|
templ("resizeArray", resizeArrayFunction(_toType));
|
||||||
templ("arrayLength", arrayLengthFunction(_fromType));
|
templ("arrayLength", arrayLengthFunction(_fromType));
|
||||||
templ("panic", panicFunction(PanicCode::ResourceError));
|
templ("panic", panicFunction(PanicCode::ResourceError));
|
||||||
|
templ("isFromDynamicCalldata", _fromType.isDynamicallySized() && fromCalldata);
|
||||||
|
templ("isFromStorage", fromStorage);
|
||||||
|
templ("readFromMemoryOrCalldata", readFromMemoryOrCalldata(*_fromType.baseType(), fromCalldata));
|
||||||
templ("srcDataLocation", arrayDataAreaFunction(_fromType));
|
templ("srcDataLocation", arrayDataAreaFunction(_fromType));
|
||||||
templ("dstDataLocation", arrayDataAreaFunction(_toType));
|
templ("dstDataLocation", arrayDataAreaFunction(_toType));
|
||||||
templ("srcStride", to_string(_fromType.storageStride()));
|
templ("srcStride", to_string(_fromType.storageStride()));
|
||||||
|
templ("stackItems", suffixedVariableNameList(
|
||||||
|
"stackItem_",
|
||||||
|
0,
|
||||||
|
_fromType.baseType()->stackItems().size()
|
||||||
|
));
|
||||||
unsigned itemsPerSlot = 32 / _toType.storageStride();
|
unsigned itemsPerSlot = 32 / _toType.storageStride();
|
||||||
templ("itemsPerSlot", to_string(itemsPerSlot));
|
templ("itemsPerSlot", to_string(itemsPerSlot));
|
||||||
templ("multipleItemsPerSlotDst", itemsPerSlot > 1);
|
templ("multipleItemsPerSlotDst", itemsPerSlot > 1);
|
||||||
bool sameType = *_fromType.baseType() == *_toType.baseType();
|
bool sameTypeFromStorage = fromStorage && (*_fromType.baseType() == *_toType.baseType());
|
||||||
if (auto functionType = dynamic_cast<FunctionType const*>(_fromType.baseType()))
|
if (auto functionType = dynamic_cast<FunctionType const*>(_fromType.baseType()))
|
||||||
{
|
{
|
||||||
solAssert(functionType->equalExcludingStateMutability(
|
solAssert(functionType->equalExcludingStateMutability(
|
||||||
dynamic_cast<FunctionType const&>(*_toType.baseType())
|
dynamic_cast<FunctionType const&>(*_toType.baseType())
|
||||||
));
|
));
|
||||||
sameType = true;
|
sameTypeFromStorage = fromStorage;
|
||||||
}
|
}
|
||||||
templ("sameType", sameType);
|
templ("sameTypeFromStorage", sameTypeFromStorage);
|
||||||
if (sameType)
|
if (sameTypeFromStorage)
|
||||||
{
|
{
|
||||||
templ("maskFull", maskLowerOrderBytesFunction(itemsPerSlot * _toType.storageStride()));
|
templ("maskFull", maskLowerOrderBytesFunction(itemsPerSlot * _toType.storageStride()));
|
||||||
templ("maskBytes", maskLowerOrderBytesFunctionDynamic());
|
templ("maskBytes", maskLowerOrderBytesFunctionDynamic());
|
||||||
@ -2088,24 +2097,32 @@ string YulUtilFunctions::copyValueArrayStorageToStorageFunction(ArrayType const&
|
|||||||
templ("convert", conversionFunction(*_fromType.baseType(), *_toType.baseType()));
|
templ("convert", conversionFunction(*_fromType.baseType(), *_toType.baseType()));
|
||||||
templ("prepareStore", prepareStoreFunction(*_toType.baseType()));
|
templ("prepareStore", prepareStoreFunction(*_toType.baseType()));
|
||||||
}
|
}
|
||||||
templ("updateSrcSlotValue", Whiskers(R"(
|
if (fromStorage)
|
||||||
<?srcReadMultiPerSlot>
|
templ("updateSrcPtr", Whiskers(R"(
|
||||||
srcItemIndexInSlot := add(srcItemIndexInSlot, 1)
|
<?srcReadMultiPerSlot>
|
||||||
if eq(srcItemIndexInSlot, <srcItemsPerSlot>) {
|
srcItemIndexInSlot := add(srcItemIndexInSlot, 1)
|
||||||
// here we are done with this slot, we need to read next one
|
if eq(srcItemIndexInSlot, <srcItemsPerSlot>) {
|
||||||
srcSlot := add(srcSlot, 1)
|
// here we are done with this slot, we need to read next one
|
||||||
srcSlotValue := sload(srcSlot)
|
srcPtr := add(srcPtr, 1)
|
||||||
srcItemIndexInSlot := 0
|
srcSlotValue := sload(srcPtr)
|
||||||
}
|
srcItemIndexInSlot := 0
|
||||||
<!srcReadMultiPerSlot>
|
}
|
||||||
srcSlot := add(srcSlot, 1)
|
<!srcReadMultiPerSlot>
|
||||||
srcSlotValue := sload(srcSlot)
|
srcPtr := add(srcPtr, 1)
|
||||||
</srcReadMultiPerSlot>
|
srcSlotValue := sload(srcPtr)
|
||||||
)")
|
</srcReadMultiPerSlot>
|
||||||
("srcReadMultiPerSlot", !sameType && _fromType.storageStride() <= 16)
|
)")
|
||||||
("srcItemsPerSlot", to_string(32 / _fromType.storageStride()))
|
("srcReadMultiPerSlot", !sameTypeFromStorage && _fromType.storageStride() <= 16)
|
||||||
.render()
|
("srcItemsPerSlot", to_string(32 / _fromType.storageStride()))
|
||||||
);
|
.render()
|
||||||
|
);
|
||||||
|
else
|
||||||
|
templ("updateSrcPtr", Whiskers(R"(
|
||||||
|
srcPtr := add(srcPtr, <srcStride>)
|
||||||
|
)")
|
||||||
|
("srcStride", fromCalldata ? to_string(_fromType.calldataStride()) : to_string(_fromType.memoryStride()))
|
||||||
|
.render()
|
||||||
|
);
|
||||||
|
|
||||||
return templ.render();
|
return templ.render();
|
||||||
});
|
});
|
||||||
|
@ -258,9 +258,9 @@ public:
|
|||||||
/// signature (to_slot, from_ptr) ->
|
/// signature (to_slot, from_ptr) ->
|
||||||
std::string copyByteArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType);
|
std::string copyByteArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType);
|
||||||
|
|
||||||
/// @returns the name of a function that will copy an array of value types from storage to storage.
|
/// @returns the name of a function that will copy an array of value types to storage.
|
||||||
/// signature (to_slot, from_slot) ->
|
/// signature (to_slot, from_ptr[, from_length]) ->
|
||||||
std::string copyValueArrayStorageToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType);
|
std::string copyValueArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType);
|
||||||
|
|
||||||
/// Returns the name of a function that will convert a given length to the
|
/// Returns the name of a function that will convert a given length to the
|
||||||
/// size in memory (number of storage slots or calldata/memory bytes) it
|
/// size in memory (number of storage slots or calldata/memory bytes) it
|
||||||
|
@ -24,6 +24,6 @@ contract C {
|
|||||||
// ----
|
// ----
|
||||||
// library: L
|
// library: L
|
||||||
// f() -> 8, 7, 1, 2, 7, 12
|
// f() -> 8, 7, 1, 2, 7, 12
|
||||||
// gas irOptimized: 166606
|
// gas irOptimized: 166525
|
||||||
// gas legacy: 169347
|
// gas legacy: 169347
|
||||||
// gas legacyOptimized: 167269
|
// gas legacyOptimized: 167269
|
||||||
|
@ -18,10 +18,10 @@ contract C {
|
|||||||
// EVMVersion: >homestead
|
// EVMVersion: >homestead
|
||||||
// ----
|
// ----
|
||||||
// h(uint256[2][]): 0x20, 3, 123, 124, 223, 224, 323, 324 -> 32, 256, 0x20, 3, 123, 124, 223, 224, 323, 324
|
// h(uint256[2][]): 0x20, 3, 123, 124, 223, 224, 323, 324 -> 32, 256, 0x20, 3, 123, 124, 223, 224, 323, 324
|
||||||
// gas irOptimized: 180882
|
// gas irOptimized: 180768
|
||||||
// gas legacy: 184929
|
// gas legacy: 184929
|
||||||
// gas legacyOptimized: 181504
|
// gas legacyOptimized: 181504
|
||||||
// i(uint256[2][2]): 123, 124, 223, 224 -> 32, 128, 123, 124, 223, 224
|
// i(uint256[2][2]): 123, 124, 223, 224 -> 32, 128, 123, 124, 223, 224
|
||||||
// gas irOptimized: 112533
|
// gas irOptimized: 112471
|
||||||
// gas legacy: 115468
|
// gas legacy: 115468
|
||||||
// gas legacyOptimized: 112988
|
// gas legacyOptimized: 112988
|
||||||
|
@ -12,7 +12,7 @@ contract Test {
|
|||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// set(uint24[3][]): 0x20, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12 -> 0x06
|
// set(uint24[3][]): 0x20, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12 -> 0x06
|
||||||
// gas irOptimized: 189640
|
// gas irOptimized: 186766
|
||||||
// gas legacy: 211149
|
// gas legacy: 211149
|
||||||
// gas legacyOptimized: 206054
|
// gas legacyOptimized: 206054
|
||||||
// data(uint256,uint256): 0x02, 0x02 -> 0x09
|
// data(uint256,uint256): 0x02, 0x02 -> 0x09
|
||||||
|
@ -9,7 +9,7 @@ contract C {
|
|||||||
|
|
||||||
// ----
|
// ----
|
||||||
// constructor(): 1, 2, 3 ->
|
// constructor(): 1, 2, 3 ->
|
||||||
// gas irOptimized: 141700
|
// gas irOptimized: 141581
|
||||||
// gas legacy: 183490
|
// gas legacy: 183490
|
||||||
// gas legacyOptimized: 151938
|
// gas legacyOptimized: 151938
|
||||||
// a(uint256): 0 -> 1
|
// a(uint256): 0 -> 1
|
||||||
|
@ -20,7 +20,7 @@ contract c {
|
|||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// store(uint256[9],uint8[3][]): 21, 22, 23, 24, 25, 26, 27, 28, 29, 0x140, 4, 1, 2, 3, 11, 12, 13, 21, 22, 23, 31, 32, 33 -> 32
|
// store(uint256[9],uint8[3][]): 21, 22, 23, 24, 25, 26, 27, 28, 29, 0x140, 4, 1, 2, 3, 11, 12, 13, 21, 22, 23, 31, 32, 33 -> 32
|
||||||
// gas irOptimized: 650748
|
// gas irOptimized: 648324
|
||||||
// gas legacy: 694515
|
// gas legacy: 694515
|
||||||
// gas legacyOptimized: 694013
|
// gas legacyOptimized: 694013
|
||||||
// retrieve() -> 9, 28, 9, 28, 4, 3, 32
|
// retrieve() -> 9, 28, 9, 28, 4, 3, 32
|
||||||
|
@ -46,6 +46,6 @@ contract C {
|
|||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// f() -> true
|
// f() -> true
|
||||||
// gas irOptimized: 146913
|
// gas irOptimized: 146936
|
||||||
// gas legacy: 155961
|
// gas legacy: 155961
|
||||||
// gas legacyOptimized: 153588
|
// gas legacyOptimized: 153588
|
||||||
|
@ -13,6 +13,6 @@ contract C {
|
|||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// f() -> 0
|
// f() -> 0
|
||||||
// gas irOptimized: 134352
|
// gas irOptimized: 134365
|
||||||
// gas legacy: 135313
|
// gas legacy: 135313
|
||||||
// gas legacyOptimized: 134548
|
// gas legacyOptimized: 134548
|
||||||
|
@ -19,6 +19,6 @@ contract c {
|
|||||||
|
|
||||||
// ----
|
// ----
|
||||||
// test() -> 0x01000000000000000000000000000000000000000000000000, 0x02000000000000000000000000000000000000000000000000, 0x03000000000000000000000000000000000000000000000000, 0x04000000000000000000000000000000000000000000000000, 0x05000000000000000000000000000000000000000000000000
|
// test() -> 0x01000000000000000000000000000000000000000000000000, 0x02000000000000000000000000000000000000000000000000, 0x03000000000000000000000000000000000000000000000000, 0x04000000000000000000000000000000000000000000000000, 0x05000000000000000000000000000000000000000000000000
|
||||||
// gas irOptimized: 209152
|
// gas irOptimized: 208149
|
||||||
// gas legacy: 221856
|
// gas legacy: 221856
|
||||||
// gas legacyOptimized: 220680
|
// gas legacyOptimized: 220680
|
||||||
|
@ -13,6 +13,6 @@ contract c {
|
|||||||
|
|
||||||
// ----
|
// ----
|
||||||
// test(uint256[2][]): 32, 3, 7, 8, 9, 10, 11, 12 -> 10
|
// test(uint256[2][]): 32, 3, 7, 8, 9, 10, 11, 12 -> 10
|
||||||
// gas irOptimized: 689834
|
// gas irOptimized: 689768
|
||||||
// gas legacy: 686268
|
// gas legacy: 686268
|
||||||
// gas legacyOptimized: 685688
|
// gas legacyOptimized: 685688
|
||||||
|
@ -38,10 +38,10 @@ contract c {
|
|||||||
// compileViaYul: true
|
// compileViaYul: true
|
||||||
// ----
|
// ----
|
||||||
// test1(uint256[][]): 0x20, 2, 0x40, 0x40, 2, 23, 42 -> 2, 65
|
// test1(uint256[][]): 0x20, 2, 0x40, 0x40, 2, 23, 42 -> 2, 65
|
||||||
// gas irOptimized: 180723
|
// gas irOptimized: 180766
|
||||||
// test2(uint256[][2]): 0x20, 0x40, 0x40, 2, 23, 42 -> 2, 65
|
// test2(uint256[][2]): 0x20, 0x40, 0x40, 2, 23, 42 -> 2, 65
|
||||||
// gas irOptimized: 157518
|
// gas irOptimized: 157564
|
||||||
// test3(uint256[2][]): 0x20, 2, 23, 42, 23, 42 -> 2, 65
|
// test3(uint256[2][]): 0x20, 2, 23, 42, 23, 42 -> 2, 65
|
||||||
// gas irOptimized: 134709
|
// gas irOptimized: 134630
|
||||||
// test4(uint256[2][2]): 23, 42, 23, 42 -> 65
|
// test4(uint256[2][2]): 23, 42, 23, 42 -> 65
|
||||||
// gas irOptimized: 111324
|
// gas irOptimized: 111268
|
||||||
|
@ -38,12 +38,12 @@ contract Test {
|
|||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// test() -> 24
|
// test() -> 24
|
||||||
// gas irOptimized: 226714
|
// gas irOptimized: 226694
|
||||||
// gas legacy: 227133
|
// gas legacy: 227133
|
||||||
// gas legacyOptimized: 226547
|
// gas legacyOptimized: 226547
|
||||||
// test1() -> 3
|
// test1() -> 3
|
||||||
// test2() -> 6
|
// test2() -> 6
|
||||||
// test3() -> 24
|
// test3() -> 24
|
||||||
// gas irOptimized: 133317
|
// gas irOptimized: 133254
|
||||||
// gas legacy: 134295
|
// gas legacy: 134295
|
||||||
// gas legacyOptimized: 133383
|
// gas legacyOptimized: 133383
|
||||||
|
@ -23,4 +23,4 @@ contract C {
|
|||||||
// compileViaYul: true
|
// compileViaYul: true
|
||||||
// ----
|
// ----
|
||||||
// f((uint256[])[]): 0x20, 3, 0x60, 0x60, 0x60, 0x20, 3, 1, 2, 3 -> 3, 1
|
// f((uint256[])[]): 0x20, 3, 0x60, 0x60, 0x60, 0x20, 3, 1, 2, 3 -> 3, 1
|
||||||
// gas irOptimized: 327727
|
// gas irOptimized: 327798
|
||||||
|
@ -26,4 +26,4 @@ contract C {
|
|||||||
// compileViaYul: true
|
// compileViaYul: true
|
||||||
// ----
|
// ----
|
||||||
// f() -> 3, 3, 3, 1
|
// f() -> 3, 3, 3, 1
|
||||||
// gas irOptimized: 182521
|
// gas irOptimized: 182237
|
||||||
|
@ -9,6 +9,6 @@ contract C {
|
|||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// f(uint256[]): 0x20, 0x03, 0x1, 0x2, 0x3 -> 0x1
|
// f(uint256[]): 0x20, 0x03, 0x1, 0x2, 0x3 -> 0x1
|
||||||
// gas irOptimized: 111027
|
// gas irOptimized: 110971
|
||||||
// gas legacy: 111565
|
// gas legacy: 111565
|
||||||
// gas legacyOptimized: 111347
|
// gas legacyOptimized: 111347
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
contract C {
|
||||||
|
uint32[] s;
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
s.push();
|
||||||
|
s.push();
|
||||||
|
}
|
||||||
|
function f() external returns (uint)
|
||||||
|
{
|
||||||
|
(s[1], s) = (4, [0]);
|
||||||
|
s = [0];
|
||||||
|
s.push();
|
||||||
|
return s[1];
|
||||||
|
// used to return 4 via IR.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// constructor()
|
||||||
|
// gas irOptimized: 237351
|
||||||
|
// gas legacy: 221315
|
||||||
|
// gas legacyOptimized: 185247
|
||||||
|
// f() -> 0
|
@ -16,6 +16,6 @@ contract C {
|
|||||||
|
|
||||||
// ----
|
// ----
|
||||||
// test() -> 7
|
// test() -> 7
|
||||||
// gas irOptimized: 123625
|
// gas irOptimized: 122483
|
||||||
// gas legacy: 205196
|
// gas legacy: 205196
|
||||||
// gas legacyOptimized: 204987
|
// gas legacyOptimized: 204987
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
contract C {
|
||||||
|
string log;
|
||||||
|
function() external[] fs;
|
||||||
|
function() external[] gs;
|
||||||
|
|
||||||
|
function a() external {
|
||||||
|
log = string.concat(log, "[a called]");
|
||||||
|
}
|
||||||
|
function b() external {
|
||||||
|
log = string.concat(log, "[b called]");
|
||||||
|
}
|
||||||
|
|
||||||
|
function f(function() external[] calldata x) external {
|
||||||
|
fs = x;
|
||||||
|
}
|
||||||
|
function g(function() external[] memory x) public {
|
||||||
|
fs = x;
|
||||||
|
}
|
||||||
|
function test() external returns (string memory) {
|
||||||
|
log = "";
|
||||||
|
function() external[] memory x = new function() external[](2);
|
||||||
|
x[0] = this.a;
|
||||||
|
x[1] = this.b;
|
||||||
|
this.f(x);
|
||||||
|
fs[0]();
|
||||||
|
fs[1]();
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
function test2() external returns (string memory) {
|
||||||
|
log = "";
|
||||||
|
function() external[] memory x = new function() external[](2);
|
||||||
|
x[0] = this.b;
|
||||||
|
x[1] = this.a;
|
||||||
|
g(x);
|
||||||
|
fs[0]();
|
||||||
|
fs[1]();
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
function test3() external returns (string memory) {
|
||||||
|
log = "";
|
||||||
|
gs = fs;
|
||||||
|
gs[0]();
|
||||||
|
gs[1]();
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// test() -> 0x20, 0x14, "[a called][b called]"
|
||||||
|
// gas irOptimized: 116724
|
||||||
|
// gas legacy: 119056
|
||||||
|
// gas legacyOptimized: 117043
|
||||||
|
// test2() -> 0x20, 0x14, "[b called][a called]"
|
||||||
|
// test3() -> 0x20, 0x14, "[b called][a called]"
|
||||||
|
// gas irOptimized: 103304
|
||||||
|
// gas legacy: 102840
|
||||||
|
// gas legacyOptimized: 101728
|
@ -12,6 +12,6 @@ contract C {
|
|||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// f(uint120[]): 0x20, 3, 1, 2, 3 -> 1
|
// f(uint120[]): 0x20, 3, 1, 2, 3 -> 1
|
||||||
// gas irOptimized: 112776
|
// gas irOptimized: 112832
|
||||||
// gas legacy: 113686
|
// gas legacy: 113686
|
||||||
// gas legacyOptimized: 113499
|
// gas legacyOptimized: 113499
|
||||||
|
@ -20,6 +20,6 @@ contract c {
|
|||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// test() -> 2, 3, 4, 5
|
// test() -> 2, 3, 4, 5
|
||||||
// gas irOptimized: 136277
|
// gas irOptimized: 135204
|
||||||
// gas legacy: 147484
|
// gas legacy: 147484
|
||||||
// gas legacyOptimized: 146456
|
// gas legacyOptimized: 146456
|
||||||
|
@ -16,6 +16,6 @@ contract c {
|
|||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// test((uint16,uint16,uint16[3],uint16[])): 0x20, 2, 3, 0, 0, 4, 0xC0, 4, 0, 0, 5, 0, 0 -> 2, 3, 4, 5
|
// test((uint16,uint16,uint16[3],uint16[])): 0x20, 2, 3, 0, 0, 4, 0xC0, 4, 0, 0, 5, 0, 0 -> 2, 3, 4, 5
|
||||||
// gas irOptimized: 138230
|
// gas irOptimized: 137092
|
||||||
// gas legacy: 145150
|
// gas legacy: 142537
|
||||||
// gas legacyOptimized: 139171
|
// gas legacyOptimized: 138023
|
||||||
|
@ -24,6 +24,6 @@ contract Creator {
|
|||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// f(uint256,address[]): 7, 0x40, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> 7, 8
|
// f(uint256,address[]): 7, 0x40, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -> 7, 8
|
||||||
// gas irOptimized: 430087
|
// gas irOptimized: 429680
|
||||||
// gas legacy: 590683
|
// gas legacy: 590683
|
||||||
// gas legacyOptimized: 448326
|
// gas legacyOptimized: 448326
|
||||||
|
@ -10,7 +10,7 @@ contract C {
|
|||||||
|
|
||||||
// ----
|
// ----
|
||||||
// constructor(): 1, 2, 3, 4 ->
|
// constructor(): 1, 2, 3, 4 ->
|
||||||
// gas irOptimized: 173087
|
// gas irOptimized: 172948
|
||||||
// gas legacy: 221377
|
// gas legacy: 221377
|
||||||
// gas legacyOptimized: 177671
|
// gas legacyOptimized: 177671
|
||||||
// a() -> 1
|
// a() -> 1
|
||||||
|
@ -15,6 +15,6 @@ contract C {
|
|||||||
// ----
|
// ----
|
||||||
// createEvent(uint256): 42 ->
|
// createEvent(uint256): 42 ->
|
||||||
// ~ emit E(uint256[][]): 0x20, 0x02, 0x40, 0xa0, 0x02, 0x2a, 0x2b, 0x02, 0x2c, 0x2d
|
// ~ emit E(uint256[][]): 0x20, 0x02, 0x40, 0xa0, 0x02, 0x2a, 0x2b, 0x02, 0x2c, 0x2d
|
||||||
// gas irOptimized: 185131
|
// gas irOptimized: 185145
|
||||||
// gas legacy: 187621
|
// gas legacy: 187621
|
||||||
// gas legacyOptimized: 184551
|
// gas legacyOptimized: 184551
|
||||||
|
@ -16,6 +16,6 @@ contract C {
|
|||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// f(uint32,(uint128,uint256[][2],uint32)): 55, 0x40, 77, 0x60, 88, 0x40, 0x40, 2, 1, 2 -> 55, 77, 1, 2, 88
|
// f(uint32,(uint128,uint256[][2],uint32)): 55, 0x40, 77, 0x60, 88, 0x40, 0x40, 2, 1, 2 -> 55, 77, 1, 2, 88
|
||||||
// gas irOptimized: 202828
|
// gas irOptimized: 202838
|
||||||
// gas legacy: 209194
|
// gas legacy: 207487
|
||||||
// gas legacyOptimized: 203583
|
// gas legacyOptimized: 203611
|
||||||
|
@ -49,7 +49,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// test_f() -> true
|
// test_f() -> true
|
||||||
// gas irOptimized: 122154
|
// gas irOptimized: 122070
|
||||||
// gas legacy: 126150
|
// gas legacy: 126150
|
||||||
// gas legacyOptimized: 123163
|
// gas legacyOptimized: 123163
|
||||||
// test_g() -> true
|
// test_g() -> true
|
||||||
|
@ -34,6 +34,6 @@ contract C {
|
|||||||
|
|
||||||
// ----
|
// ----
|
||||||
// f(bytes): 0x20, 0x5, "abcde" -> 0
|
// f(bytes): 0x20, 0x5, "abcde" -> 0
|
||||||
// gas irOptimized: 239194
|
// gas irOptimized: 239090
|
||||||
// gas legacy: 240541
|
// gas legacy: 240541
|
||||||
// gas legacyOptimized: 239654
|
// gas legacyOptimized: 239654
|
||||||
|
@ -20,6 +20,6 @@ contract C {
|
|||||||
|
|
||||||
// ----
|
// ----
|
||||||
// g() -> 2, 6
|
// g() -> 2, 6
|
||||||
// gas irOptimized: 178677
|
// gas irOptimized: 178637
|
||||||
// gas legacy: 180945
|
// gas legacy: 180945
|
||||||
// gas legacyOptimized: 179460
|
// gas legacyOptimized: 179460
|
||||||
|
Loading…
Reference in New Issue
Block a user