Skip to content

Cranelift: extend alias analysis to eliminate dead stores#13806

Merged
fitzgen merged 4 commits into
bytecodealliance:mainfrom
fitzgen:dead-store-elimination
Jul 23, 2026
Merged

Cranelift: extend alias analysis to eliminate dead stores#13806
fitzgen merged 4 commits into
bytecodealliance:mainfrom
fitzgen:dead-store-elimination

Conversation

@fitzgen

@fitzgen fitzgen commented Jul 2, 2026

Copy link
Copy Markdown
Member

This commit adds an "observed?" bit to every alias region in the alias analysis's LastStores data. Loads from a region set the bit, as do instructions that act as memory fences, like calls. When we see a store to a region that hasn't been observed, and the store is to the same memory location as the last store to that region, then the last store is dead and can be removed. This integrates with alias analysis's existing single forwards pass that is fused with the egraph pass; it doesn't require additional passes or iterations. However, because it is a single pass and not a fixed point, updates do not cascade, and removing a dead store does not then recompute LastStores and reveal that (for example) the dead store's killer is an idempotent store which could also be removed. See the new check-unset-reset-flag.clif filetest, which roughly reflects what component-model fused adapters do with the MAY_LEAVE flag, for an example of this behavior.

Fixes #4167

@fitzgen
fitzgen requested review from a team as code owners July 2, 2026 18:52
@fitzgen
fitzgen requested review from alexcrichton and removed request for a team July 2, 2026 18:52
@fitzgen

fitzgen commented Jul 2, 2026

Copy link
Copy Markdown
Member Author

This is a speed up for our compile-time builtins benchmarks, as mentioned here:

increment-each-byte-in-buf/concurrency_support=true/compile-time-builtins-host-buf-api
                        time:   [760.93 µs 763.02 µs 765.31 µs]
                        change: [+0.7952% +1.0940% +1.4500%] (p = 0.00 < 0.05)
                        Change within noise threshold.

increment-random-byte-in-buf/concurrency_support=true/compile-time-builtins-host-buf-api
                        time:   [396.16 ns 397.44 ns 398.85 ns]
                        change: [−12.682% −10.658% −8.7674%] (p = 0.00 < 0.05)
                        Performance has improved.

increment-each-byte-in-buf/concurrency_support=false/compile-time-builtins-host-buf-api
                        time:   [296.47 µs 297.83 µs 299.17 µs]
                        change: [−26.293% −24.048% −21.729%] (p = 0.00 < 0.05)
                        Performance has improved.

increment-random-byte-in-buf/concurrency_support=false/compile-time-builtins-host-buf-api
                        time:   [99.568 ns 99.805 ns 100.05 ns]
                        change: [−25.627% −23.111% −20.554%] (p = 0.00 < 0.05)
                        Performance has improved.

On a PCA-based subset of Sightglass:

  • No statistically significant difference for execution
  • No statistically significant difference for compilation on 15/24 benchmarks
  • Statistically significant compilation slowdowns on 9/24 benchmarks, ranging from 1.0x-1.2x to 1.01x-1.08x
Sightglass results
Finished benchmarking in 00h:12m:58s

compilation :: cycles :: benchmarks/kotlin-richards/kotlin-richards.wasm

  Δ = 14913.90 ± 11273.49 (confidence = 99%)

  main.dylib is 1.01x to 1.08x faster than dse.dylib!

  [301551 351870.08 390568] dse.dylib
  [292431 336956.18 367624] main.dylib

compilation :: cycles :: benchmarks/libsodium/libsodium-sodium_utils2.wasm

  Δ = 11259.46 ± 9154.49 (confidence = 99%)

  main.dylib is 1.01x to 1.08x faster than dse.dylib!

  [241739 274766.10 304721] dse.dylib
  [227971 263506.64 305211] main.dylib

compilation :: cycles :: benchmarks/regex/benchmark.wasm

  Δ = 32952.12 ± 30840.00 (confidence = 99%)

  main.dylib is 1.00x to 1.06x faster than dse.dylib!

  [970289 1035352.48 1438627] dse.dylib
  [943980 1002400.36 1232649] main.dylib

compilation :: cycles :: benchmarks/rust-protobuf/benchmark.wasm

  Δ = 8122.94 ± 7198.78 (confidence = 99%)

  main.dylib is 1.00x to 1.06x faster than dse.dylib!

  [257869 277932.68 324798] dse.dylib
  [247861 269809.74 305491] main.dylib

