Skip to content

Commit b12d8fa

Browse files
authored
Merge pull request #14574 from ethereum/fix-data-flow-analyzer-inconsistent-variable-clearing
Fix variable clearing in DataFlowAnalyzer being affected by variable names
2 parents b54e720 + 7c36f23 commit b12d8fa

File tree

11 files changed

+248
-6
lines changed

11 files changed

+248
-6
lines changed

Changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Bugfixes:
1616
* NatSpec: Fix internal error when requesting userdoc or devdoc for a contract that emits an event defined in a foreign contract or interface.
1717
* SMTChecker: Fix encoding error that causes loops to unroll after completion.
1818
* SMTChecker: Fix inconsistency on constant condition checks when ``while`` or ``for`` loops are unrolled before the condition check.
19+
* Yul Optimizer: Fix replacement decisions during CSE being affected by Yul variable names generated by the compiler, resulting in different (but equivalent) bytecode in some situations.
1920

2021

2122
### 0.8.21 (2023-07-19)

libyul/optimiser/DataFlowAnalyzer.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,13 +351,14 @@ void DataFlowAnalyzer::clearValues(std::set<YulString> _variables)
351351
});
352352

353353
// Also clear variables that reference variables to be cleared.
354+
std::set<YulString> referencingVariables;
354355
for (auto const& variableToClear: _variables)
355356
for (auto const& [ref, names]: m_state.references)
356357
if (names.count(variableToClear))
357-
_variables.emplace(ref);
358+
referencingVariables.emplace(ref);
358359

