Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Compiler Features:

Bugfixes:
* Code Generator: Fix ICE on parenthesized custom error construction in require statement.
* Code Generator: Fix uninitialized internal function pointers being read from a packed storage slot with the wrong value when a subsequent variable in the slot holds a non-zero value.
* Commandline Interface: Report proper error instead of ICE on non-hex mixed-case address value given via `--libraries`.

Build System:
Expand Down
4 changes: 4 additions & 0 deletions libsolidity/codegen/LValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ void GenericStorageItem<IsTransient>::retrieveValue(langutil::SourceLocation con
if (type->category() == Type::Category::UserDefinedValueType)
type = type->encodingType();
bool cleaned = false;
// shift bits to the right positioning the state variable at the start of the slot
m_context
<< Instruction::SWAP1 << s_loadInstruction << Instruction::SWAP1
<< u256(0x100) << Instruction::EXP << Instruction::SWAP1 << Instruction::DIV;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note: If this is supposed to do shift, it does not need to use EXP instruction for that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DIV does the right shifting. I am trying to remember exactly, but I think that EXP is just telling by how much you shift.
I will confirm this.

@matheusaaguiar matheusaaguiar Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, just for clarity, I think this uses DIV (and other places MUL) because shift opcodes were introduced in later EVM versions.

@matheusaaguiar matheusaaguiar Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had "decoded" this before, but it was used with MUL in another place.
The variable offset is given in bytes, EXP 0x100, offset computes the amount of bits to shift when using DIV.

256^offset = (2^8)^offset = 2^8 * offset.

When the bytes of the slot are divided by this value, it is equivalent to shifting it 8 * offset bits to the right.

Expand All @@ -257,9 +258,12 @@ void GenericStorageItem<IsTransient>::retrieveValue(langutil::SourceLocation con
}
else if (fun->kind() == FunctionType::Kind::Internal)
{
// internal function pointers occupy 8 bytes, so we mask the remaining bytes in the slot
m_context << ((u256(0x1) << (8 * type->storageBytes())) - 1) << Instruction::AND;
m_context << Instruction::DUP1 << Instruction::ISZERO;
CompilerUtils(m_context).pushZeroValue(*fun);
m_context << Instruction::MUL << Instruction::OR;
cleaned = true;
}
}
else if (type->leftAligned())
Expand Down
13 changes: 9 additions & 4 deletions test/cmdlineTests/optimizer_inliner_dynamic_reference/output
Original file line number Diff line number Diff line change
Expand Up @@ -112,15 +112,20 @@ sub_0: assembly {
/* "input.sol":295:298 x() */
tag_19
swap1
0xffffffff
/* "input.sol":295:296 x */
dup1
iszero
tag_20
0xffffffffffffffff
dup4
and
iszero
mul
or
/* "input.sol":295:298 x() */
0xffffffff
dup2
and
swap2
and
or
jump // in
tag_19:
/* "input.sol":295:302 x() + 1 */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,20 @@ sub_0: assembly {
/* "input.sol":289:292 x() */
tag_16
swap1
0xffffffff
/* "input.sol":289:290 x */
dup1
iszero
tag_17
0xffffffffffffffff
dup4
and
iszero
mul
or
/* "input.sol":289:292 x() */
0xffffffff
dup2
and
swap2
and
or
jump // in
tag_16:
/* "input.sol":289:296 x() + 1 */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,8 @@ contract C {
// ====
// compileViaYul: false
// ----
// t() -> FAILURE
// t() -> FAILURE, hex"4e487b71", 0x51
// gas legacy: 77534
// gas legacy code: 69600
// gas legacyOptimized: 76677
// gas legacyOptimized code: 28600
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ contract C {
// gas irOptimized code: 19000
// gas legacy: 79492
// gas legacy code: 69600
// gas legacyOptimized: 77587
// gas legacyOptimized: 77611
// gas legacyOptimized code: 28600
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
contract C {
uint32 public x = 1;
function() internal internalFunctionPointer;
uint32 private y = 2;

function testComparison() public view returns (bool) {
function() internal uninitializedPointer;
if (internalFunctionPointer == uninitializedPointer)
return true;
return false;
}
function testLocalAssignment() public view returns (bool) {
function() internal uninitializedPointer;
function() internal localPointer = internalFunctionPointer;
if (localPointer == uninitializedPointer)
return true;
return false;
}
}
// ----
// testComparison() -> true
// testLocalAssignment() -> true
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
contract C {
uint32 public transient x;
function() internal transient internalFunctionPointer;
uint32 private transient y;

function testComparison() public returns (bool) {
x = 1;
y = 2;
function() internal uninitializedPointer;
if (internalFunctionPointer == uninitializedPointer)
return true;
return false;
}
function testLocalAssignment() public returns (bool) {
x = 1;
y = 2;
function() internal uninitializedPointer;
function() internal localPointer = internalFunctionPointer;
if (localPointer == uninitializedPointer)
return true;
return false;
}
}
// ====
// EVMVersion: >=cancun
// ----
// testComparison() -> true
// testLocalAssignment() -> true