diff --git a/cuda-oxide/crates/rustc-codegen-cuda/examples/drop_glue/Cargo.toml b/cuda-oxide/crates/rustc-codegen-cuda/examples/drop_glue/Cargo.toml index aa58df12a9..f6bb6b8327 100644 --- a/cuda-oxide/crates/rustc-codegen-cuda/examples/drop_glue/Cargo.toml +++ b/cuda-oxide/crates/rustc-codegen-cuda/examples/drop_glue/Cargo.toml @@ -1,3 +1,5 @@ +cargo-features = ["profile-rustflags"] + [package] name = "drop_glue" version = "0.1.0" @@ -11,3 +13,18 @@ license = "Apache-2.0" cuda-core = { path = "../../../../../cuda-core" } cuda-device = { path = "../../../cuda-device" } cuda-host = { path = "../../../cuda-host" } + +# The compile-only probes need each destructor's own MIR to reach the importer: +# a CFG cycle in `LoopingDrop::drop`, the recursive call in `RecursiveDrop::drop` +# and the two-way branch in `DiamondDrop::drop`. At default release settings +# rustc's MIR inliner folds the empty callees into the destructors and the +# destructors into their glue first. `#[inline(never)]` is not the answer: +# it is carried to LLVM as `noinline`, so the destructors would stay real PTX +# functions and the optimized kernels would call them instead of showing the +# store-then-self-loop that verify-code-shape.sh checks. Switching MIR inlining +# off for this one crate keeps the calls for LLVM to inline. Scoping the flag +# to this package keeps the shared dependency tree identical to every other +# example; a global RUSTFLAGS entry would re-key every dependency unit and +# force a full second dependency-tree build in CI. +[profile.release.package.drop_glue] +rustflags = ["-Zinline-mir=no"] diff --git a/cuda-oxide/crates/rustc-codegen-cuda/examples/drop_glue/README.md b/cuda-oxide/crates/rustc-codegen-cuda/examples/drop_glue/README.md index 4ca68b357a..1f2777c8f2 100644 --- a/cuda-oxide/crates/rustc-codegen-cuda/examples/drop_glue/README.md +++ b/cuda-oxide/crates/rustc-codegen-cuda/examples/drop_glue/README.md @@ -63,7 +63,10 @@ checks the optimized PTX. For the diverging probes the pre-drop store remains and is immediately followed by an unconditional self-loop, while the unreachable post-drop store is absent. For the reconverging probe both stores remain. The PTX adjacency holds because `opt -O2` inlines the -retained glue; `#[inline(never)]` is not propagated as `noinline`. +retained glue and the destructors. They carry no inline attribute, since +`#[inline(never)]` reaches LLVM as `noinline` and would keep them real PTX +functions; MIR inlining is off for this crate instead (`Cargo.toml`), so the +importer still sees each destructor's own MIR. These kernels are compiled but **never launched by `main`**. Do not launch the diverging probes manually: their destructors intentionally never diff --git a/cuda-oxide/crates/rustc-codegen-cuda/examples/drop_glue/src/main.rs b/cuda-oxide/crates/rustc-codegen-cuda/examples/drop_glue/src/main.rs index fa3b02fddb..316fd51308 100644 --- a/cuda-oxide/crates/rustc-codegen-cuda/examples/drop_glue/src/main.rs +++ b/cuda-oxide/crates/rustc-codegen-cuda/examples/drop_glue/src/main.rs @@ -42,12 +42,13 @@ impl Drop for DropMarker { } // Compile-only regression probes. These destructors intentionally never return; -// their kernels are generated but never launched by `main`. +// their kernels are generated but never launched by `main`. MIR inlining is +// off for this crate (see Cargo.toml) so each destructor reaches the importer +// with its own MIR. #[allow(dead_code)] struct LoopingDrop(u8); impl Drop for LoopingDrop { - #[inline(never)] #[allow(clippy::empty_loop)] fn drop(&mut self) { loop {} @@ -55,7 +56,6 @@ impl Drop for LoopingDrop { } #[allow(unconditional_recursion)] -#[inline(never)] fn recursive_noop() { recursive_noop(); } @@ -64,7 +64,6 @@ fn recursive_noop() { struct RecursiveDrop(u8); impl Drop for RecursiveDrop { - #[inline(never)] fn drop(&mut self) { recursive_noop(); } @@ -73,18 +72,17 @@ impl Drop for RecursiveDrop { // Precision control for the proof. An effect-free destructor whose two // branches join back together returns on every path, so the proof must keep // accepting it and lower the drop to a plain branch. The callees have visible -// empty bodies; `#[inline(never)]` keeps the diamond in the destructor's MIR. -#[inline(never)] +// empty bodies. MIR inlining is off for this crate (see Cargo.toml), which +// keeps the diamond in the destructor's MIR; `#[inline(never)]` would reach +// LLVM as `noinline` and keep these helpers real PTX functions. fn left_noop() {} -#[inline(never)] fn right_noop() {} #[allow(dead_code)] struct DiamondDrop(u8); impl Drop for DiamondDrop { - #[inline(never)] fn drop(&mut self) { if self.0 == 0 { left_noop() diff --git a/cuda-oxide/crates/rustc-codegen-cuda/examples/drop_glue/verify-code-shape.sh b/cuda-oxide/crates/rustc-codegen-cuda/examples/drop_glue/verify-code-shape.sh index e9695f81bd..beb07d83d7 100644 --- a/cuda-oxide/crates/rustc-codegen-cuda/examples/drop_glue/verify-code-shape.sh +++ b/cuda-oxide/crates/rustc-codegen-cuda/examples/drop_glue/verify-code-shape.sh @@ -100,10 +100,12 @@ def require_store(body, value, name, label="pre-drop"): return store.end() # The PTX adjacency below holds because `opt -O2` inlines the retained glue -# and the destructor into the kernel: `#[inline(never)]` is not propagated as -# `noinline`, as the cluster example's shape check also notes. The -# drop_glue.ll assertion is the one that tracks the importer's own decision; -# this check confirms what LLVM made of the call it kept. +# and the destructor into the kernel. The destructors carry no inline +# attribute: `#[inline(never)]` reaches LLVM as `noinline` and would keep them +# real PTX functions, so the kernel would call them instead. MIR inlining is +# off for this crate (Cargo.toml) so the importer still sees each destructor's +# own MIR. The drop_glue.ll assertion is the one that tracks the importer's +# own decision; this check confirms what LLVM made of the call it kept. def require_nonreturning_loop(body, after_store, name): # Both optimized probes lower to a store followed by an unconditional # self-loop. Tie the loop to that store's continuation: an unrelated