1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
// SPDX-License-Identifier: Apache-2.0
// Copyright 2026 Craton Software Company
//! Cranelift IR → Pliron `dialect-mir` lowering — **v0.3.1 scaffold only**.
//!
//! This module is the empty-stub landing site for the Pliron-based
//! auto-offload pipeline tracked in [RFC
//! 0001](../../../../rfcs/0001-cuda-oxide-integration.md) ("Pliron lever and
//! the auto-offload pipeline" / "Future possibilities"). It is gated behind
//! the opt-in `cuda-oxide-backend` feature because the parity port has not
//! landed yet; the workspace toolchain pin (`nightly-2026-04-03`) already
//! matches cuda-oxide's own pin, so enabling the feature on the default
//! workspace toolchain builds.
//!
//! # What this module IS
//!
//! - The final trait signature ([`WasmToPliron`]) that the v0.4+ port will
//! implement. As of wave 1 of the Pliron pipeline the trait is keyed on a
//! real [`cranelift_codegen::ir::Function`] input and a real
//! [`crate::lowered_ir::LoweredFunction`] output — the
//! [`&str`]→[`String`] placeholder shape that landed in v0.3.1 is gone.
//! The interim [`crate::lowered_ir::LoweredFunction`] IR is the stable
//! contract under our control; wave 3 will graft a
//! `LoweredFunction → pliron::Operation` converter onto the same trait
//! without disturbing call sites. Code that wants to write
//! backend-agnostic lowering today can target this trait and the v0.4
//! diff is a body swap, not a refactor.
//! - A [`StubLowerer`] that implements the trait, always returning
//! [`PlironLoweringError::NotYetImplemented`] with an actionable message
//! pointing to RFC 0001. Retained as a sentinel implementor for tests
//! that want a known-failing impl of the trait.
//! - A real [`CraneliftLowerer`] (wave 2.5) that implements the trait by
//! delegating to the wave-2 module-level lowering driver
//! ([`crate::lowering_driver::lower_function`]). This is the
//! production-path implementor: it returns an actual
//! [`crate::lowered_ir::LoweredFunction`] for any Cranelift IR function
//! the wave-1 per-family lowerings cover, and maps driver errors through
//! the existing [`From<crate::lowering_errors::LoweringError>`] impl
//! into [`PlironLoweringError::UnsupportedOp`]. Wave 3 will extend the
//! impl to also produce a `pliron::Operation` once the Pliron dependency
//! is added.
//! - A convenience entry point [`cranelift_to_dialect_mir`] that wraps
//! [`CraneliftLowerer`], mirroring the shape of the eventual public free
//! function so the detector → lowering call site is stable across the
//! port.
//! - The [Cranelift IR ⇄ dialect-mir mapping table](#mapping-table) below.
//! This is the most load-bearing artifact in the file: the v0.4 author
//! should be able to walk this table top-to-bottom and produce the real
//! lowering without re-deriving which Cranelift opcodes need device-side
//! semantics and which are PTX-illegal.
//!
//! # What this module is NOT (yet)
//!
//! - The real lowering implementation. That is the v0.4+ research
//! investment per RFC 0001 step 4. The cuda-oxide compiler pipeline is
//! `Rust → rustc_public (Stable MIR) → dialect-mir → mem2reg →
//! dialect-llvm → LLVM IR → PTX`; this module would graft a fourth
//! front-end (`Wasm → Cranelift IR → dialect-mir`) onto the same
//! pipeline, so we get arbitrary pure-compute auto-offload coverage
//! instead of the three hand-written blueprints today's
//! [`crate::detector`] recognises (matmul, vector_add, conv2d 3x3).
//! - A binding to the Pliron crate. As of W3.1 (2026-05-27), pliron is
//! published on crates.io at 0.15.0 (the original RFC 0001 "Drawbacks"
//! claim that Pliron was git-only is stale and has been amended) and
//! `tensor-wasm-jit` depends on it directly behind the
//! `cuda-oxide-backend` feature. The Pliron `Operation` / `Module`
//! bindings themselves are still introduced in wave 3; this module
//! keeps the scaffold/interim-IR shape so the wave-1 lowering families
//! stay testable without taking the cuda-oxide nightly toolchain
//! override.
//! - A `dialect-llvm` or `mem2reg` pass binding. Those live further down
//! the cuda-oxide pipeline; this module's contract ends at `dialect-mir`.
//!
//! # Why it exists today
//!
//! So the v0.4 author has (a) a concrete target signature to plug into,
//! (b) a documented mapping table that captures the load-bearing op-by-op
//! decisions, and (c) a unit-test harness asserting trait-object
//! Send+Sync — instead of staring at a blank file and re-deriving the
//! design from RFC 0001 + the cuda-oxide architecture overview.
//!
//! # References
//!
//! - [RFC 0001 — cuda-oxide as the v0.5 cust successor](../../../../rfcs/0001-cuda-oxide-integration.md)
//! - cuda-oxide architecture overview: <https://nvlabs.github.io/cuda-oxide/>
//! - Pliron upstream: <https://github.com/vaivaswatha/pliron>
//!
//! # Mapping table
//!
//! Cranelift opcodes the v0.4 author needs to handle, paired with their
//! Pliron `dialect-mir` equivalents. The table is **load-bearing**: this is
//! the design decision that should not be re-derived in the v0.4 PR.
//!
//! | Cranelift op | Pliron `dialect-mir` op | Notes |
//! |------------------------|-------------------------------|-------|
//! | `iadd` | `arith.addi` | Integer add. Width carried in the result type. |
//! | `isub` | `arith.subi` | Integer subtract. |
//! | `imul` | `arith.muli` | Integer multiply. Overflow semantics inherit from Cranelift (wrap on signed/unsigned). |
//! | `idiv` / `udiv` | `arith.divsi` / `arith.divui` | Signed vs unsigned chosen by Cranelift opcode discriminant. PTX `div` is well-defined on Ampere+. |
//! | `fadd` | `arith.addf` | IEEE-754 float add. |
//! | `fsub` | `arith.subf` | Float subtract. |
//! | `fmul` | `arith.mulf` | Float multiply. |
//! | `fdiv` | `arith.divf` | Float divide. PTX `div.rn.f32` is the default rounding mode. |
//! | `fma` | `arith.fma` | Fused multiply-add. Maps directly to PTX `fma.rn`. Mandatory: emitting `mul`+`add` instead loses the FMA-rounding contract Cranelift guarantees. |
//! | `load` | `memref.load` | **Device-pointer translation** required: the Cranelift `load` operates on a guest linear-memory offset; the dialect-mir `memref.load` operates on a device pointer. The v0.4 port must thread the W1.1 kernel-args base-pointer through here. |
//! | `store` | `memref.store` | Same device-pointer translation as `load`. |
//! | `stack_load` | `memref.load` + SSA local | Cranelift stack slots become SSA-local `memref` allocas in dialect-mir. PTX has no stack — the lowering must promote to registers via the downstream `mem2reg` pass. |
//! | `stack_store` | `memref.store` + SSA local | Symmetric with `stack_load`. |
//! | `select` | `arith.select` | Three-operand select. Maps to PTX `selp`. |
//! | `br_table` | `cf.switch` | Multi-way branch. PTX has no native switch — dialect-llvm lowers this to a sequence of `setp` + `bra`. |
//! | `jump` | `cf.br` | Unconditional branch. |
//! | `brif` / `brz` / `brnz`| `cf.cond_br` | Conditional branch with two successors. |
//! | `call` | `func.call` | **Device-function calls only.** Calls into other dialect-mir-lowered kernels are legal; calls into host functions (anything reaching back into the Wasmtime runtime) are **forbidden** in PTX — the detector must reject candidates that contain host calls before reaching this lowering. |
//! | `call_indirect` | `func.call_indirect` | Same host-call prohibition. PTX function pointers exist (sm_70+) but are expensive; the v0.4 port should consider devirtualising before lowering. |
//! | `bitcast` | `arith.bitcast` | Reinterpret bits. Free in PTX — produces no instruction. |
//! | `breduce` | `arith.trunci` | Width-reducing integer truncation. Maps to PTX `cvt.<small>.<large>`. |
//! | `bextend` / `sextend` | `arith.extui` / `arith.extsi` | Width-extending. Sign-vs-zero extend chosen by Cranelift opcode. |
//! | `vmin` | `vector.minimum` | SIMD per-lane min. Maps to per-warp-lane PTX (one lane per thread within a 32-thread warp). |
//! | `vmax` | `vector.maximum` | SIMD per-lane max. Same warp-lane mapping. |
//! | `vsplat` | `vector.splat` | Broadcast scalar to all lanes. PTX has no native splat — the lowering writes the scalar to every lane via the warp-shuffle intrinsic. |
//! | `vselect` | `vector.select` | Per-lane mask select. Maps to PTX `selp.b32` per lane. |
//! | `vall_true` / `vany_true` | `vector.reduce_and/or` | Warp-wide reduction. Maps to PTX `vote.all` / `vote.any`. |
//!
//! ## Unsupported in v0.4 (deferred or hard-rejected)
//!
//! These Cranelift opcodes have no dialect-mir lowering planned; the
//! detector must filter candidates containing them before invoking this
//! pipeline. Documented here so the v0.4 author does not waste cycles
//! mapping them:
//!
//! - **Atomics** (`atomic_load`, `atomic_store`, `atomic_rmw`,
//! `atomic_cas`). PTX has atomics, but Wasm threads + GPU atomics is a
//! memory-model alignment problem larger than the v0.4 scope. Deferred
//! to a future RFC.
//! - **Floating-point exception bits** (`fcvt_to_sint_sat` traps,
//! `f32const NaN` propagation guarantees beyond IEEE-754 default). PTX
//! default rounding + flush-to-zero diverges from Wasm-strict FP. The
//! v0.4 detector should reject candidates that depend on strict-FP.
//! - **`table.get` / `table.set`** (Wasm reference types). Tables live
//! host-side; lowering them would require device-resident table mirrors,
//! which is out of scope.
//! - **`ref.func` / GC ops** (Wasm GC proposal). No device-side
//! representation; hard-rejected.
//! - **`memory.grow` / `memory.size`**. Linear-memory resizing requires a
//! host round-trip; the kernel must run with a fixed memory snapshot.
//! - **`memory.copy` / `memory.fill` >4 KiB**. PTX has `cp.async.bulk`
//! (sm_90+) but the v0.4 port targets sm_80 baseline (per
//! [`ptx_emit::DEFAULT_TARGET`](crate::ptx_emit::DEFAULT_TARGET)). Small
//! copies inline; large copies fall back to a host bounce.
use Function;
use Error;
use crateLoweredFunction;
/// Errors produced by the (still-scaffolded) Cranelift → Pliron
/// `dialect-mir` lowering.
///
/// Every variant carries enough context for the v0.4 author to grep for
/// the call site and replace the stub with the real lowering. The
/// `NotYetImplemented` variant is the one every scaffold call site returns
/// today; `UnsupportedOp` and `DialectVersionMismatch` are pre-declared so
/// the public error surface is stable across the v0.4 port.
// Manual Display for DialectVersionMismatch so the error message stays on
// one line in tracing output — `thiserror`'s `#[error("...")]` attribute
// supports field interpolation, but spelling the format out keeps the
// struct-field rustdoc clean.
/// Lowering entry point trait.
///
/// Implementors translate a Cranelift IR function into the wave 1 interim
/// IR ([`LoweredFunction`]) suitable for handing off to cuda-oxide's
/// downstream `mem2reg` → `dialect-llvm` → LLVM IR → PTX pipeline. Wave 3
/// of the Pliron pipeline will graft a `LoweredFunction → pliron::Operation`
/// converter on top of this trait without disturbing the call sites; see
/// RFC 0001 step 4 and the [`crate::lowered_ir`] module docs for the
/// rationale for routing through an interim IR rather than coding directly
/// against the alpha (but now crates.io-published, since W3.1 / 2026-05-27)
/// Pliron `Operation` / `Module` types.
///
/// The wave-1 signature is final on the input side — it now takes a real
/// [`cranelift_codegen::ir::Function`] reference rather than the v0.3.1
/// `&str` placeholder — and final on the output side modulo the interim
/// IR. The v0.4 port replaces the trait body, not its shape.
///
/// # Send + Sync
///
/// Implementations must be `Send + Sync` so they can flow through the
/// Tokio-backed dispatch path
/// ([`crate::deopt::DeoptGuard`]-adjacent call sites and the eventual
/// `cuda-async`-based future-sync replacement RFC 0001 mentions under
/// "Future possibilities"). The trait itself does not enforce the bound
/// — Rust adds it implicitly for `dyn WasmToPliron + Send + Sync` — but
/// the unit tests in this module include a compile-time assertion that
/// [`StubLowerer`] satisfies it.
/// Zero-state stub implementor of [`WasmToPliron`].
///
/// **Scaffold only.** Always returns
/// [`PlironLoweringError::NotYetImplemented`] with the sentinel
/// `"StubLowerer::lower"` tag. The v0.4 port will either replace this
/// type with the real lowering or keep it for testing as a known-failing
/// implementation.
;
/// Real [`WasmToPliron`] impl that delegates to the wave-2 lowering driver.
///
/// Replaces the [`StubLowerer`] sentinel for callers that want actual
/// [`LoweredFunction`] output. The trait surface returns
/// `Result<LoweredFunction, PlironLoweringError>`, so this impl maps the
/// driver's [`crate::lowering_errors::LoweringError`] through the existing
/// `From` impl into [`PlironLoweringError::UnsupportedOp`].
;
/// Convenience entry point: lower a Cranelift IR function to a
/// [`LoweredFunction`] via the default [`CraneliftLowerer`].
///
/// Wave 2.5: this function now delegates to the real driver; wave 3 will
/// extend it to also produce a `pliron::Operation` once the Pliron dep
/// is added.