mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #12047 from ethereum/fixFixedBytesCompilerUtils
Properly handle fixed-byte-like types.
This commit is contained in:
commit
df9721f869
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
Important Bugfixes:
|
Important Bugfixes:
|
||||||
* Immutables: Properly perform sign extension on signed immutables.
|
* Immutables: Properly perform sign extension on signed immutables.
|
||||||
|
* User Defined Value Type: Fix storage layout of user defined value types for underlying types shorter than 32 bytes.
|
||||||
|
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
@ -2537,6 +2537,7 @@ Type const& UserDefinedValueType::underlyingType() const
|
|||||||
{
|
{
|
||||||
Type const* type = m_definition.underlyingType()->annotation().type;
|
Type const* type = m_definition.underlyingType()->annotation().type;
|
||||||
solAssert(type, "");
|
solAssert(type, "");
|
||||||
|
solAssert(type->category() != Category::UserDefinedValueType, "");
|
||||||
return *type;
|
return *type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3071,10 +3072,7 @@ u256 FunctionType::storageSize() const
|
|||||||
|
|
||||||
bool FunctionType::leftAligned() const
|
bool FunctionType::leftAligned() const
|
||||||
{
|
{
|
||||||
if (m_kind == Kind::External)
|
return m_kind == Kind::External;
|
||||||
return true;
|
|
||||||
else
|
|
||||||
solAssert(false, "Alignment property of non-exportable function type requested.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned FunctionType::storageBytes() const
|
unsigned FunctionType::storageBytes() const
|
||||||
|
@ -1108,6 +1108,8 @@ public:
|
|||||||
bool leftAligned() const override { return underlyingType().leftAligned(); }
|
bool leftAligned() const override { return underlyingType().leftAligned(); }
|
||||||
bool canBeStored() const override { return underlyingType().canBeStored(); }
|
bool canBeStored() const override { return underlyingType().canBeStored(); }
|
||||||
u256 storageSize() const override { return underlyingType().storageSize(); }
|
u256 storageSize() const override { return underlyingType().storageSize(); }
|
||||||
|
unsigned storageBytes() const override { return underlyingType().storageBytes(); }
|
||||||
|
|
||||||
bool isValueType() const override
|
bool isValueType() const override
|
||||||
{
|
{
|
||||||
solAssert(underlyingType().isValueType(), "");
|
solAssert(underlyingType().isValueType(), "");
|
||||||
@ -1119,6 +1121,25 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool containsNestedMapping() const override
|
||||||
|
{
|
||||||
|
solAssert(nameable(), "Called for a non nameable type.");
|
||||||
|
solAssert(!underlyingType().containsNestedMapping(), "");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasSimpleZeroValueInMemory() const override
|
||||||
|
{
|
||||||
|
solAssert(underlyingType().hasSimpleZeroValueInMemory(), "");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dataStoredIn(DataLocation _loc) const override
|
||||||
|
{
|
||||||
|
solAssert(!underlyingType().dataStoredIn(_loc), "");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::string toString(bool _short) const override;
|
std::string toString(bool _short) const override;
|
||||||
std::string canonicalName() const override { solAssert(false, ""); }
|
std::string canonicalName() const override { solAssert(false, ""); }
|
||||||
std::string signatureInExternalFunction(bool) const override { solAssert(false, ""); }
|
std::string signatureInExternalFunction(bool) const override { solAssert(false, ""); }
|
||||||
|
@ -1552,10 +1552,13 @@ void CompilerUtils::storeStringData(bytesConstRef _data)
|
|||||||
unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWords)
|
unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWords)
|
||||||
{
|
{
|
||||||
solAssert(_type.isValueType(), "");
|
solAssert(_type.isValueType(), "");
|
||||||
|
Type const* type = &_type;
|
||||||
|
if (auto const* userDefined = dynamic_cast<UserDefinedValueType const*>(type))
|
||||||
|
type = &userDefined->underlyingType();
|
||||||
|
|
||||||
unsigned numBytes = _type.calldataEncodedSize(_padToWords);
|
unsigned numBytes = type->calldataEncodedSize(_padToWords);
|
||||||
bool isExternalFunctionType = false;
|
bool isExternalFunctionType = false;
|
||||||
if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
|
if (auto const* funType = dynamic_cast<FunctionType const*>(type))
|
||||||
if (funType->kind() == FunctionType::Kind::External)
|
if (funType->kind() == FunctionType::Kind::External)
|
||||||
isExternalFunctionType = true;
|
isExternalFunctionType = true;
|
||||||
if (numBytes == 0)
|
if (numBytes == 0)
|
||||||
@ -1570,21 +1573,20 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda
|
|||||||
splitExternalFunctionType(true);
|
splitExternalFunctionType(true);
|
||||||
else if (numBytes != 32)
|
else if (numBytes != 32)
|
||||||
{
|
{
|
||||||
bool leftAligned = _type.category() == Type::Category::FixedBytes;
|
|
||||||
// add leading or trailing zeros by dividing/multiplying depending on alignment
|
// add leading or trailing zeros by dividing/multiplying depending on alignment
|
||||||
unsigned shiftFactor = (32 - numBytes) * 8;
|
unsigned shiftFactor = (32 - numBytes) * 8;
|
||||||
rightShiftNumberOnStack(shiftFactor);
|
rightShiftNumberOnStack(shiftFactor);
|
||||||
if (leftAligned)
|
if (type->leftAligned())
|
||||||
{
|
{
|
||||||
leftShiftNumberOnStack(shiftFactor);
|
leftShiftNumberOnStack(shiftFactor);
|
||||||
cleanupNeeded = false;
|
cleanupNeeded = false;
|
||||||
}
|
}
|
||||||
else if (IntegerType const* intType = dynamic_cast<IntegerType const*>(&_type))
|
else if (IntegerType const* intType = dynamic_cast<IntegerType const*>(type))
|
||||||
if (!intType->isSigned())
|
if (!intType->isSigned())
|
||||||
cleanupNeeded = false;
|
cleanupNeeded = false;
|
||||||
}
|
}
|
||||||
if (_fromCalldata)
|
if (_fromCalldata)
|
||||||
convertType(_type, _type, cleanupNeeded, false, true);
|
convertType(_type, *type, cleanupNeeded, false, true);
|
||||||
|
|
||||||
return numBytes;
|
return numBytes;
|
||||||
}
|
}
|
||||||
@ -1639,12 +1641,10 @@ unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWords,
|
|||||||
"Memory store of more than 32 bytes requested (Type: " + _type.toString(true) + ")."
|
"Memory store of more than 32 bytes requested (Type: " + _type.toString(true) + ")."
|
||||||
);
|
);
|
||||||
|
|
||||||
bool leftAligned = _type.category() == Type::Category::FixedBytes;
|
|
||||||
|
|
||||||
if (_cleanup)
|
if (_cleanup)
|
||||||
convertType(_type, _type, true);
|
convertType(_type, _type, true);
|
||||||
|
|
||||||
if (numBytes != 32 && !leftAligned && !_padToWords)
|
if (numBytes != 32 && !_type.leftAligned() && !_padToWords)
|
||||||
// shift the value accordingly before storing
|
// shift the value accordingly before storing
|
||||||
leftShiftNumberOnStack((32 - numBytes) * 8);
|
leftShiftNumberOnStack((32 - numBytes) * 8);
|
||||||
|
|
||||||
|
@ -113,6 +113,7 @@ void MemoryItem::storeValue(Type const& _sourceType, SourceLocation const&, bool
|
|||||||
if (!m_padded)
|
if (!m_padded)
|
||||||
{
|
{
|
||||||
solAssert(m_dataType->calldataEncodedSize(false) == 1, "Invalid non-padded type.");
|
solAssert(m_dataType->calldataEncodedSize(false) == 1, "Invalid non-padded type.");
|
||||||
|
solAssert(m_dataType->category() != Type::Category::UserDefinedValueType, "");
|
||||||
if (m_dataType->category() == Type::Category::FixedBytes)
|
if (m_dataType->category() == Type::Category::FixedBytes)
|
||||||
m_context << u256(0) << Instruction::BYTE;
|
m_context << u256(0) << Instruction::BYTE;
|
||||||
m_context << Instruction::SWAP1 << Instruction::MSTORE8;
|
m_context << Instruction::SWAP1 << Instruction::MSTORE8;
|
||||||
@ -226,27 +227,17 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const
|
|||||||
m_context << Instruction::POP << Instruction::SLOAD;
|
m_context << Instruction::POP << Instruction::SLOAD;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
Type const* type = m_dataType;
|
||||||
|
if (type->category() == Type::Category::UserDefinedValueType)
|
||||||
|
type = type->encodingType();
|
||||||
bool cleaned = false;
|
bool cleaned = false;
|
||||||
m_context
|
m_context
|
||||||
<< Instruction::SWAP1 << Instruction::SLOAD << Instruction::SWAP1
|
<< Instruction::SWAP1 << Instruction::SLOAD << Instruction::SWAP1
|
||||||
<< u256(0x100) << Instruction::EXP << Instruction::SWAP1 << Instruction::DIV;
|
<< u256(0x100) << Instruction::EXP << Instruction::SWAP1 << Instruction::DIV;
|
||||||
if (m_dataType->category() == Type::Category::FixedPoint)
|
if (type->category() == Type::Category::FixedPoint)
|
||||||
// implementation should be very similar to the integer case.
|
// implementation should be very similar to the integer case.
|
||||||
solUnimplemented("Not yet implemented - FixedPointType.");
|
solUnimplemented("Not yet implemented - FixedPointType.");
|
||||||
if (m_dataType->category() == Type::Category::FixedBytes)
|
else if (FunctionType const* fun = dynamic_cast<decltype(fun)>(type))
|
||||||
{
|
|
||||||
CompilerUtils(m_context).leftShiftNumberOnStack(256 - 8 * m_dataType->storageBytes());
|
|
||||||
cleaned = true;
|
|
||||||
}
|
|
||||||
else if (
|
|
||||||
m_dataType->category() == Type::Category::Integer &&
|
|
||||||
dynamic_cast<IntegerType const&>(*m_dataType).isSigned()
|
|
||||||
)
|
|
||||||
{
|
|
||||||
m_context << u256(m_dataType->storageBytes() - 1) << Instruction::SIGNEXTEND;
|
|
||||||
cleaned = true;
|
|
||||||
}
|
|
||||||
else if (FunctionType const* fun = dynamic_cast<decltype(fun)>(m_dataType))
|
|
||||||
{
|
{
|
||||||
if (fun->kind() == FunctionType::Kind::External)
|
if (fun->kind() == FunctionType::Kind::External)
|
||||||
{
|
{
|
||||||
@ -260,10 +251,24 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const
|
|||||||
m_context << Instruction::MUL << Instruction::OR;
|
m_context << Instruction::MUL << Instruction::OR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (type->leftAligned())
|
||||||
|
{
|
||||||
|
CompilerUtils(m_context).leftShiftNumberOnStack(256 - 8 * type->storageBytes());
|
||||||
|
cleaned = true;
|
||||||
|
}
|
||||||
|
else if (
|
||||||
|
type->category() == Type::Category::Integer &&
|
||||||
|
dynamic_cast<IntegerType const&>(*type).isSigned()
|
||||||
|
)
|
||||||
|
{
|
||||||
|
m_context << u256(type->storageBytes() - 1) << Instruction::SIGNEXTEND;
|
||||||
|
cleaned = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!cleaned)
|
if (!cleaned)
|
||||||
{
|
{
|
||||||
solAssert(m_dataType->sizeOnStack() == 1, "");
|
solAssert(type->sizeOnStack() == 1, "");
|
||||||
m_context << ((u256(0x1) << (8 * m_dataType->storageBytes())) - 1) << Instruction::AND;
|
m_context << ((u256(0x1) << (8 * type->storageBytes())) - 1) << Instruction::AND;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -329,10 +334,13 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
|||||||
Instruction::AND;
|
Instruction::AND;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (m_dataType->category() == Type::Category::FixedBytes)
|
else if (m_dataType->leftAligned())
|
||||||
{
|
{
|
||||||
solAssert(_sourceType.category() == Type::Category::FixedBytes, "source not fixed bytes");
|
solAssert(_sourceType.category() == Type::Category::FixedBytes || (
|
||||||
CompilerUtils(m_context).rightShiftNumberOnStack(256 - 8 * dynamic_cast<FixedBytesType const&>(*m_dataType).numBytes());
|
_sourceType.encodingType() &&
|
||||||
|
_sourceType.encodingType()->category() == Type::Category::FixedBytes
|
||||||
|
), "source not fixed bytes");
|
||||||
|
CompilerUtils(m_context).rightShiftNumberOnStack(256 - 8 * m_dataType->storageBytes());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -2912,24 +2912,20 @@ string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type)
|
|||||||
)");
|
)");
|
||||||
templ("functionName", functionName);
|
templ("functionName", functionName);
|
||||||
|
|
||||||
unsigned storageBytes = _type.storageBytes();
|
Type const* encodingType = &_type;
|
||||||
if (IntegerType const* type = dynamic_cast<IntegerType const*>(&_type))
|
if (_type.category() == Type::Category::UserDefinedValueType)
|
||||||
if (type->isSigned() && storageBytes != 32)
|
encodingType = _type.encodingType();
|
||||||
|
unsigned storageBytes = encodingType->storageBytes();
|
||||||
|
if (IntegerType const* intType = dynamic_cast<IntegerType const*>(encodingType))
|
||||||
|
if (intType->isSigned() && storageBytes != 32)
|
||||||
{
|
{
|
||||||
templ("cleaned", "signextend(" + to_string(storageBytes - 1) + ", value)");
|
templ("cleaned", "signextend(" + to_string(storageBytes - 1) + ", value)");
|
||||||
return templ.render();
|
return templ.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool leftAligned = false;
|
|
||||||
if (
|
|
||||||
_type.category() != Type::Category::Function ||
|
|
||||||
dynamic_cast<FunctionType const&>(_type).kind() == FunctionType::Kind::External
|
|
||||||
)
|
|
||||||
leftAligned = _type.leftAligned();
|
|
||||||
|
|
||||||
if (storageBytes == 32)
|
if (storageBytes == 32)
|
||||||
templ("cleaned", "value");
|
templ("cleaned", "value");
|
||||||
else if (leftAligned)
|
else if (encodingType->leftAligned())
|
||||||
templ("cleaned", shiftLeftFunction(256 - 8 * storageBytes) + "(value)");
|
templ("cleaned", shiftLeftFunction(256 - 8 * storageBytes) + "(value)");
|
||||||
else
|
else
|
||||||
templ("cleaned", "and(value, " + toCompactHexWithPrefix((u256(1) << (8 * storageBytes)) - 1) + ")");
|
templ("cleaned", "and(value, " + toCompactHexWithPrefix((u256(1) << (8 * storageBytes)) - 1) + ")");
|
||||||
@ -2965,7 +2961,7 @@ string YulUtilFunctions::prepareStoreFunction(Type const& _type)
|
|||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
templ("functionName", functionName);
|
templ("functionName", functionName);
|
||||||
if (_type.category() == Type::Category::FixedBytes)
|
if (_type.leftAligned())
|
||||||
templ("actualPrepare", shiftRightFunction(256 - 8 * _type.storageBytes()) + "(value)");
|
templ("actualPrepare", shiftRightFunction(256 - 8 * _type.storageBytes()) + "(value)");
|
||||||
else
|
else
|
||||||
templ("actualPrepare", "value");
|
templ("actualPrepare", "value");
|
||||||
@ -3304,6 +3300,7 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
|||||||
bodyTemplate("cleanOutput", cleanupFunction(_to));
|
bodyTemplate("cleanOutput", cleanupFunction(_to));
|
||||||
string convert;
|
string convert;
|
||||||
|
|
||||||
|
solAssert(_to.category() != Type::Category::UserDefinedValueType, "");
|
||||||
if (auto const* toFixedBytes = dynamic_cast<FixedBytesType const*>(&_to))
|
if (auto const* toFixedBytes = dynamic_cast<FixedBytesType const*>(&_to))
|
||||||
convert = shiftLeftFunction(256 - toFixedBytes->numBytes() * 8);
|
convert = shiftLeftFunction(256 - toFixedBytes->numBytes() * 8);
|
||||||
else if (dynamic_cast<FixedPointType const*>(&_to))
|
else if (dynamic_cast<FixedPointType const*>(&_to))
|
||||||
|
1
test/cmdlineTests/storage_layout_user_defined/args
Normal file
1
test/cmdlineTests/storage_layout_user_defined/args
Normal file
@ -0,0 +1 @@
|
|||||||
|
--storage-layout
|
2
test/cmdlineTests/storage_layout_user_defined/err
Normal file
2
test/cmdlineTests/storage_layout_user_defined/err
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Warning: Source file does not specify required compiler version!
|
||||||
|
--> storage_layout_user_defined/input.sol
|
16
test/cmdlineTests/storage_layout_user_defined/input.sol
Normal file
16
test/cmdlineTests/storage_layout_user_defined/input.sol
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// SPDX-License-Identifier: GPL v3
|
||||||
|
type MyInt128 is int128;
|
||||||
|
type MyInt8 is int8;
|
||||||
|
contract C {
|
||||||
|
// slot 0
|
||||||
|
MyInt128 a;
|
||||||
|
MyInt128 b;
|
||||||
|
// slot 1
|
||||||
|
MyInt128 c;
|
||||||
|
MyInt8 d;
|
||||||
|
MyInt8 e;
|
||||||
|
MyInt8 f;
|
||||||
|
MyInt8 g;
|
||||||
|
// slot 2
|
||||||
|
MyInt8 h;
|
||||||
|
}
|
4
test/cmdlineTests/storage_layout_user_defined/output
Normal file
4
test/cmdlineTests/storage_layout_user_defined/output
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
======= storage_layout_user_defined/input.sol:C =======
|
||||||
|
Contract Storage Layout:
|
||||||
|
{"storage":[{"astId":7,"contract":"storage_layout_user_defined/input.sol:C","label":"a","offset":0,"slot":"0","type":"t_userDefinedValueType(MyInt128)2"},{"astId":10,"contract":"storage_layout_user_defined/input.sol:C","label":"b","offset":16,"slot":"0","type":"t_userDefinedValueType(MyInt128)2"},{"astId":13,"contract":"storage_layout_user_defined/input.sol:C","label":"c","offset":0,"slot":"1","type":"t_userDefinedValueType(MyInt128)2"},{"astId":16,"contract":"storage_layout_user_defined/input.sol:C","label":"d","offset":16,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"},{"astId":19,"contract":"storage_layout_user_defined/input.sol:C","label":"e","offset":17,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"},{"astId":22,"contract":"storage_layout_user_defined/input.sol:C","label":"f","offset":18,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"},{"astId":25,"contract":"storage_layout_user_defined/input.sol:C","label":"g","offset":19,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"},{"astId":28,"contract":"storage_layout_user_defined/input.sol:C","label":"h","offset":20,"slot":"1","type":"t_userDefinedValueType(MyInt8)4"}],"types":{"t_userDefinedValueType(MyInt128)2":{"encoding":"inplace","label":"MyInt128","numberOfBytes":"16"},"t_userDefinedValueType(MyInt8)4":{"encoding":"inplace","label":"MyInt8","numberOfBytes":"1"}}}
|
@ -51,13 +51,13 @@ contract C {
|
|||||||
// compileViaYul: also
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// test_f() -> true
|
// test_f() -> true
|
||||||
// gas irOptimized: 122656
|
// gas irOptimized: 122887
|
||||||
// gas legacy: 125037
|
// gas legacy: 126168
|
||||||
// gas legacyOptimized: 122605
|
// gas legacyOptimized: 123199
|
||||||
// test_g() -> true
|
// test_g() -> true
|
||||||
// gas irOptimized: 95908
|
// gas irOptimized: 96673
|
||||||
// gas legacy: 100586
|
// gas legacy: 101311
|
||||||
// gas legacyOptimized: 95996
|
// gas legacyOptimized: 96566
|
||||||
// addresses(uint256): 0 -> 0x18
|
// addresses(uint256): 0 -> 0x18
|
||||||
// addresses(uint256): 1 -> 0x19
|
// addresses(uint256): 1 -> 0x19
|
||||||
// addresses(uint256): 3 -> 0x1b
|
// addresses(uint256): 3 -> 0x1b
|
||||||
|
@ -31,7 +31,7 @@ contract C {
|
|||||||
// write_a() ->
|
// write_a() ->
|
||||||
// a() -> 0x2001
|
// a() -> 0x2001
|
||||||
// write_b() ->
|
// write_b() ->
|
||||||
// b() -> 0xf00e000000000000000000000000000000000000000000000000000000000000
|
// b() -> 0x5403000000000000000000000000000000000000000000000000000000000000
|
||||||
// get_b(uint256): 0 -> 0xf000000000000000000000000000000000000000000000000000000000000000
|
// get_b(uint256): 0 -> 0x5400000000000000000000000000000000000000000000000000000000000000
|
||||||
// get_b(uint256): 1 -> 0x0e00000000000000000000000000000000000000000000000000000000000000
|
// get_b(uint256): 1 -> 0x0300000000000000000000000000000000000000000000000000000000000000
|
||||||
// get_b(uint256): 2 -> FAILURE, hex"4e487b71", 0x32
|
// get_b(uint256): 2 -> FAILURE, hex"4e487b71", 0x32
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
type MyInt8 is int8;
|
||||||
|
contract C {
|
||||||
|
MyInt8 public x = MyInt8.wrap(-5);
|
||||||
|
|
||||||
|
/// The most significant bit is flipped to 0
|
||||||
|
function create_dirty_slot() external {
|
||||||
|
uint mask = 2**255 -1;
|
||||||
|
assembly {
|
||||||
|
let value := sload(x.slot)
|
||||||
|
sstore(x.slot, and(mask, value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function read_unclean_value() external returns (bytes32 ret) {
|
||||||
|
MyInt8 value = x;
|
||||||
|
assembly {
|
||||||
|
ret := value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// x() -> -5
|
||||||
|
// create_dirty_slot() ->
|
||||||
|
// read_unclean_value() -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb
|
@ -0,0 +1,21 @@
|
|||||||
|
type MyInt is int16;
|
||||||
|
type MyBytes is bytes2;
|
||||||
|
contract C {
|
||||||
|
MyInt immutable a = MyInt.wrap(-2);
|
||||||
|
MyBytes immutable b = MyBytes.wrap("ab");
|
||||||
|
function() internal returns (uint) immutable f = g;
|
||||||
|
function direct() view external returns (MyInt, MyBytes) {
|
||||||
|
return (a, b);
|
||||||
|
}
|
||||||
|
function viaasm() view external returns (bytes32 x, bytes32 y) {
|
||||||
|
MyInt _a = a;
|
||||||
|
MyBytes _b = b;
|
||||||
|
assembly { x := _a y := _b }
|
||||||
|
}
|
||||||
|
function g() internal pure returns (uint) { return 2; }
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// direct() -> -2, 0x6162000000000000000000000000000000000000000000000000000000000000
|
||||||
|
// viaasm() -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe, 0x6162000000000000000000000000000000000000000000000000000000000000
|
@ -0,0 +1,74 @@
|
|||||||
|
type MyInt8 is int8;
|
||||||
|
type MyAddress is address;
|
||||||
|
type MyInt96 is int96;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
MyInt8 a;
|
||||||
|
MyInt8 b;
|
||||||
|
MyInt8 c;
|
||||||
|
MyAddress d;
|
||||||
|
|
||||||
|
MyAddress e;
|
||||||
|
|
||||||
|
MyAddress f;
|
||||||
|
MyInt96 g;
|
||||||
|
|
||||||
|
function storage_a() pure external returns(uint slot, uint offset) {
|
||||||
|
assembly {
|
||||||
|
slot := a.slot
|
||||||
|
offset := a.offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function storage_b() pure external returns(uint slot, uint offset) {
|
||||||
|
assembly {
|
||||||
|
slot := b.slot
|
||||||
|
offset := b.offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function storage_c() pure external returns(uint slot, uint offset) {
|
||||||
|
assembly {
|
||||||
|
slot := d.slot
|
||||||
|
offset := c.offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function storage_d() pure external returns(uint slot, uint offset) {
|
||||||
|
assembly {
|
||||||
|
slot := d.slot
|
||||||
|
offset := d.offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function storage_e() pure external returns(uint slot, uint offset) {
|
||||||
|
assembly {
|
||||||
|
slot := e.slot
|
||||||
|
offset := e.offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function storage_f() pure external returns(uint slot, uint offset) {
|
||||||
|
assembly {
|
||||||
|
slot := f.slot
|
||||||
|
offset := f.offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function storage_g() pure external returns(uint slot, uint offset) {
|
||||||
|
assembly {
|
||||||
|
slot := g.slot
|
||||||
|
offset := g.offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// storage_a() -> 0, 0
|
||||||
|
// storage_b() -> 0, 1
|
||||||
|
// storage_c() -> 0, 2
|
||||||
|
// storage_d() -> 0, 3
|
||||||
|
// storage_e() -> 1, 0
|
||||||
|
// storage_f() -> 2, 0
|
||||||
|
// storage_g() -> 2, 0x14
|
@ -0,0 +1,167 @@
|
|||||||
|
type MyInt64 is int64;
|
||||||
|
struct HalfSlot {
|
||||||
|
MyInt64 a;
|
||||||
|
MyInt64 b;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RegularHalfSlot {
|
||||||
|
int64 a;
|
||||||
|
int64 b;
|
||||||
|
}
|
||||||
|
|
||||||
|
type MyAddress is address;
|
||||||
|
type MyInt96 is int96;
|
||||||
|
struct FullSlot {
|
||||||
|
MyInt96 a;
|
||||||
|
MyAddress b;
|
||||||
|
}
|
||||||
|
struct RegularFullSlot {
|
||||||
|
int96 a;
|
||||||
|
address b;
|
||||||
|
}
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
HalfSlot public a;
|
||||||
|
RegularHalfSlot public ra;
|
||||||
|
|
||||||
|
HalfSlot public b;
|
||||||
|
RegularHalfSlot public rb;
|
||||||
|
|
||||||
|
HalfSlot public c;
|
||||||
|
RegularHalfSlot public rc;
|
||||||
|
|
||||||
|
FullSlot public d;
|
||||||
|
RegularFullSlot public rd;
|
||||||
|
|
||||||
|
function storage_a() pure external returns(uint slot, uint offset) {
|
||||||
|
assembly {
|
||||||
|
slot := a.slot
|
||||||
|
offset := a.offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function storage_ra() pure external returns(uint slot, uint offset) {
|
||||||
|
assembly {
|
||||||
|
slot := ra.slot
|
||||||
|
offset := ra.offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function storage_b() pure external returns(uint slot, uint offset) {
|
||||||
|
assembly {
|
||||||
|
slot := b.slot
|
||||||
|
offset := b.offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function storage_rb() pure external returns(uint slot, uint offset) {
|
||||||
|
assembly {
|
||||||
|
slot := rb.slot
|
||||||
|
offset := rb.offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function storage_c() pure external returns(uint slot, uint offset) {
|
||||||
|
assembly {
|
||||||
|
slot := c.slot
|
||||||
|
offset := c.offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function storage_rc() pure external returns(uint slot, uint offset) {
|
||||||
|
assembly {
|
||||||
|
slot := rc.slot
|
||||||
|
offset := rc.offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function storage_d() pure external returns(uint slot, uint offset) {
|
||||||
|
assembly {
|
||||||
|
slot := d.slot
|
||||||
|
offset := d.offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function storage_rd() pure external returns(uint slot, uint offset) {
|
||||||
|
assembly {
|
||||||
|
slot := rd.slot
|
||||||
|
offset := rd.offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function set_a(MyInt64 _a, MyInt64 _b) external {
|
||||||
|
a.a = _a;
|
||||||
|
a.b = _b;
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_ra(int64 _a, int64 _b) external {
|
||||||
|
ra.a = _a;
|
||||||
|
ra.b = _b;
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_b(MyInt64 _a, MyInt64 _b) external {
|
||||||
|
b.a = _a;
|
||||||
|
b.b = _b;
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_rb(int64 _a, int64 _b) external {
|
||||||
|
rb.a = _a;
|
||||||
|
rb.b = _b;
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_c(MyInt64 _a, MyInt64 _b) external {
|
||||||
|
c.a = _a;
|
||||||
|
c.b = _b;
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_rc(int64 _a, int64 _b) external {
|
||||||
|
rc.a = _a;
|
||||||
|
rc.b = _b;
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_d(MyInt96 _a, MyAddress _b) external {
|
||||||
|
d.a = _a;
|
||||||
|
d.b = _b;
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_rd(int96 _a, address _b) external {
|
||||||
|
rd.a = _a;
|
||||||
|
rd.b = _b;
|
||||||
|
}
|
||||||
|
|
||||||
|
function read_slot(uint slot) view external returns (uint value) {
|
||||||
|
assembly {
|
||||||
|
value := sload(slot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// storage_a() -> 0, 0
|
||||||
|
// set_a(int64,int64): 100, 200 ->
|
||||||
|
// read_slot(uint256): 0 -> 0xc80000000000000064
|
||||||
|
// storage_ra() -> 1, 0
|
||||||
|
// set_ra(int64,int64): 100, 200 ->
|
||||||
|
// read_slot(uint256): 1 -> 0xc80000000000000064
|
||||||
|
// storage_b() -> 2, 0
|
||||||
|
// set_b(int64,int64): 0, 200 ->
|
||||||
|
// read_slot(uint256): 2 -> 3689348814741910323200
|
||||||
|
// storage_rb() -> 3, 0
|
||||||
|
// set_rb(int64,int64): 0, 200 ->
|
||||||
|
// read_slot(uint256): 3 -> 3689348814741910323200
|
||||||
|
// storage_c() -> 4, 0
|
||||||
|
// set_c(int64,int64): 100, 0 ->
|
||||||
|
// read_slot(uint256): 4 -> 0x64
|
||||||
|
// storage_rc() -> 5, 0
|
||||||
|
// set_rc(int64,int64): 100, 0 ->
|
||||||
|
// read_slot(uint256): 5 -> 0x64
|
||||||
|
// storage_d() -> 6, 0
|
||||||
|
// set_d(int96,address): 39614081257132168796771975167, 1461501637330902918203684832716283019655932542975 ->
|
||||||
|
// read_slot(uint256): 6 -> -39614081257132168796771975169
|
||||||
|
// storage_rd() -> 7, 0
|
||||||
|
// set_rd(int96,address): 39614081257132168796771975167, 1461501637330902918203684832716283019655932542975 ->
|
||||||
|
// read_slot(uint256): 7 -> -39614081257132168796771975169
|
@ -0,0 +1,35 @@
|
|||||||
|
type MyInt is int16;
|
||||||
|
contract C {
|
||||||
|
bytes2 first = "ab";
|
||||||
|
MyInt public a = MyInt.wrap(-2);
|
||||||
|
bytes2 third = "ef";
|
||||||
|
function direct() external returns (MyInt) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
function indirect() external returns (int16) {
|
||||||
|
return MyInt.unwrap(a);
|
||||||
|
}
|
||||||
|
function toMemDirect() external returns (MyInt[1] memory) {
|
||||||
|
return [a];
|
||||||
|
}
|
||||||
|
function toMemIndirect() external returns (int16[1] memory) {
|
||||||
|
return [MyInt.unwrap(a)];
|
||||||
|
}
|
||||||
|
function div() external returns (int16) {
|
||||||
|
return MyInt.unwrap(a) / 2;
|
||||||
|
}
|
||||||
|
function viaasm() external returns (bytes32 x) {
|
||||||
|
MyInt st = a;
|
||||||
|
assembly { x := st }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// a() -> -2
|
||||||
|
// direct() -> -2
|
||||||
|
// indirect() -> -2
|
||||||
|
// toMemDirect() -> -2
|
||||||
|
// toMemIndirect() -> -2
|
||||||
|
// div() -> -1
|
||||||
|
// viaasm() -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe
|
Loading…
Reference in New Issue
Block a user