compilation :: cycles :: benchmarks/spidermonkey/spidermonkey-regex.wasm

  Δ = 313441.64 ± 208711.36 (confidence = 99%)

  main.dylib is 1.01x to 1.04x faster than dse.dylib!

  [12750292 13253845.16 14541050] dse.dylib
  [12534325 12940403.52 14251931] main.dylib

compilation :: cycles :: benchmarks/rust-html-rewriter/benchmark.wasm

  Δ = 18136.88 ± 17507.14 (confidence = 99%)

  main.dylib is 1.00x to 1.04x faster than dse.dylib!

  [963333 1003994.12 1098845] dse.dylib
  [944430 985857.24 1117108] main.dylib

compilation :: cycles :: benchmarks/shootout/shootout-switch.wasm

  Δ = 20047.00 ± 14264.82 (confidence = 99%)

  main.dylib is 1.00x to 1.02x faster than dse.dylib!

  [1520847 1563117.68 1632339] dse.dylib
  [1499991 1543070.68 1599627] main.dylib

compilation :: cycles :: benchmarks/tract-onnx-image-classification/benchmark.wasm

  Δ = 262510.32 ± 164289.72 (confidence = 99%)

  main.dylib is 1.00x to 1.02x faster than dse.dylib!

  [21804152 22232734.92 23028528] dse.dylib
  [21495450 21970224.60 23077832] main.dylib

compilation :: cycles :: benchmarks/hashset/benchmark.wasm

  Δ = 25563.28 ± 18660.26 (confidence = 99%)

  main.dylib is 1.00x to 1.02x faster than dse.dylib!

  [2222646 2281357.80 2374406] dse.dylib
  [2169797 2255794.52 2343737] main.dylib

instantiation :: cycles :: benchmarks/libsodium/libsodium-onetimeauth7.wasm

  No difference in performance.

  [1269 1881.36 5494] dse.dylib
  [1422 1760.06 2264] main.dylib

instantiation :: cycles :: benchmarks/hex-simd/benchmark.wasm

  No difference in performance.

  [1313 1820.08 2677] dse.dylib
  [1472 1917.64 2969] main.dylib

instantiation :: cycles :: benchmarks/rust-html-rewriter/benchmark.wasm

  No difference in performance.

  [2036 2465.42 3994] dse.dylib
  [1909 2585.84 6553] main.dylib

instantiation :: cycles :: benchmarks/libsodium/libsodium-sodium_utils2.wasm

  No difference in performance.

  [1373 1861.58 2697] dse.dylib
  [1456 1777.52 2183] main.dylib

instantiation :: cycles :: benchmarks/cm-online-stats/cm-online-stats.wasm

  No difference in performance.

  [2228 2854.96 4322] dse.dylib
  [2117 2734.44 3505] main.dylib

instantiation :: cycles :: benchmarks/splay/splay.wasm

  No difference in performance.

  [1489 1986.74 2954] dse.dylib
  [1523 1904.84 3232] main.dylib

compilation :: cycles :: benchmarks/tinygo/tinygo-json.wasm

  No difference in performance.

  [1453970 1658897.36 1910534] dse.dylib
  [1343060 1591412.80 1877167] main.dylib

instantiation :: cycles :: benchmarks/spidermonkey/spidermonkey-regex.wasm

  No difference in performance.

  [2817 3216.34 4192] dse.dylib
  [2817 3348.50 5011] main.dylib

instantiation :: cycles :: benchmarks/meshoptimizer/benchmark.wasm

  No difference in performance.

  [1446 1788.92 2349] dse.dylib
  [1531 1846.82 2265] main.dylib

compilation :: cycles :: benchmarks/cm-online-stats/cm-online-stats.wasm

  No difference in performance.

  [64689 74000.16 83595] dse.dylib
  [62536 76361.92 323740] main.dylib

compilation :: cycles :: benchmarks/quicksort/benchmark.wasm

  No difference in performance.

  [196717 223366.08 248556] dse.dylib
  [195536 216784.46 250824] main.dylib

compilation :: cycles :: benchmarks/bz2/benchmark.wasm

  No difference in performance.

  [414079 462718.52 509915] dse.dylib
  [406949 449318.56 511436] main.dylib

instantiation :: cycles :: benchmarks/rust-protobuf/benchmark.wasm

  No difference in performance.

  [1504 1965.40 2699] dse.dylib
  [1515 1913.04 2567] main.dylib

instantiation :: cycles :: benchmarks/blake3-scalar/benchmark.wasm

  No difference in performance.

  [1444 1901.52 2644] dse.dylib
  [1542 1950.04 2659] main.dylib

