From 49ee0d3b26662aa8e266c3e7a5506d8039ba9d09 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Fri, 27 Feb 2026 12:59:36 -0800 Subject: [PATCH] Change effect for `pause` The `pause` instruction doesn't have observable side effects, but for it to fulfill its purpose of making spinlocks more efficient, we need to avoid moving it out of loops. We previously did this by setting the `isAtomic` effect, but that effect will soon go away in favor of a more detailed analysis of how expressions may synchronize across threads. Switch to using `branchesOut` as the effect to keep `pause` from being moved instead and add a test demonstrating that it works as intended. --- src/ir/effects.h | 7 ++++--- test/lit/passes/licm.wast | 35 ++++++++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/ir/effects.h b/src/ir/effects.h index 5ad1c4b955f..15786aab89e 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h @@ -644,9 +644,10 @@ class EffectAnalyzer { parent.isAtomic = true; } void visitPause(Pause* curr) { - // It's not much of a problem if pause gets reordered with anything, but - // we don't want it to be removed entirely. - parent.isAtomic = true; + // We don't want this to be moved out of loops, but it doesn't otherwises + // matter much how it gets reordered. Say we transfer control as a coarse + // approximation of this. + parent.branchesOut = true; } void visitSIMDExtract(SIMDExtract* curr) {} void visitSIMDReplace(SIMDReplace* curr) {} diff --git a/test/lit/passes/licm.wast b/test/lit/passes/licm.wast index f13cf07c1d9..0d82ad4a943 100644 --- a/test/lit/passes/licm.wast +++ b/test/lit/passes/licm.wast @@ -1,6 +1,6 @@ ;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited. -;; RUN: foreach %s %t wasm-opt --licm -S -o - | filecheck %s +;; RUN: foreach %s %t wasm-opt -all --licm -S -o - | filecheck %s (module (memory 10 20) @@ -11,7 +11,7 @@ ;; CHECK: (memory $0 10 20) - ;; CHECK: (func $unreachable-get + ;; CHECK: (func $unreachable-get (type $1) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (drop ;; CHECK-NEXT: (local.get $x) @@ -35,7 +35,7 @@ ) ) - ;; CHECK: (func $unreachable-get-call (param $p i32) + ;; CHECK: (func $unreachable-get-call (type $0) (param $p i32) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (loop $loop ;; CHECK-NEXT: (unreachable) @@ -56,7 +56,7 @@ ) ) - ;; CHECK: (func $unreachable-get-store (param $p i32) + ;; CHECK: (func $unreachable-get-store (type $0) (param $p i32) ;; CHECK-NEXT: (local $x i32) ;; CHECK-NEXT: (loop $loop ;; CHECK-NEXT: (unreachable) @@ -78,5 +78,30 @@ ) ) ) -) + ;; CHECK: (func $pause (type $0) (param $p i32) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 0) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (loop $loop + ;; CHECK-NEXT: (nop) + ;; CHECK-NEXT: (pause) + ;; CHECK-NEXT: (drop + ;; CHECK-NEXT: (i32.const 1) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: (br $loop) + ;; CHECK-NEXT: ) + ;; CHECK-NEXT: ) + (func $pause (param $p i32) + ;; We don't want pause to be moved out of loops. In principle we should be + ;; able to move side-effect-free expressions back before the pause, but we + ;; currently do not. Pause is rare and specialized enough that this shouldn't + ;; be a problem. + (loop $loop + (drop (i32.const 0)) + (pause) + (drop (i32.const 1)) + (br $loop) + ) + ) +)