359360
// Clear the value and update the reference relation.
360-
for (auto const& name: _variables)
361+
for (auto const& name: _variables + referencingVariables)
361362
{
362363
m_state.value.erase(name);
363364
m_state.references.erase(name);
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
object "C" {
2+
code {}
3+
4+
object "C_deployed" {
5+
code {
6+
main(0, 0)
7+
8+
function main(a, b) {
9+
for {} 1 {}
10+
{
11+
if iszero(a) { break }
12+
13+
let mid := avg(a, a)
14+
switch a
15+
case 0 {
16+
a := mid
17+
}
18+
default {
19+
sstore(0, mid)
20+
}
21+
}
22+
}
23+
24+
function avg(x, y) -> var {
25+
let __placeholder__ := add(x, y)
26+
var := add(__placeholder__, __placeholder__)
27+
}
28+
}
29+
}
30+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env bash
2+
set -eo pipefail
3+
4+
# This is a regression test against https://github.com/ethereum/solidity/issues/14494
5+
# Due to the bug, a decision about which variable to use to replace a subexpression in CSE would
6+
# depend on sorting order of variable names. A variable not being used as a replacement could lead
7+
# to it being unused in general and removed by Unused Pruner. This would show up as a difference
8+
# in the bytecode.
9+
10+
# shellcheck source=scripts/common.sh
11+
source "${REPO_ROOT}/scripts/common.sh"
12+
# shellcheck source=scripts/common_cmdline.sh
13+
source "${REPO_ROOT}/scripts/common_cmdline.sh"
14+
15+
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
16+
17+
function assemble_with_variable_name {
18+
local input_file="$1"
19+
local variable_name="$2"
20+
21+
sed -e "s|__placeholder__|${variable_name}|g" "$input_file" | msg_on_error --no-stderr \
22+
"$SOLC" --strict-assembly - --optimize --debug-info none |
23+
stripCLIDecorations
24+
}
25+
26+
diff_values \
27+
"$(assemble_with_variable_name "${SCRIPT_DIR}/cse_bug.yul" _1)" \
28+
"$(assemble_with_variable_name "${SCRIPT_DIR}/cse_bug.yul" _2)"

test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ contract test {
4848
}
4949
// ----
5050
// constructor()
51-
// gas irOptimized: 1722610
51+
// gas irOptimized: 1722166
5252
// gas legacy: 2210160
5353
// gas legacyOptimized: 1734152
5454
// div(uint256,uint256): 3141592653589793238, 88714123 -> 35412542528203691288251815328

test/libsolidity/semanticTests/externalContracts/ramanujan_pi.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ contract test {
3333
}
3434
// ----
3535
// constructor()
36-
// gas irOptimized: 407075
36+
// gas irOptimized: 406595
3737
// gas legacy: 631753
3838
// gas legacyOptimized: 459425
3939
// prb_pi() -> 3141592656369545286

test/libsolidity/semanticTests/externalContracts/strings.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ contract test {
4949
}
5050
// ----
5151
// constructor()
52-
// gas irOptimized: 634328
52+
// gas irOptimized: 633680
5353
// gas legacy: 1065857
5454
// gas legacyOptimized: 725423
5555
// toSlice(string): 0x20, 11, "hello world" -> 11, 0xa0
@@ -69,6 +69,6 @@ contract test {
6969
// gas legacy: 31621
7070
// gas legacyOptimized: 27914
7171
// benchmark(string,bytes32): 0x40, 0x0842021, 8, "solidity" -> 0x2020
72-
// gas irOptimized: 1981677
72+
// gas irOptimized: 1981664
7373
// gas legacy: 4235651
7474
// gas legacyOptimized: 2319622
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
main(0, 0)
3+
4+
function main(a, b) {
5+
for {} 1 {}
6+
{
7+
if iszero(a) { break }
8+
9+
let mid := avg(a, a)
10+
switch a
11+
case 0 {
12+
a := mid
13+
}
14+
default {
15+
sstore(0, mid)
16+
}
17+
}
18+
}
19+
20+
function avg(x, y) -> var {
21+
// NOTE: Variable names should not affect CSE.
22+
// This should not be optimized differently than name_dependent_cse_bug_part_2.yul.
23+
// `let mid := var` should be present in both or missing in both.
24+
let _1 := add(x, y)
25+
var := add(_1, _1)
26+
}
27+
}
28+
// ====
29+
// EVMVersion: >=shanghai
30+
// ----
31+
// step: fullSuite
32+
//
33+
// {
34+
// {
35+
// let a := 0
36+
// for { } a { }
37+
// {
38+
// let _1 := add(a, a)
39+
// let var := add(_1, _1)
40+
// switch a
41+
// case 0 { a := var }
42+
// default { sstore(0, var) }
43+
// }
44+
// }
45+
// }
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
main(0, 0)
3+
4+
function main(a, b) {
5+
for {} 1 {}
6+
{
7+
if iszero(a) { break }
8+
9+
let mid := avg(a, a)
10+
switch a
11+
case 0 {
12+
a := mid
13+
}
14+
default {
15+
sstore(0, mid)
16+
}
17+
}
18+
}
19+
20+
function avg(x, y) -> var {
21+
// NOTE: Variable names should not affect CSE.
22+
// This should not be optimized differently than name_dependent_cse_bug_part_2_pre_shanghai.yul.
23+
// `let mid := var` should be present in both or missing in both.
24+
let _1 := add(x, y)
25+
var := add(_1, _1)
26+
}
27+
}
28+
// ====
29+
// EVMVersion: <shanghai
30+
// ----
31+
// step: fullSuite
32+
//
33+
// {
34+
// {
35+
// let a := 0
36+
// let a_1 := 0
37+
// for { } a_1 { }
38+
// {
39+
// let _1 := add(a_1, a_1)
40+
// let var := add(_1, _1)
41+
// switch a_1
42+
// case 0 { a_1 := var }
43+
// default { sstore(a, var) }
44+
// }
45+
// }
46+
// }
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
main(0, 0)
3+
4+
function main(a, b) {
5+
for {} 1 {}
6+
{
7+
if iszero(a) { break }
8+
9+
let mid := avg(a, a)
10+
switch a
11+
case 0 {
12+
a := mid
13+
}
14+
default {
15+
sstore(0, mid)
16+
}
17+
}
18+
}
19+
20+
function avg(x, y) -> var {
21+
// NOTE: Variable names should not affect CSE.
22+
// This should not be optimized differently than name_dependent_cse_bug_part_1.yul.
23+
// `let mid := var` should be present in both or missing in both.
24+
let _2 := add(x, y)
25+
var := add(_2, _2)
26+
}
27+
}
28+
// ====
29+
// EVMVersion: >=shanghai
30+
// ----
31+
// step: fullSuite
32+
//
33+
// {
34+
// {
35+
// let a := 0
36+
// for { } a { }
37+
// {
38+
// let _1 := add(a, a)
39+
// let var := add(_1, _1)
40+
// switch a
41+
// case 0 { a := var }
42+
// default { sstore(0, var) }
43+
// }
44+
// }
45+
// }

0 commit comments

Comments
 (0)