instantiation :: cycles :: benchmarks/shootout/shootout-switch.wasm

  No difference in performance.

  [1372 1634.40 2208] dse.dylib
  [1332 1675.20 2204] main.dylib

instantiation :: cycles :: benchmarks/shootout/shootout-random.wasm

  No difference in performance.

  [1287 1768.64 2418] dse.dylib
  [1368 1729.40 2601] main.dylib

compilation :: cycles :: benchmarks/sqlite3/sqlite3.wasm

  No difference in performance.

  [2028978 2150582.12 2426111] dse.dylib
  [1992886 2103457.22 2361924] main.dylib

compilation :: cycles :: benchmarks/splay/splay.wasm

  No difference in performance.

  [40752 50227.08 65703] dse.dylib
  [41510 49142.34 80924] main.dylib

instantiation :: cycles :: benchmarks/rust-compression/benchmark.wasm

  No difference in performance.

  [3236 3782.54 4717] dse.dylib
  [3325 3864.02 5487] main.dylib

instantiation :: cycles :: benchmarks/regex/benchmark.wasm

  No difference in performance.

  [2343 2897.28 4140] dse.dylib
  [2321 2840.90 3979] main.dylib

execution :: cycles :: benchmarks/libsodium/libsodium-onetimeauth7.wasm

  No difference in performance.

  [131143 137337.38 150095] dse.dylib
  [131473 140004.64 152778] main.dylib

instantiation :: cycles :: benchmarks/hashset/benchmark.wasm

  No difference in performance.

  [1661 1939.42 2366] dse.dylib
  [1670 1904.18 2322] main.dylib

execution :: cycles :: benchmarks/hashset/benchmark.wasm

  No difference in performance.

  [364284 383346.66 430711] dse.dylib
  [367406 390113.94 437923] main.dylib

instantiation :: cycles :: benchmarks/shootout/shootout-keccak.wasm

  No difference in performance.

  [1356 1682.50 2233] dse.dylib
  [1375 1654.66 2407] main.dylib

execution :: cycles :: benchmarks/quicksort/benchmark.wasm

  No difference in performance.

  [216322 237900.00 258167] dse.dylib
  [214027 234091.34 262504] main.dylib

execution :: cycles :: benchmarks/meshoptimizer/benchmark.wasm

  No difference in performance.

  [75675 79689.82 85614] dse.dylib
  [75515 80746.90 86866] main.dylib

compilation :: cycles :: benchmarks/hex-simd/benchmark.wasm

  No difference in performance.

  [193538 214353.42 266493] dse.dylib
  [188717 211563.14 258833] main.dylib

compilation :: cycles :: benchmarks/shootout/shootout-minicsv.wasm

  No difference in performance.

  [40587 46959.44 55809] dse.dylib
  [41479 46364.46 67529] main.dylib

compilation :: cycles :: benchmarks/shootout/shootout-keccak.wasm

  No difference in performance.

  [387820 421629.22 474935] dse.dylib
  [390590 426937.38 464010] main.dylib

instantiation :: cycles :: benchmarks/libsodium/libsodium-scalarmult_ed25519.wasm

  No difference in performance.

  [1521 2032.48 3011] dse.dylib
  [1483 2008.26 2869] main.dylib

execution :: cycles :: benchmarks/shootout/shootout-keccak.wasm

  No difference in performance.

  [68304 72161.40 76890] dse.dylib
  [69138 72965.48 78470] main.dylib

compilation :: cycles :: benchmarks/rust-compression/benchmark.wasm

  No difference in performance.

  [1142122 1741647.68 2082353] dse.dylib
  [1142814 1760990.44 2013696] main.dylib

instantiation :: cycles :: benchmarks/kotlin-richards/kotlin-richards.wasm

  No difference in performance.

  [3420 4276.56 5723] dse.dylib
  [3516 4322.76 7282] main.dylib

instantiation :: cycles :: benchmarks/tract-onnx-image-classification/benchmark.wasm

  No difference in performance.

  [34874 42264.54 57053] dse.dylib
  [34251 42704.50 63048] main.dylib

execution :: cycles :: benchmarks/rust-compression/benchmark.wasm

  No difference in performance.

  [158340 166201.44 191822] dse.dylib
  [158476 167888.06 177944] main.dylib

instantiation :: cycles :: benchmarks/sqlite3/sqlite3.wasm

  No difference in performance.

  [2010 2319.86 2837] dse.dylib
  [1904 2342.14 3099] main.dylib

