mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #6909 from ethereum/yul-delete-operation
[Sol->Yul] Make IRStorageItem work with dynamic offsets
This commit is contained in:
commit
2849169bff
@ -224,34 +224,45 @@ string YulUtilFunctions::shiftLeftFunction(size_t _numBits)
|
|||||||
solAssert(_numBits < 256, "");
|
solAssert(_numBits < 256, "");
|
||||||
|
|
||||||
string functionName = "shift_left_" + to_string(_numBits);
|
string functionName = "shift_left_" + to_string(_numBits);
|
||||||
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 := shl(<numBits>, value)
|
shl(<numBits>, value)
|
||||||
}
|
<!hasShifts>
|
||||||
)")
|
mul(value, <multiplier>)
|
||||||
("functionName", functionName)
|
</hasShifts>
|
||||||
("numBits", to_string(_numBits))
|
}
|
||||||
.render();
|
)")
|
||||||
});
|
("functionName", functionName)
|
||||||
}
|
("numBits", to_string(_numBits))
|
||||||
else
|
("hasShifts", m_evmVersion.hasBitwiseShifting())
|
||||||
{
|
("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
|
||||||
return m_functionCollector->createFunction(functionName, [&]() {
|
.render();
|
||||||
return
|
});
|
||||||
Whiskers(R"(
|
}
|
||||||
function <functionName>(value) -> newValue {
|
|
||||||
newValue := mul(value, <multiplier>)
|
string YulUtilFunctions::dynamicShiftLeftFunction()
|
||||||
}
|
{
|
||||||
)")
|
string functionName = "shift_left";
|
||||||
("functionName", functionName)
|
return m_functionCollector->createFunction(functionName, [&]() {
|
||||||
("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
|
return
|
||||||
.render();
|
Whiskers(R"(
|
||||||
});
|
function <functionName>(bits, value) -> newValue {
|
||||||
}
|
newValue :=
|
||||||
|
<?hasShifts>
|
||||||
|
shl(bits, value)
|
||||||
|
<!hasShifts>
|
||||||
|
mul(value, exp(2, bits))
|
||||||
|
</hasShifts>
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("hasShifts", m_evmVersion.hasBitwiseShifting())
|
||||||
|
.render();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::shiftRightFunction(size_t _numBits)
|
string YulUtilFunctions::shiftRightFunction(size_t _numBits)
|
||||||
@ -261,7 +272,7 @@ 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_" + m_evmVersion.name();
|
string functionName = "shift_right_" + to_string(_numBits) + "_unsigned";
|
||||||
return m_functionCollector->createFunction(functionName, [&]() {
|
return m_functionCollector->createFunction(functionName, [&]() {
|
||||||
return
|
return
|
||||||
Whiskers(R"(
|
Whiskers(R"(
|
||||||
@ -282,6 +293,30 @@ string YulUtilFunctions::shiftRightFunction(size_t _numBits)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::dynamicShiftRightFunction()
|
||||||
|
{
|
||||||
|
// Note that if this is extended with signed shifts,
|
||||||
|
// the opcodes SAR and SDIV behave differently with regards to rounding!
|
||||||
|
|
||||||
|
string const functionName = "shift_right_unsigned";
|
||||||
|
return m_functionCollector->createFunction(functionName, [&]() {
|
||||||
|
return
|
||||||
|
Whiskers(R"(
|
||||||
|
function <functionName>(bits, value) -> newValue {
|
||||||
|
newValue :=
|
||||||
|
<?hasShifts>
|
||||||
|
shr(bits, value)
|
||||||
|
<!hasShifts>
|
||||||
|
div(value, exp(2, bits))
|
||||||
|
</hasShifts>
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("hasShifts", m_evmVersion.hasBitwiseShifting())
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::updateByteSliceFunction(size_t _numBytes, size_t _shiftBytes)
|
string YulUtilFunctions::updateByteSliceFunction(size_t _numBytes, size_t _shiftBytes)
|
||||||
{
|
{
|
||||||
solAssert(_numBytes <= 32, "");
|
solAssert(_numBytes <= 32, "");
|
||||||
@ -306,6 +341,29 @@ string YulUtilFunctions::updateByteSliceFunction(size_t _numBytes, size_t _shift
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::dynamicUpdateByteSliceFunction(size_t _numBytes)
|
||||||
|
{
|
||||||
|
solAssert(_numBytes <= 32, "");
|
||||||
|
size_t numBits = _numBytes * 8;
|
||||||
|
string functionName = "update_byte_slice_" + to_string(_numBytes);
|
||||||
|
return m_functionCollector->createFunction(functionName, [&]() {
|
||||||
|
return
|
||||||
|
Whiskers(R"(
|
||||||
|
function <functionName>(value, shiftBytes, toInsert) -> result {
|
||||||
|
let shiftBits := mul(shiftBytes, 8)
|
||||||
|
let mask := <shl>(shiftBits, <mask>)
|
||||||
|
toInsert := <shl>(shiftBits, toInsert)
|
||||||
|
value := and(value, not(mask))
|
||||||
|
result := or(value, and(toInsert, mask))
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("mask", formatNumber((bigint(1) << numBits) - 1))
|
||||||
|
("shl", dynamicShiftLeftFunction())
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::roundUpFunction()
|
string YulUtilFunctions::roundUpFunction()
|
||||||
{
|
{
|
||||||
string functionName = "round_up_to_mul_of_32";
|
string functionName = "round_up_to_mul_of_32";
|
||||||
@ -613,6 +671,89 @@ string YulUtilFunctions::readFromStorage(Type const& _type, size_t _offset, bool
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::dynamicReadFromStorage(Type const& _type, bool _splitFunctionTypes)
|
||||||
|
{
|
||||||
|
solUnimplementedAssert(!_splitFunctionTypes, "");
|
||||||
|
string functionName =
|
||||||
|
"read_from_storage_" +
|
||||||
|
string(_splitFunctionTypes ? "split_" : "") +
|
||||||
|
"_" +
|
||||||
|
_type.identifier();
|
||||||
|
return m_functionCollector->createFunction(functionName, [&] {
|
||||||
|
solAssert(_type.sizeOnStack() == 1, "");
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(slot, offset) -> value {
|
||||||
|
value := <extract>(sload(slot), offset)
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("extract", dynamicExtractFromStorageValue(_type, false))
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::updateStorageValueFunction(Type const& _type, boost::optional<unsigned> const _offset)
|
||||||
|
{
|
||||||
|
string const functionName =
|
||||||
|
"update_storage_value_" +
|
||||||
|
(_offset.is_initialized() ? ("offset_" + to_string(*_offset)) : "") +
|
||||||
|
_type.identifier();
|
||||||
|
|
||||||
|
return m_functionCollector->createFunction(functionName, [&] {
|
||||||
|
if (_type.isValueType())
|
||||||
|
{
|
||||||
|
solAssert(_type.storageBytes() <= 32, "Invalid storage bytes size.");
|
||||||
|
solAssert(_type.storageBytes() > 0, "Invalid storage bytes size.");
|
||||||
|
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(slot, <offset>value) {
|
||||||
|
sstore(slot, <update>(sload(slot), <offset><prepare>(value)))
|
||||||
|
}
|
||||||
|
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("update",
|
||||||
|
_offset.is_initialized() ?
|
||||||
|
updateByteSliceFunction(_type.storageBytes(), *_offset) :
|
||||||
|
dynamicUpdateByteSliceFunction(_type.storageBytes())
|
||||||
|
)
|
||||||
|
("offset", _offset.is_initialized() ? "" : "offset, ")
|
||||||
|
("prepare", prepareStoreFunction(_type))
|
||||||
|
.render();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_type.category() == Type::Category::Array)
|
||||||
|
solUnimplementedAssert(false, "");
|
||||||
|
else if (_type.category() == Type::Category::Struct)
|
||||||
|
solUnimplementedAssert(false, "");
|
||||||
|
else
|
||||||
|
solAssert(false, "Invalid non-value type for assignment.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::dynamicExtractFromStorageValue(Type const& _type, bool _splitFunctionTypes)
|
||||||
|
{
|
||||||
|
solUnimplementedAssert(!_splitFunctionTypes, "");
|
||||||
|
|
||||||
|
string functionName =
|
||||||
|
"extract_from_storage_value_" +
|
||||||
|
string(_splitFunctionTypes ? "split_" : "") +
|
||||||
|
_type.identifier();
|
||||||
|
return m_functionCollector->createFunction(functionName, [&] {
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(slot_value, offset) -> value {
|
||||||
|
value := <cleanupStorage>(<shr>(mul(offset, 8), slot_value))
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("shr", dynamicShiftRightFunction())
|
||||||
|
("cleanupStorage", cleanupFromStorageFunction(_type, false))
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::extractFromStorageValue(Type const& _type, size_t _offset, bool _splitFunctionTypes)
|
string YulUtilFunctions::extractFromStorageValue(Type const& _type, size_t _offset, bool _splitFunctionTypes)
|
||||||
{
|
{
|
||||||
solUnimplementedAssert(!_splitFunctionTypes, "");
|
solUnimplementedAssert(!_splitFunctionTypes, "");
|
||||||
|
@ -74,13 +74,18 @@ public:
|
|||||||
std::string leftAlignFunction(Type const& _type);
|
std::string leftAlignFunction(Type const& _type);
|
||||||
|
|
||||||
std::string shiftLeftFunction(size_t _numBits);
|
std::string shiftLeftFunction(size_t _numBits);
|
||||||
|
std::string dynamicShiftLeftFunction();
|
||||||
std::string shiftRightFunction(size_t _numBits);
|
std::string shiftRightFunction(size_t _numBits);
|
||||||
|
std::string dynamicShiftRightFunction();
|
||||||
|
|
||||||
/// @returns the name of a function f(value, toInsert) -> newValue which replaces the
|
/// @returns the name of a function f(value, toInsert) -> newValue which replaces the
|
||||||
/// _numBytes bytes starting at byte position _shiftBytes (counted from the least significant
|
/// _numBytes bytes starting at byte position _shiftBytes (counted from the least significant
|
||||||
/// byte) by the _numBytes least significant bytes of `toInsert`.
|
/// byte) by the _numBytes least significant bytes of `toInsert`.
|
||||||
std::string updateByteSliceFunction(size_t _numBytes, size_t _shiftBytes);
|
std::string updateByteSliceFunction(size_t _numBytes, size_t _shiftBytes);
|
||||||
|
|
||||||
|
/// signature: (value, shiftBytes, toInsert) -> result
|
||||||
|
std::string dynamicUpdateByteSliceFunction(size_t _numBytes);
|
||||||
|
|
||||||
/// @returns the name of a function that rounds its input to the next multiple
|
/// @returns the name of a function that rounds its input to the next multiple
|
||||||
/// of 32 or the input if it is a multiple of 32.
|
/// of 32 or the input if it is a multiple of 32.
|
||||||
std::string roundUpFunction();
|
std::string roundUpFunction();
|
||||||
@ -121,6 +126,7 @@ public:
|
|||||||
/// @param _splitFunctionTypes if false, returns the address and function signature in a
|
/// @param _splitFunctionTypes if false, returns the address and function signature in a
|
||||||
/// single variable.
|
/// single variable.
|
||||||
std::string readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes);
|
std::string readFromStorage(Type const& _type, size_t _offset, bool _splitFunctionTypes);
|
||||||
|
std::string dynamicReadFromStorage(Type const& _type, bool _splitFunctionTypes);
|
||||||
|
|
||||||
/// @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.
|
||||||
@ -128,6 +134,13 @@ public:
|
|||||||
/// @param _splitFunctionTypes if false, returns the address and function signature in a
|
/// @param _splitFunctionTypes if false, returns the address and function signature in a
|
||||||
/// single variable.
|
/// single variable.
|
||||||
std::string extractFromStorageValue(Type const& _type, size_t _offset, bool _splitFunctionTypes);
|
std::string extractFromStorageValue(Type const& _type, size_t _offset, bool _splitFunctionTypes);
|
||||||
|
std::string dynamicExtractFromStorageValue(Type const& _type, bool _splitFunctionTypes);
|
||||||
|
|
||||||
|
/// Returns the name of a function will write the given value to
|
||||||
|
/// the specified slot and offset. If offset is not given, it is expected as
|
||||||
|
/// runtime parameter.
|
||||||
|
/// signature: (slot, [offset,] value)
|
||||||
|
std::string updateStorageValueFunction(Type const& _type, boost::optional<unsigned> const _offset = boost::optional<unsigned>());
|
||||||
|
|
||||||
/// 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
|
||||||
|
@ -54,26 +54,37 @@ string IRLocalVariable::setToZero() const
|
|||||||
IRStorageItem::IRStorageItem(
|
IRStorageItem::IRStorageItem(
|
||||||
IRGenerationContext& _context,
|
IRGenerationContext& _context,
|
||||||
VariableDeclaration const& _varDecl
|
VariableDeclaration const& _varDecl
|
||||||
):
|
)
|
||||||
IRLValue(_context, _varDecl.annotation().type)
|
:IRStorageItem(
|
||||||
|
_context,
|
||||||
|
*_varDecl.annotation().type,
|
||||||
|
_context.storageLocationOfVariable(_varDecl)
|
||||||
|
)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
IRStorageItem::IRStorageItem(
|
||||||
|
IRGenerationContext& _context,
|
||||||
|
Type const& _type,
|
||||||
|
std::pair<u256, unsigned> slot_offset
|
||||||
|
)
|
||||||
|
: IRLValue(_context, &_type),
|
||||||
|
m_slot(toCompactHexWithPrefix(slot_offset.first)),
|
||||||
|
m_offset(slot_offset.second)
|
||||||
{
|
{
|
||||||
u256 slot;
|
|
||||||
unsigned offset;
|
|
||||||
std::tie(slot, offset) = _context.storageLocationOfVariable(_varDecl);
|
|
||||||
m_slot = toCompactHexWithPrefix(slot);
|
|
||||||
m_offset = offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IRStorageItem::IRStorageItem(
|
IRStorageItem::IRStorageItem(
|
||||||
IRGenerationContext& _context,
|
IRGenerationContext& _context,
|
||||||
string _slot,
|
string _slot,
|
||||||
unsigned _offset,
|
boost::variant<string, unsigned> _offset,
|
||||||
Type const& _type
|
Type const& _type
|
||||||
):
|
):
|
||||||
IRLValue(_context, &_type),
|
IRLValue(_context, &_type),
|
||||||
m_slot(move(_slot)),
|
m_slot(move(_slot)),
|
||||||
m_offset(_offset)
|
m_offset(std::move(_offset))
|
||||||
{
|
{
|
||||||
|
solAssert(!m_offset.empty(), "");
|
||||||
|
solAssert(!m_slot.empty(), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
string IRStorageItem::retrieveValue() const
|
string IRStorageItem::retrieveValue() const
|
||||||
@ -81,39 +92,45 @@ string IRStorageItem::retrieveValue() const
|
|||||||
if (!m_type->isValueType())
|
if (!m_type->isValueType())
|
||||||
return m_slot;
|
return m_slot;
|
||||||
solUnimplementedAssert(m_type->category() != Type::Category::Function, "");
|
solUnimplementedAssert(m_type->category() != Type::Category::Function, "");
|
||||||
return m_context.utils().readFromStorage(*m_type, m_offset, false) + "(" + m_slot + ")";
|
if (m_offset.type() == typeid(string))
|
||||||
|
return
|
||||||
|
m_context.utils().dynamicReadFromStorage(*m_type, false) +
|
||||||
|
"(" +
|
||||||
|
m_slot +
|
||||||
|
", " +
|
||||||
|
boost::get<string>(m_offset) +
|
||||||
|
")";
|
||||||
|
else if (m_offset.type() == typeid(unsigned))
|
||||||
|
return
|
||||||
|
m_context.utils().readFromStorage(*m_type, boost::get<unsigned>(m_offset), false) +
|
||||||
|
"(" +
|
||||||
|
m_slot +
|
||||||
|
")";
|
||||||
|
|
||||||
|
solAssert(false, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
string IRStorageItem::storeValue(string const& _value, Type const& _sourceType) const
|
string IRStorageItem::storeValue(string const& _value, Type const& _sourceType) const
|
||||||
{
|
{
|
||||||
if (m_type->isValueType())
|
if (m_type->isValueType())
|
||||||
{
|
|
||||||
solAssert(m_type->storageBytes() <= 32, "Invalid storage bytes size.");
|
|
||||||
solAssert(m_type->storageBytes() > 0, "Invalid storage bytes size.");
|
|
||||||
solAssert(m_type->storageBytes() + m_offset <= 32, "");
|
|
||||||
|
|
||||||
solAssert(_sourceType == *m_type, "Different type, but might not be an error.");
|
solAssert(_sourceType == *m_type, "Different type, but might not be an error.");
|
||||||
|
|
||||||
return Whiskers("sstore(<slot>, <update>(sload(<slot>), <prepare>(<value>)))\n")
|
boost::optional<unsigned> offset;
|
||||||
("slot", m_slot)
|
|
||||||
("update", m_context.utils().updateByteSliceFunction(m_type->storageBytes(), m_offset))
|
if (m_offset.type() == typeid(unsigned))
|
||||||
("prepare", m_context.utils().prepareStoreFunction(*m_type))
|
offset = get<unsigned>(m_offset);
|
||||||
("value", _value)
|
|
||||||
.render();
|
return
|
||||||
}
|
m_context.utils().updateStorageValueFunction(*m_type, offset) +
|
||||||
else
|
"(" +
|
||||||
{
|
m_slot +
|
||||||
solAssert(
|
(m_offset.type() == typeid(string) ?
|
||||||
_sourceType.category() == m_type->category(),
|
(", " + get<string>(m_offset)) :
|
||||||
"Wrong type conversation for assignment."
|
""
|
||||||
);
|
) +
|
||||||
if (m_type->category() == Type::Category::Array)
|
", " +
|
||||||
solUnimplementedAssert(false, "");
|
_value +
|
||||||
else if (m_type->category() == Type::Category::Struct)
|
")\n";
|
||||||
solUnimplementedAssert(false, "");
|
|
||||||
else
|
|
||||||
solAssert(false, "Invalid non-value type for assignment.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string IRStorageItem::setToZero() const
|
string IRStorageItem::setToZero() const
|
||||||
|
@ -20,8 +20,11 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libdevcore/Common.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
#include <boost/variant.hpp>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
@ -83,7 +86,7 @@ public:
|
|||||||
IRStorageItem(
|
IRStorageItem(
|
||||||
IRGenerationContext& _context,
|
IRGenerationContext& _context,
|
||||||
std::string _slot,
|
std::string _slot,
|
||||||
unsigned _offset,
|
boost::variant<std::string, unsigned> _offset,
|
||||||
Type const& _type
|
Type const& _type
|
||||||
);
|
);
|
||||||
std::string retrieveValue() const override;
|
std::string retrieveValue() const override;
|
||||||
@ -91,8 +94,14 @@ public:
|
|||||||
|
|
||||||
std::string setToZero() const override;
|
std::string setToZero() const override;
|
||||||
private:
|
private:
|
||||||
std::string m_slot;
|
IRStorageItem(
|
||||||
unsigned m_offset;
|
IRGenerationContext& _context,
|
||||||
|
Type const& _type,
|
||||||
|
std::pair<u256, unsigned> slot_offset
|
||||||
|
);
|
||||||
|
|
||||||
|
std::string const m_slot;
|
||||||
|
boost::variant<std::string, unsigned> const m_offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,5 +9,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f() -> 6
|
// f() -> 6
|
||||||
|
@ -9,5 +9,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f() -> 5
|
// f() -> 5
|
||||||
|
@ -13,6 +13,8 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f(uint256): 0 -> 2
|
// f(uint256): 0 -> 2
|
||||||
// f(uint256): 1 -> 18
|
// f(uint256): 1 -> 18
|
||||||
|
@ -24,6 +24,8 @@ contract C {
|
|||||||
return 500;
|
return 500;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// call(uint256): 0 -> 0
|
// call(uint256): 0 -> 0
|
||||||
// call(uint256): 1 -> 1
|
// call(uint256): 1 -> 1
|
||||||
|
@ -4,6 +4,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ====
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// EVMVersion: >=constantinople
|
// EVMVersion: >=constantinople
|
||||||
// ----
|
// ----
|
||||||
// f(uint256): 7 -> 28
|
// f(uint256): 7 -> 28
|
||||||
|
@ -3,6 +3,8 @@ contract C {
|
|||||||
return a + b + c + d + e;
|
return a + b + c + d + e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f(uint256,uint256,uint256,uint256,uint256): 1, 1, 1, 1, 1
|
// f(uint256,uint256,uint256,uint256,uint256): 1, 1, 1, 1, 1
|
||||||
// -> 5
|
// -> 5
|
||||||
|
@ -3,6 +3,8 @@ contract C {
|
|||||||
return a + b + c + d + e;
|
return a + b + c + d + e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f(uint256,uint256,uint256,uint256,uint256): 1, 1, 1, 1, 1
|
// f(uint256,uint256,uint256,uint256,uint256): 1, 1, 1, 1, 1
|
||||||
// # A comment on the function parameters. #
|
// # A comment on the function parameters. #
|
||||||
|
Loading…
Reference in New Issue
Block a user