Skip to content

Commit 847f0d7

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 2c5a4d5 commit 847f0d7

File tree

5 files changed

+44
-1
lines changed

5 files changed

+44
-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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--strict-assembly --optimize --yul-optimizations T:ts --bin
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
function identity(value) -> ret {
3+
ret := value
4+
}
5+
function fun_fun_31() -> ret {
6+
let x := 0x01
7+
let expr_9 := identity(x)
8+
9+
let expr_10 := 0x01
10+
let expr_25 := 0x05
11+
switch expr_10
12+
case 0 { }
13+
default {
14+
let expr_14 := 0x06
15+
let expr_18 := identity(x)
16+
expr_25 := or(expr_14, expr_18)
17+
}
18+
// expression simplifier spuriously gets a match on this: `expr_25 == or(0x06, expr_18)` where expr_18 is
19+
// from the inner scope
20+
ret := or(expr_9, expr_25)
21+
leave
22+
}
23+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
======= input.yul (EVM) =======
3+
4+
Binary representation:
5+
00

0 commit comments

Comments
 (0)