compilation :: cycles :: benchmarks/blake3-scalar/benchmark.wasm

  No difference in performance.

  [190639 230120.58 283396] dse.dylib
  [199892 227969.50 303630] main.dylib

execution :: cycles :: benchmarks/kotlin-richards/kotlin-richards.wasm

  No difference in performance.

  [444643 463732.58 482387] dse.dylib
  [449079 468074.72 491842] main.dylib

instantiation :: cycles :: benchmarks/tinygo/tinygo-json.wasm

  No difference in performance.

  [1653 2113.88 3052] dse.dylib
  [1793 2096.16 3172] main.dylib

execution :: cycles :: benchmarks/splay/splay.wasm

  No difference in performance.

  [44587672 45155858.02 47174850] dse.dylib
  [44761966 45434209.28 47298269] main.dylib

compilation :: cycles :: benchmarks/shootout/shootout-random.wasm

  No difference in performance.

  [189936 208566.76 234468] dse.dylib
  [184041 207320.00 234296] main.dylib

execution :: cycles :: benchmarks/libsodium/libsodium-scalarmult_ed25519.wasm

  No difference in performance.

  [63755 68012.82 71741] dse.dylib
  [63893 68418.42 76321] main.dylib

compilation :: cycles :: benchmarks/libsodium/libsodium-onetimeauth7.wasm

  No difference in performance.

  [253475 286339.86 369066] dse.dylib
  [249413 284748.24 320236] main.dylib

execution :: cycles :: benchmarks/shootout/shootout-switch.wasm

  No difference in performance.

  [815544 838446.92 863197] dse.dylib
  [812779 842966.12 874425] main.dylib

instantiation :: cycles :: benchmarks/shootout/shootout-minicsv.wasm

  No difference in performance.

  [1291 1664.46 2279] dse.dylib
  [1336 1656.94 2295] main.dylib

instantiation :: cycles :: benchmarks/bz2/benchmark.wasm

  No difference in performance.

  [1532 1879.10 2385] dse.dylib
  [1454 1870.90 2519] main.dylib

execution :: cycles :: benchmarks/rust-protobuf/benchmark.wasm

  No difference in performance.

  [201520 213823.90 231508] dse.dylib
  [201450 212934.06 243603] main.dylib

execution :: cycles :: benchmarks/cm-online-stats/cm-online-stats.wasm

  No difference in performance.

  [10449954 10705319.54 11151990] dse.dylib
  [10514805 10748090.08 11208289] main.dylib

execution :: cycles :: benchmarks/hex-simd/benchmark.wasm

  No difference in performance.

  [65938 70924.48 98382] dse.dylib
  [64564 70653.40 89504] main.dylib

instantiation :: cycles :: benchmarks/quicksort/benchmark.wasm

  No difference in performance.

  [1474 1800.20 2507] dse.dylib
  [1441 1793.84 2453] main.dylib

execution :: cycles :: benchmarks/bz2/benchmark.wasm

  No difference in performance.

  [110230 117605.04 125873] dse.dylib
  [113169 117967.76 142068] main.dylib

execution :: cycles :: benchmarks/shootout/shootout-minicsv.wasm

  No difference in performance.

  [7148613 7333797.64 7632231] dse.dylib
  [7119934 7354849.84 7645026] main.dylib

execution :: cycles :: benchmarks/regex/benchmark.wasm

  No difference in performance.

  [95889 98818.38 108010] dse.dylib
  [95064 98550.76 102502] main.dylib

execution :: cycles :: benchmarks/blake3-scalar/benchmark.wasm

  No difference in performance.

  [92550 98629.78 108287] dse.dylib
  [92312 98363.70 106838] main.dylib

execution :: cycles :: benchmarks/tract-onnx-image-classification/benchmark.wasm

  No difference in performance.

  [3854443 3930013.78 4086243] dse.dylib
  [3873545 3938115.28 4107499] main.dylib

execution :: cycles :: benchmarks/shootout/shootout-random.wasm

  No difference in performance.

  [3344464 3435485.56 3570093] dse.dylib
  [3324341 3428700.84 3604256] main.dylib

execution :: cycles :: benchmarks/rust-html-rewriter/benchmark.wasm

  No difference in performance.

  [146798 151977.58 157691] dse.dylib
  [147479 151692.08 156738] main.dylib

execution :: cycles :: benchmarks/sqlite3/sqlite3.wasm

  No difference in performance.

  [14218916 14415792.74 15383184] dse.dylib
  [14197184 14434728.14 15175099] main.dylib

