A // comment is supposed to be invisible to the compiler. On CUDA 12.9+, one typographic character inside a generated PTX comment is enough to fail the entire kernel load with an opaque CUDA_ERROR_INVALID_PTX — and it had been silently fine on CUDA 11.x for years.
Today we released OxiCUDA 0.5.2 — a portability release that fixes a class of PTX bugs CUDA 11.x tolerated silently and CUDA 12.9+ toolchains reject outright, closes several Windows-specific test and lock-contention bugs, and fixes a numerical-correctness bug in the tensor-network DMRG excited-states solver.
No CUDA SDK. No nvcc. No C/C++ toolchain. OxiCUDA is a type-safe, memory-safe, pure-Rust replacement for the entire NVIDIA CUDA Toolkit software stack — cuBLAS, cuDNN, cuFFT, cuSPARSE, cuSOLVER, cuRAND, and more, across 74 crates and ~1.30M lines of safe Rust. libcuda.so/nvcuda.dll is loaded dynamically at runtime via libloading; nothing about building or running OxiCUDA requires the CUDA SDK, headers, or a C/C++ compiler. The result compiles to a single static binary and runs everywhere the driver is present.
Why OxiCUDA 0.5.2 is a game changer
Portability bugs are the worst kind precisely because they don’t show up where you’re looking:
- A kernel that JIT-compiles cleanly on your CUDA 11.x dev box can fail to load on a teammate’s or CI’s CUDA 12.9+ toolchain with an error that names no file, line, or character
//comments carry zero semantic meaning toptxas— until a stray em dash, arrow, or math symbol lands inside one and the assembler rejects the whole module anyway- Test and runtime code written and only ever run on Linux/macOS tends to quietly assume
/dev/nullexists,ptxasis the binary’s exact name, and a deleted lock file is actually gone — all false on Windows - A physics solver can converge, print a plausible-looking energy, and still be silently wrong, if the invariant it optimizes against doesn’t actually hold the way the code assumes
OxiCUDA 0.5.2 ends all of that.
- PTX gets scrubbed of non-ASCII bytes before it ever reaches the JIT.
Module::from_ptx/from_ptx_with_optionsnow strip any non-ASCII byte from the PTX source via a newascii_only()helper — byte-for-byte substitution, soptxasdiagnostics still point at the correct line and column if something else is wrong. - The bug is fixed at its source, too. Every PTX-comment-emitting template across
oxicuda-ptx(templates::gemm),oxicuda-fft(callbacks),oxicuda-launch(dynamic_parallelism), andoxicuda-signal(dct::dct2,dct::dct3,dwt::haar,filter::fir) now emits ASCII only — em dashes,×,→,π,√,Σ, and box-drawing rules are replaced with-,x,->,pi,sqrt,sum, and----. - The DMRG excited-states penalty now rotates into a shared basis before comparing states.
oxicuda-tn‘soptimise_with_penaltypreviously applied a prior state’s raw two-site tensor when computing theω·Σᵢ|Θᵢ⟩⟨Θᵢ|penalty — only valid if both states shared a bond basis, which they don’t, since each MPS is canonicalized independently by its own SVDs. Newoverlap_env_left/overlap_env_right/prior_two_site_projectorhelpers contract the overlap transfer matrices between the two states’ blocks first, so the penalty now pushes against the right direction instead of an arbitrary one. - Lock contention on Windows is retried instead of misreported.
FileLockGuard‘s retry loop now treats Windows’ErrorKind::PermissionDeniedas contention, not justErrorKind::AlreadyExists— a deleted-but-still-open lock file stays “delete pending” on Windows until the last handle closes, which previously surfaced as a fatalInvalidValueinstead of ordinary, retryable contention. ptxas-invoking tests actually run on Windows now. Acrossoxicuda-blas,oxicuda-dnn,oxicuda-ptx,oxicuda-solver,oxicuda-sparse, andoxicuda-signal, tests now probeptxas.exe, assemble to a throwaway.cubinfile instead of the non-existent-on-Windows/dev/null, and surfaceptxas’s stdout — where its real diagnostics live — in failure messages.
Technical Deep Dive
- Foundation —
oxicuda-driver,oxicuda-memory.Module::from_ptx’s newascii_only()scrubber andFileLockGuard’s Windows contention fix both live in this layer, underneath everything else that loads a module or takes a lock. - PTX codegen —
oxicuda-ptx,oxicuda-fft,oxicuda-launch,oxicuda-signal. Every generator that emitted a typographic Unicode character in a kernel comment is fixed at the source, so the non-ASCII rejection is closed both defensively (the driver scrubber) and at generation time (the templates themselves). - Tensor networks —
oxicuda-tn. The DMRG excited-states basis-rotation fix lives here, alongside the existing MPS/MPO/TEBD/PEPS machinery from Vol.50. - Test infrastructure across the workspace.
oxicuda-blas,oxicuda-dnn,oxicuda-solver, andoxicuda-sparseall picked up the sameptxas.exe/throwaway-file/stdout-capture fix, so Windows CI runs get the same signal Linux/macOS runs already had.
Getting Started
cargo add oxicuda --features blas
Default features remain driver, memory, and launch; every subsystem — ptx, autotune, blas, dnn, fft, sparse, solver, rand, nvrtc, primitives, vulkan, metal, webgpu, rocm, level-zero, and full for everything — is its own opt-in flag.
A complete GEMM, end to end — unchanged by this release, and now assembling cleanly on CUDA 12.9+ regardless of what characters ended up in the generated comments:
use oxicuda::prelude::*;
fn main() -> Result<(), oxicuda::Error> {
// Initialize driver and select GPU device
let device = Device::get(0)?;
let ctx = Context::new(device)?;
let stream = Stream::new(&ctx)?;
// Allocate device memory
let mut d_a = DeviceBuffer::<f32>::zeroed(1024)?;
let mut d_b = DeviceBuffer::<f32>::zeroed(1024)?;
let mut d_c = DeviceBuffer::<f32>::zeroed(1024)?;
// Copy host data to device
d_a.copy_from_host(&host_a)?;
d_b.copy_from_host(&host_b)?;
// Launch a GEMM: C = alpha * A @ B + beta * C
let handle = BlasHandle::new(&stream)?;
handle.gemm(
Transpose::None, Transpose::None,
m, n, k,
1.0f32, // alpha
&d_a, lda,
&d_b, ldb,
0.0f32, // beta
&mut d_c, ldc,
)?;
stream.synchronize()?;
// Copy result back to host
let mut result = vec![0.0f32; m * n];
d_c.copy_to_host(&mut result)?;
Ok(())
}
On a machine with an NVIDIA GPU, run the same validation this release’s fixes were checked against:
cargo test --features gpu-tests
What’s New in 0.5.2
- Non-ASCII bytes are scrubbed from PTX before it reaches the JIT. A new
ascii_only()helper inModule::from_ptx/from_ptx_with_optionsstrips any non-ASCII byte, byte-for-byte, so JIT diagnostics still point at the right line and column. - Generated PTX comments are ASCII-only at the source.
oxicuda-ptx,oxicuda-fft,oxicuda-launch, andoxicuda-signalno longer emit em dashes,×,→,π,√,Σ, or box-drawing rules in kernel comments. - DMRG excited-states penalty fixed.
oxicuda-tn’soptimise_with_penaltynow rotates each prior state into the current state’s block basis before applying the penalty, instead of comparing raw tensors across incompatible bond bases. - Windows lock contention retried correctly.
FileLockGuardnow treatsErrorKind::PermissionDeniedas retryable contention, matching how Windows actually handles a “delete pending” lock file. managed_hintstest respects device capability. TheMigrationPolicy::PreferDevice(0)test now checkssupports_concurrent_managed_access()before assertingcuMemAdvisesuccess, since WDDM (Windows) GPUs correctly reject it.ptxas-invoking tests work on Windows. Across six crates, tests now findptxas.exe, assemble to a real throwaway file instead of/dev/null, and captureptxas’s stdout in failure messages.- Test suite held at 38,675 passing tests (
--all-features; 37,320 with default features), up from 38,646/37,316 at 0.5.1.
Tips
- Upgrade if you’ve ever seen
CUDA_ERROR_INVALID_PTXwith no obvious cause. A typographic character in a hand-written or generated PTX comment is a classic silent-on-11.x, broken-on-12.9+ trap — 0.5.2’sascii_only()scrubber catches it even in PTX OxiCUDA didn’t generate itself. - Don’t paste “smart quotes,” em dashes, or math symbols into PTX comments by hand. Even though
oxicuda’s own generators are fixed,ptxasstill rejects non-ASCII bytes in hand-written.ptxfiles outside OxiCUDA’s scrubbed path. - If you maintain a DMRG excited-states search on
oxicuda-tn, re-run it. Results fromoptimise_with_penaltybefore 0.5.2 may have converged to a state with unexpectedly large overlap with previously found states — the basis-rotation fix changes the actual optimization trajectory, not just its numerics. - On Windows, lock-contention errors that used to look fatal will now just retry. If you had workaround code catching
InvalidValuearoundFileLockGuardacquisition, it’s no longer needed — genuine contention is retried automatically. - Run
cargo test --features gpu-testson Windows if you can. This release’s test-infrastructure fixes (ptxas.exe, throwaway cubin files, stdout capture) specifically target signal quality on Windows CI — they’re most visible there.
This is the foundation
OxiCUDA is the GPU layer beneath the rest of the COOLJAPAN ecosystem. Its own architecture diagram puts SciRS2, OxiONNX, TrustformeRS, and ToRSh directly on top of it, with OxiBLAS and OxiFFT rounding out the list of projects that lean on this stack — and the oxicuda crate itself is an umbrella re-export over all 74 crates, so a single cargo add oxicuda pulls in whichever subsystem a dependent project needs. Every one of them inherits 0.5.2’s portability fixes today, without a single line of their own code changing.
Repository: https://github.com/cool-japan/oxicuda
Star the repo if you believe a stray character in a comment should never be the reason a GPU kernel fails to load. Every star tells us to keep building.
The era of “works on my toolchain” is over. Pure Rust GPU computing is here — and as of 0.5.2, it’s a little more portable than it was yesterday.
— KitaSan at COOLJAPAN OÜ July 27, 2026