mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #6690 from ethereum/useConditionalWhiskers
Use conditional whiskers
This commit is contained in:
commit
8c232e8196
@ -261,35 +261,25 @@ string YulUtilFunctions::shiftRightFunction(size_t _numBits)
|
|||||||
// Note that if this is extended with signed shifts,
|
// Note that if this is extended with signed shifts,
|
||||||
// the opcodes SAR and SDIV behave differently with regards to rounding!
|
// the opcodes SAR and SDIV behave differently with regards to rounding!
|
||||||
|
|
||||||
string functionName = "shift_right_" + to_string(_numBits) + "_unsigned";
|
string functionName = "shift_right_" + to_string(_numBits) + "_unsigned_" + m_evmVersion.name();
|
||||||
if (m_evmVersion.hasBitwiseShifting())
|
return m_functionCollector->createFunction(functionName, [&]() {
|
||||||
{
|
return
|
||||||
return m_functionCollector->createFunction(functionName, [&]() {
|
Whiskers(R"(
|
||||||
return
|
function <functionName>(value) -> newValue {
|
||||||
Whiskers(R"(
|
newValue :=
|
||||||
function <functionName>(value) -> newValue {
|
<?hasShifts>
|
||||||
newValue := shr(<numBits>, value)
|
shr(<numBits>, value)
|
||||||
}
|
<!hasShifts>
|
||||||
)")
|
div(value, <multiplier>)
|
||||||
("functionName", functionName)
|
</hasShifts>
|
||||||
("numBits", to_string(_numBits))
|
}
|
||||||
.render();
|
)")
|
||||||
});
|
("functionName", functionName)
|
||||||
}
|
("hasShifts", m_evmVersion.hasBitwiseShifting())
|
||||||
else
|
("numBits", to_string(_numBits))
|
||||||
{
|
("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
|
||||||
return m_functionCollector->createFunction(functionName, [&]() {
|
.render();
|
||||||
return
|
});
|
||||||
Whiskers(R"(
|
|
||||||
function <functionName>(value) -> newValue {
|
|
||||||
newValue := div(value, <multiplier>)
|
|
||||||
}
|
|
||||||
)")
|
|
||||||
("functionName", functionName)
|
|
||||||
("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
|
|
||||||
.render();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::updateByteSliceFunction(size_t _numBytes, size_t _shiftBytes)
|
string YulUtilFunctions::updateByteSliceFunction(size_t _numBytes, size_t _shiftBytes)
|
||||||
@ -336,28 +326,23 @@ string YulUtilFunctions::overflowCheckedUIntAddFunction(size_t _bits)
|
|||||||
solAssert(0 < _bits && _bits <= 256 && _bits % 8 == 0, "");
|
solAssert(0 < _bits && _bits <= 256 && _bits % 8 == 0, "");
|
||||||
string functionName = "checked_add_uint_" + to_string(_bits);
|
string functionName = "checked_add_uint_" + to_string(_bits);
|
||||||
return m_functionCollector->createFunction(functionName, [&]() {
|
return m_functionCollector->createFunction(functionName, [&]() {
|
||||||
if (_bits < 256)
|
return
|
||||||
return
|
Whiskers(R"(
|
||||||
Whiskers(R"(
|
function <functionName>(x, y) -> sum {
|
||||||
function <functionName>(x, y) -> sum {
|
<?shortType>
|
||||||
let mask := <mask>
|
let mask := <mask>
|
||||||
sum := add(and(x, mask), and(y, mask))
|
sum := add(and(x, mask), and(y, mask))
|
||||||
if and(sum, not(mask)) { revert(0, 0) }
|
if and(sum, not(mask)) { revert(0, 0) }
|
||||||
}
|
<!shortType>
|
||||||
)")
|
|
||||||
("functionName", functionName)
|
|
||||||
("mask", toCompactHexWithPrefix((u256(1) << _bits) - 1))
|
|
||||||
.render();
|
|
||||||
else
|
|
||||||
return
|
|
||||||
Whiskers(R"(
|
|
||||||
function <functionName>(x, y) -> sum {
|
|
||||||
sum := add(x, y)
|
sum := add(x, y)
|
||||||
if lt(sum, x) { revert(0, 0) }
|
if lt(sum, x) { revert(0, 0) }
|
||||||
}
|
</shortType>
|
||||||
)")
|
}
|
||||||
("functionName", functionName)
|
)")
|
||||||
.render();
|
("shortType", _bits < 256)
|
||||||
|
("functionName", functionName)
|
||||||
|
("mask", toCompactHexWithPrefix((u256(1) << _bits) - 1))
|
||||||
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -383,43 +368,38 @@ string YulUtilFunctions::arrayLengthFunction(ArrayType const& _type)
|
|||||||
return m_functionCollector->createFunction(functionName, [&]() {
|
return m_functionCollector->createFunction(functionName, [&]() {
|
||||||
Whiskers w(R"(
|
Whiskers w(R"(
|
||||||
function <functionName>(value) -> length {
|
function <functionName>(value) -> length {
|
||||||
<body>
|
<?dynamic>
|
||||||
|
<?memory>
|
||||||
|
length := mload(value)
|
||||||
|
</memory>
|
||||||
|
<?storage>
|
||||||
|
length := sload(value)
|
||||||
|
<?byteArray>
|
||||||
|
// Retrieve length both for in-place strings and off-place strings:
|
||||||
|
// Computes (x & (0x100 * (ISZERO (x & 1)) - 1)) / 2
|
||||||
|
// i.e. for short strings (x & 1 == 0) it does (x & 0xff) / 2 and for long strings it
|
||||||
|
// computes (x & (-1)) / 2, which is equivalent to just x / 2.
|
||||||
|
let mask := sub(mul(0x100, iszero(and(length, 1))), 1)
|
||||||
|
length := div(and(length, mask), 2)
|
||||||
|
</byteArray>
|
||||||
|
</storage>
|
||||||
|
<!dynamic>
|
||||||
|
length := <length>
|
||||||
|
</dynamic>
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
w("functionName", functionName);
|
w("functionName", functionName);
|
||||||
string body;
|
w("dynamic", _type.isDynamicallySized());
|
||||||
if (!_type.isDynamicallySized())
|
if (!_type.isDynamicallySized())
|
||||||
body = "length := " + toCompactHexWithPrefix(_type.length());
|
w("length", toCompactHexWithPrefix(_type.length()));
|
||||||
else
|
w("memory", _type.location() == DataLocation::Memory);
|
||||||
{
|
w("storage", _type.location() == DataLocation::Storage);
|
||||||
switch (_type.location())
|
w("byteArray", _type.isByteArray());
|
||||||
{
|
if (_type.isDynamicallySized())
|
||||||
case DataLocation::CallData:
|
solAssert(
|
||||||
solAssert(false, "called regular array length function on calldata array");
|
_type.location() != DataLocation::CallData,
|
||||||
break;
|
"called regular array length function on calldata array"
|
||||||
case DataLocation::Memory:
|
);
|
||||||
body = "length := mload(value)";
|
|
||||||
break;
|
|
||||||
case DataLocation::Storage:
|
|
||||||
if (_type.isByteArray())
|
|
||||||
{
|
|
||||||
// Retrieve length both for in-place strings and off-place strings:
|
|
||||||
// Computes (x & (0x100 * (ISZERO (x & 1)) - 1)) / 2
|
|
||||||
// i.e. for short strings (x & 1 == 0) it does (x & 0xff) / 2 and for long strings it
|
|
||||||
// computes (x & (-1)) / 2, which is equivalent to just x / 2.
|
|
||||||
body = R"(
|
|
||||||
length := sload(value)
|
|
||||||
let mask := sub(mul(0x100, iszero(and(length, 1))), 1)
|
|
||||||
length := div(and(length, mask), 2)
|
|
||||||
)";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
body = "length := sload(value)";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
solAssert(!body.empty(), "");
|
|
||||||
w("body", body);
|
|
||||||
return w.render();
|
return w.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -433,20 +413,21 @@ string YulUtilFunctions::arrayAllocationSizeFunction(ArrayType const& _type)
|
|||||||
function <functionName>(length) -> size {
|
function <functionName>(length) -> size {
|
||||||
// Make sure we can allocate memory without overflow
|
// Make sure we can allocate memory without overflow
|
||||||
if gt(length, 0xffffffffffffffff) { revert(0, 0) }
|
if gt(length, 0xffffffffffffffff) { revert(0, 0) }
|
||||||
size := <allocationSize>
|
<?byteArray>
|
||||||
<addLengthSlot>
|
// round up
|
||||||
|
size := and(add(length, 0x1f), not(0x1f))
|
||||||
|
<!byteArray>
|
||||||
|
size := mul(length, 0x20)
|
||||||
|
</byteArray>
|
||||||
|
<?dynamic>
|
||||||
|
// add length slot
|
||||||
|
size := add(size, 0x20)
|
||||||
|
</dynamic>
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
w("functionName", functionName);
|
w("functionName", functionName);
|
||||||
if (_type.isByteArray())
|
w("byteArray", _type.isByteArray());
|
||||||
// Round up
|
w("dynamic", _type.isDynamicallySized());
|
||||||
w("allocationSize", "and(add(length, 0x1f), not(0x1f))");
|
|
||||||
else
|
|
||||||
w("allocationSize", "mul(length, 0x20)");
|
|
||||||
if (_type.isDynamicallySized())
|
|
||||||
w("addLengthSlot", "size := add(size, 0x20)");
|
|
||||||
else
|
|
||||||
w("addLengthSlot", "");
|
|
||||||
return w.render();
|
return w.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -455,64 +436,30 @@ string YulUtilFunctions::arrayDataAreaFunction(ArrayType const& _type)
|
|||||||
{
|
{
|
||||||
string functionName = "array_dataslot_" + _type.identifier();
|
string functionName = "array_dataslot_" + _type.identifier();
|
||||||
return m_functionCollector->createFunction(functionName, [&]() {
|
return m_functionCollector->createFunction(functionName, [&]() {
|
||||||
switch (_type.location())
|
// No special processing for calldata arrays, because they are stored as
|
||||||
{
|
// offset of the data area and length on the stack, so the offset already
|
||||||
case DataLocation::Memory:
|
// points to the data area.
|
||||||
if (_type.isDynamicallySized())
|
// This might change, if calldata arrays are stored in a single
|
||||||
return Whiskers(R"(
|
// stack slot at some point.
|
||||||
function <functionName>(memPtr) -> dataPtr {
|
return Whiskers(R"(
|
||||||
dataPtr := add(memPtr, 0x20)
|
function <functionName>(ptr) -> data {
|
||||||
}
|
data := ptr
|
||||||
)")
|
<?dynamic>
|
||||||
("functionName", functionName)
|
<?memory>
|
||||||
.render();
|
data := add(ptr, 0x20)
|
||||||
else
|
</memory>
|
||||||
return Whiskers(R"(
|
<?storage>
|
||||||
function <functionName>(memPtr) -> dataPtr {
|
mstore(0, ptr)
|
||||||
dataPtr := memPtr
|
data := keccak256(0, 0x20)
|
||||||
}
|
</storage>
|
||||||
)")
|
</dynamic>
|
||||||
("functionName", functionName)
|
|
||||||
.render();
|
|
||||||
case DataLocation::Storage:
|
|
||||||
if (_type.isDynamicallySized())
|
|
||||||
{
|
|
||||||
Whiskers w(R"(
|
|
||||||
function <functionName>(slot) -> dataSlot {
|
|
||||||
mstore(0, slot)
|
|
||||||
dataSlot := keccak256(0, 0x20)
|
|
||||||
}
|
|
||||||
)");
|
|
||||||
w("functionName", functionName);
|
|
||||||
return w.render();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Whiskers w(R"(
|
|
||||||
function <functionName>(slot) -> dataSlot {
|
|
||||||
dataSlot := slot
|
|
||||||
}
|
|
||||||
)");
|
|
||||||
w("functionName", functionName);
|
|
||||||
return w.render();
|
|
||||||
}
|
|
||||||
case DataLocation::CallData:
|
|
||||||
{
|
|
||||||
// Calldata arrays are stored as offset of the data area and length
|
|
||||||
// on the stack, so the offset already points to the data area.
|
|
||||||
// This might change, if calldata arrays are stored in a single
|
|
||||||
// stack slot at some point.
|
|
||||||
Whiskers w(R"(
|
|
||||||
function <functionName>(slot) -> dataSlot {
|
|
||||||
dataSlot := slot
|
|
||||||
}
|
|
||||||
)");
|
|
||||||
w("functionName", functionName);
|
|
||||||
return w.render();
|
|
||||||
}
|
}
|
||||||
default:
|
)")
|
||||||
solAssert(false, "");
|
("functionName", functionName)
|
||||||
}
|
("dynamic", _type.isDynamicallySized())
|
||||||
|
("memory", _type.location() == DataLocation::Memory)
|
||||||
|
("storage", _type.location() == DataLocation::Storage)
|
||||||
|
.render();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -523,36 +470,25 @@ string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
|
|||||||
solAssert(_type.baseType()->storageBytes() > 16, "");
|
solAssert(_type.baseType()->storageBytes() > 16, "");
|
||||||
string functionName = "array_nextElement_" + _type.identifier();
|
string functionName = "array_nextElement_" + _type.identifier();
|
||||||
return m_functionCollector->createFunction(functionName, [&]() {
|
return m_functionCollector->createFunction(functionName, [&]() {
|
||||||
switch (_type.location())
|
Whiskers templ(R"(
|
||||||
{
|
function <functionName>(ptr) -> next {
|
||||||
case DataLocation::Memory:
|
next := add(ptr, <advance>)
|
||||||
return Whiskers(R"(
|
}
|
||||||
function <functionName>(memPtr) -> nextPtr {
|
)");
|
||||||
nextPtr := add(memPtr, 0x20)
|
templ("functionName", functionName);
|
||||||
}
|
if (_type.location() == DataLocation::Memory)
|
||||||
)")
|
templ("advance", "0x20");
|
||||||
("functionName", functionName)
|
else if (_type.location() == DataLocation::Storage)
|
||||||
.render();
|
templ("advance", "1");
|
||||||
case DataLocation::Storage:
|
else if (_type.location() == DataLocation::CallData)
|
||||||
return Whiskers(R"(
|
templ("advance", toCompactHexWithPrefix(
|
||||||
function <functionName>(slot) -> nextSlot {
|
_type.baseType()->isDynamicallyEncoded() ?
|
||||||
nextSlot := add(slot, 1)
|
32 :
|
||||||
}
|
_type.baseType()->calldataEncodedSize()
|
||||||
)")
|
));
|
||||||
("functionName", functionName)
|
else
|
||||||
.render();
|
solAssert(false, "");
|
||||||
case DataLocation::CallData:
|
return templ.render();
|
||||||
return Whiskers(R"(
|
|
||||||
function <functionName>(calldataPtr) -> nextPtr {
|
|
||||||
nextPtr := add(calldataPtr, <stride>)
|
|
||||||
}
|
|
||||||
)")
|
|
||||||
("stride", toCompactHexWithPrefix(_type.baseType()->isDynamicallyEncoded() ? 32 : _type.baseType()->calldataEncodedSize()))
|
|
||||||
("functionName", functionName)
|
|
||||||
.render();
|
|
||||||
default:
|
|
||||||
solAssert(false, "");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -654,7 +590,7 @@ string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _spl
|
|||||||
return m_functionCollector->createFunction(functionName, [&] {
|
return m_functionCollector->createFunction(functionName, [&] {
|
||||||
Whiskers templ(R"(
|
Whiskers templ(R"(
|
||||||
function <functionName>(value) -> cleaned {
|
function <functionName>(value) -> cleaned {
|
||||||
<body>
|
cleaned := <cleaned>
|
||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
templ("functionName", functionName);
|
templ("functionName", functionName);
|
||||||
@ -663,16 +599,16 @@ string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type, bool _spl
|
|||||||
if (IntegerType const* type = dynamic_cast<IntegerType const*>(&_type))
|
if (IntegerType const* type = dynamic_cast<IntegerType const*>(&_type))
|
||||||
if (type->isSigned() && storageBytes != 32)
|
if (type->isSigned() && storageBytes != 32)
|
||||||
{
|
{
|
||||||
templ("body", "cleaned := signextend(" + to_string(storageBytes - 1) + ", value)");
|
templ("cleaned", "signextend(" + to_string(storageBytes - 1) + ", value)");
|
||||||
return templ.render();
|
return templ.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (storageBytes == 32)
|
if (storageBytes == 32)
|
||||||
templ("body", "cleaned := value");
|
templ("cleaned", "value");
|
||||||
else if (_type.leftAligned())
|
else if (_type.leftAligned())
|
||||||
templ("body", "cleaned := " + shiftLeftFunction(256 - 8 * storageBytes) + "(value)");
|
templ("cleaned", shiftLeftFunction(256 - 8 * storageBytes) + "(value)");
|
||||||
else
|
else
|
||||||
templ("body", "cleaned := and(value, " + toCompactHexWithPrefix((u256(1) << (8 * storageBytes)) - 1) + ")");
|
templ("cleaned", "and(value, " + toCompactHexWithPrefix((u256(1) << (8 * storageBytes)) - 1) + ")");
|
||||||
|
|
||||||
return templ.render();
|
return templ.render();
|
||||||
});
|
});
|
||||||
|
@ -14,9 +14,9 @@ contract C {
|
|||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// creation:
|
// creation:
|
||||||
// codeDepositCost: 1120800
|
// codeDepositCost: 1122600
|
||||||
// executionCost: 1167
|
// executionCost: 1167
|
||||||
// totalCost: 1121967
|
// totalCost: 1123767
|
||||||
// external:
|
// external:
|
||||||
// a(): 530
|
// a(): 530
|
||||||
// b(uint256): infinite
|
// b(uint256): infinite
|
||||||
|
Loading…
Reference in New Issue
Block a user