Skip to content

Commit 68f8693

Browse files
committed
[optimize-instructions] Fold relational comparisons of tee/get pairs
Fixes #7672. `LocalCSE` rewrites repeated subexpressions as a `local.tee` followed by a `local.get`, so two identical operands to a relational comparison can reach `OptimizeInstructions` as `(rel (local.tee $n X) (local.get $n))`. The pass has a helper for exactly this shape (`areMatchingTeeAndGet`) but consults it only in the `RefEq` and `Select` handlers, not in `optimizeRelational`. The comparison survives into the `-O3` output and blocks subsequent dead-code elimination. Add the fold at the top of `optimizeRelational`. The result of a self-comparison is a constant (`gt`/`lt`/`ne` -> 0, `ge`/`le`/`eq` -> 1). X is evaluated once for its side effects and then dropped, so the reproduction's load chain preserves its original trap behavior. Refs #6719 -- the drop-before-unreachable fix that made the residual `drop; unreachable` visible in the issue's output.
1 parent 6c97a76 commit 68f8693

2 files changed

Lines changed: 62 additions & 0 deletions

File tree

‎src/passes/OptimizeInstructions.cpp‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4990,6 +4990,47 @@ struct OptimizeInstructions
49904990
using namespace Match;
49914991

49924992
auto type = curr->right->type;
4993+
4994+
// (rel (local.tee $n X) (local.get $n)) compares X against itself, so
4995+
// the result is a constant. Evaluate X once for its side effects,
4996+
// discard the tee's value, and return the constant. Do not duplicate X:
4997+
// it may trap or write (the reproduction is a chain of loads).
4998+
if (areMatchingTeeAndGet(curr->left, curr->right)) {
4999+
bool isTrue;
5000+
switch (curr->op) {
5001+
case GtSInt32:
5002+
case GtUInt32:
5003+
case LtSInt32:
5004+
case LtUInt32:
5005+
case GtSInt64:
5006+
case GtUInt64:
5007+
case LtSInt64:
5008+
case LtUInt64:
5009+
case NeInt32:
5010+
case NeInt64:
5011+
isTrue = false;
5012+
break;
5013+
case GeSInt32:
5014+
case GeUInt32:
5015+
case LeSInt32:
5016+
case LeUInt32:
5017+
case GeSInt64:
5018+
case GeUInt64:
5019+
case LeSInt64:
5020+
case LeUInt64:
5021+
case EqInt32:
5022+
case EqInt64:
5023+
isTrue = true;
5024+
break;
5025+
default:
5026+
WASM_UNREACHABLE("unexpected relational op");
5027+
}
5028+
Builder builder(*getModule());
5029+
return builder.makeSequence(
5030+
builder.makeDrop(curr->left),
5031+
builder.makeConst(Literal::makeFromInt32(isTrue, Type::i32)));
5032+
}
5033+
49935034
if (curr->left->type.isInteger()) {
49945035
if (curr->op == Abstract::getBinary(type, Abstract::Eq) ||
49955036
curr->op == Abstract::getBinary(type, Abstract::Ne)) {
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
;; RUN: wasm-opt -all --optimize-instructions %s -S -o - | filecheck %s
2+
3+
;; (i32.gt_s (local.tee $0 X) (local.get $0)) is X compared with itself,
4+
;; always 0. The tee and its load are preserved for trap behavior; the
5+
;; comparison folds to a constant.
6+
;; CHECK: (drop
7+
;; CHECK-NEXT: (local.tee $0
8+
;; CHECK-NEXT: (i32.load
9+
;; CHECK-NEXT: (i32.const 0)
10+
;; CHECK-NEXT: )
11+
;; CHECK-NEXT: )
12+
;; CHECK-NEXT: )
13+
;; CHECK-NEXT: (i32.const 0)
14+
(module
15+
(memory 1)
16+
(func $f (result i32) (local $0 i32)
17+
(i32.gt_s
18+
(local.tee $0 (i32.load (i32.const 0)))
19+
(local.get $0))
20+
)
21+
)

0 commit comments

Comments
 (0)