Skip to content

Commit b22b2a6

Browse files
committed
Yul expression simplifier: Don't substitute out of scope variables
In some circumstances (in particular if not the AST is not in SSA form), it could happen that the ExpressionSimplifier would substitute out-of-scope variables.
1 parent c79f2cc commit b22b2a6

File tree

3 files changed

+49
-1
lines changed

3 files changed

+49
-1
lines changed

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Compiler Features:
77

88
Bugfixes:
99
* Assembler: Fix not using a fixed-width type for IDs being assigned to subassemblies nested more than one level away, resulting in inconsistent `--asm-json` output between target architectures.
10+
* Yul Optimizer: Fix edge case in which invalid Yul code is produced by ExpressionSimplifier due to expressions being substituted that contain out-of-scope variables.
1011

1112
### 0.8.30 (2025-05-07)
1213

libyul/optimiser/ExpressionSimplifier.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <libyul/optimiser/SimplificationRules.h>
2626
#include <libyul/optimiser/OptimiserStep.h>
2727
#include <libyul/optimiser/OptimizerUtilities.h>
28+
#include <libyul/optimiser/Semantics.h>
2829
#include <libyul/AST.h>
2930
#include <libyul/Utilities.h>
3031

@@ -45,7 +46,19 @@ void ExpressionSimplifier::visit(Expression& _expression)
4546
while (auto const* match = SimplificationRules::findFirstMatch(
4647
_expression,
4748
m_dialect,
48-
[this](YulName _var) { return variableValue(_var); }
49+
[this](YulName const& _var) -> AssignedValue const* {
50+
AssignedValue const* value = variableValue(_var);
51+
if (!value || !value->value)
52+
return nullptr;
53+
54+
// check that all variables in the value expression are in current scope
55+
MovableChecker const checker(m_dialect, *value->value);
56+
for (YulName const& referencedVar: checker.referencedVariables())
57+
if (!inScope(referencedVar))
58+
return nullptr; // don't substitute if any referenced var is out of scope
59+
60+
return value;
61+
}
4962
))
5063
{
5164
auto const* evmDialect = dynamic_cast<EVMDialect const*>(&m_dialect);
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// regression test for https://github.com/ethereum/solidity/issues/16155
2+
{
3+
function identity(value) -> ret { ret := value }
4+
5+
function f() -> ret
6+
{
7+
let id1 := identity(0x01)
8+
let x := 0x05
9+
{
10+
let const_six := 0x06
11+
let id2 := identity(0x01)
12+
x := or(0x06, id2)
13+
}
14+
// check that we don't substitute `or(const_six, id2)` for `x`
15+
ret := or(id1, x)
16+
}
17+
18+
mstore(42, f())
19+
}
20+
// ----
21+
// step: expressionSimplifier
22+
//
23+
// {
24+
// { mstore(42, f()) }
25+
// function identity(value) -> ret
26+
// { ret := value }
27+
// function f() -> ret_1
28+
// {
29+
// let id1 := identity(0x01)
30+
// let x := 0x05
31+
// { x := or(0x06, id1) }
32+
// ret_1 := or(id1, x)
33+
// }
34+
// }

0 commit comments

Comments
 (0)