From 939e44b4f102222c646d71f947ca94738561c700 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 4 Mar 2026 09:53:23 -0800 Subject: [PATCH 1/2] fix --- src/passes/OptimizeInstructions.cpp | 21 +++++++++++ .../lit/passes/optimize-instructions-mvp.wast | 37 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index 28d5ae68153..770cf75c391 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -2793,6 +2793,7 @@ struct OptimizeInstructions // Ignore extraneous things and compare them syntactically. We can also // look at the full fallthrough for both sides now. + auto* originalLeft = left; left = getFallthrough(left); auto* originalRight = right; right = getFallthrough(right); @@ -2821,6 +2822,26 @@ struct OptimizeInstructions } } + // The same, with left, as we can have this situation: + // + // (local.tee $x ..) + // (something using $x) + // ) + // (something using $x) + // + // The fallthroughs are identical, but the tee may cause us to read a + // different value. + if (originalLeft != left) { + auto originalLeftEffects = effects(originalLeft); + // |left == right| here (we would have exited early, otherwise, above), so + // we could compute either. Compute |left| as it might have better cache + // locality. + auto leftEffects = effects(left); + if (originalLeftEffects.invalidates(leftEffects)) { + return false; + } + } + // To be equal, they must also be known to return the same result // deterministically. We check the right side, as if the right is marked // idempotent, that is enough (that tells us it does not generate a new diff --git a/test/lit/passes/optimize-instructions-mvp.wast b/test/lit/passes/optimize-instructions-mvp.wast index 8a4b729fa9c..f0b831d060e 100644 --- a/test/lit/passes/optimize-instructions-mvp.wast +++ b/test/lit/passes/optimize-instructions-mvp.wast @@ -16451,6 +16451,43 @@ ) ) ) + + ;; CHECK: (func $ternary-tee-with-identical-values (param $x i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.and + ;; CHECK-NEXT: (local.tee $x + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (i32.eqz + ;; CHECK-NEXT: (local.get $x) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $ternary-tee-with-identical-values (param $x i32) + (drop + ;; The tee's value, and the select's condition, are equal. If we just + ;; look at them, we could think they are equal, and simplify this. + ;; However, the tee modifies $x, so we cannot fold the those two + ;; expressions together while removing the select. (We can, though, + ;; remove the select and the const, and use an and, but we do remain with + ;; two copies of the eq.) + (select + (local.tee $x + (i32.eqz + (local.get $x) + ) + ) + (i32.const 0) + (i32.eqz + (local.get $x) + ) + ) + ) + ) + ;; CHECK: (func $send-i32 (param $0 i32) ;; CHECK-NEXT: ) (func $send-i32 (param i32)) From ce9c7bc1c0f7cfecf6b2b2ad0bb5a9c1be008d1a Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 4 Mar 2026 09:57:41 -0800 Subject: [PATCH 2/2] fix --- test/lit/passes/optimize-instructions-mvp.wast | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/lit/passes/optimize-instructions-mvp.wast b/test/lit/passes/optimize-instructions-mvp.wast index f0b831d060e..c744c0eb3ad 100644 --- a/test/lit/passes/optimize-instructions-mvp.wast +++ b/test/lit/passes/optimize-instructions-mvp.wast @@ -16470,10 +16470,10 @@ (drop ;; The tee's value, and the select's condition, are equal. If we just ;; look at them, we could think they are equal, and simplify this. - ;; However, the tee modifies $x, so we cannot fold the those two + ;; However, the tee modifies $x, so we cannot fold those two ;; expressions together while removing the select. (We can, though, ;; remove the select and the const, and use an and, but we do remain with - ;; two copies of the eq.) + ;; two copies of the eq, as those interact.) (select (local.tee $x (i32.eqz