compilation :: cycles :: benchmarks/meshoptimizer/benchmark.wasm

  No difference in performance.

  [367176 400341.12 437304] dse.dylib
  [362980 399916.12 432478] main.dylib

compilation :: cycles :: benchmarks/libsodium/libsodium-scalarmult_ed25519.wasm

  No difference in performance.

  [340288 380227.20 526483] dse.dylib
  [339950 380512.48 518440] main.dylib

execution :: cycles :: benchmarks/spidermonkey/spidermonkey-regex.wasm

  No difference in performance.

  [66098 68167.70 71128] dse.dylib
  [66336 68190.04 71532] main.dylib

execution :: cycles :: benchmarks/tinygo/tinygo-json.wasm

  No difference in performance.

  [119820 125794.90 129823] dse.dylib
  [122013 125824.44 136271] main.dylib

I can look into speeding things up a bit more, but we should probably discuss this at the next Cranelift meeting.

@alexcrichton
alexcrichton requested review from cfallin and removed request for a team and alexcrichton July 2, 2026 19:31
@alexcrichton

Copy link
Copy Markdown
Member

I'm going to move this to being reviewed by @cfallin who I know is on PTO right now but I think will have sufficiently more context about this that he'll do a much better job than I

@cfallin

cfallin commented Jul 2, 2026

Copy link
Copy Markdown
Member

(happy to do so; logistical note that I'm out on PTO this week and next, then traveling-for-work the following week, so I can't guarantee I'll get to this before Mon Jul 20)

@github-actions github-actions Bot added the cranelift Issues related to the Cranelift code generator label Jul 2, 2026

@cfallin cfallin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks correct to me -- thanks for the really careful work here!

A few points below but nothing fundamental.

Comment thread cranelift/codegen/src/alias_analysis.rs
Comment thread cranelift/codegen/src/egraph/mod.rs
Comment thread cranelift/codegen/src/egraph/mod.rs Outdated
Comment thread cranelift/codegen/src/egraph/mod.rs Outdated
Comment thread cranelift/codegen/src/alias_analysis.rs Outdated
@fitzgen

fitzgen commented Jul 20, 2026

Copy link
Copy Markdown
Member Author

Regarding the compilation hit that this PR implies: I have a handful of optimizations that I will start making PRs for that recover that compile time in other areas.

@cfallin

cfallin commented Jul 20, 2026

Copy link
Copy Markdown
Member

One point to track as a followup further improvement, possibly: if we have a mode (enforced by Wasmtime API) that ensures that the Store no longer exposes state after a trap occurs (i.e., disallows reads from memories/tables/globals), we could then set a bit on an alias-region definition stating trap_unobservable, and then we could avoid "observing" (and making live) any trap-unobservable stores. This might be a nice performance improvement wherever one runs a Wasm component in the (frankly most common) case that the driver throws away the store upon trap...

fitzgen added 3 commits July 21, 2026 13:36
This commit adds an "observed?" bit to every alias region in the alias
analysis's `LastStores` data. Loads from a region set the bit, as do
instructions that act as memory fences, like calls. When we see a store to a
region that hasn't been observed, and the store is to the same memory location
as the last store to that region, then the last store is dead and can be
removed. This integrates with alias analysis's existing single forwards pass
that is fused with the egraph pass; it doesn't require additional passes or
iterations. However, because it is a single pass and not a fixed point, updates
do not cascade, and removing a dead store does not then recompute `LastStores`
and reveal that (for example) the dead store's killer is an idempotent store
which could also be removed. See the new `check-unset-reset-flag.clif` filetest,
which roughly reflects what component-model fused adapters do with the
`MAY_LEAVE` flag, for an example of this behavior.

Fixes bytecodealliance#4167
@fitzgen
fitzgen force-pushed the dead-store-elimination branch from e8f2a1f to 0448d1c Compare July 21, 2026 21:20
@fitzgen

fitzgen commented Jul 22, 2026

Copy link
Copy Markdown
Member Author

We talked about this a bit at today's Cranelift meeting and the conclusion was to go ahead and merge; I'll also have a couple more perf follow ups soon.

@fitzgen
fitzgen added this pull request to the merge queue Jul 22, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jul 22, 2026
@fitzgen
fitzgen added this pull request to the merge queue Jul 23, 2026
Merged via the queue into bytecodealliance:main with commit 46c4e84 Jul 23, 2026
53 checks passed
@fitzgen
fitzgen deleted the dead-store-elimination branch July 23, 2026 00:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cranelift Issues related to the Cranelift code generator

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cranelift: eliminate dead stores

3 participants