From ae2116b6c06f89466c1f29ca66d19557c74714d8 Mon Sep 17 00:00:00 2001 From: nihalpasham Date: Mon, 19 Oct 2026 19:45:26 +0530 Subject: [PATCH] test(drop_glue): keep destructor MIR with -Zinline-mir=no, not #[inline(never)] The compile-only probes put #[inline(never)] on their destructors and empty callees so each destructor's own MIR (a loop, a recursive call, a two-way branch) reaches the importer's no-op proof. verify-code-shape.sh then expects the optimized kernels to show a store followed by a self-loop, which only holds while LLVM inlines those destructors. Once #[inline(never)] is carried to LLVM as noinline, as rustc's own backend does, the destructors stay real PTX functions, the kernels call them, and the check fails. Switch MIR inlining off for this one package instead, the way disjoint_slice_len and kernel_reference_validity do, and drop the attributes. The importer sees the same destructor MIR as before, LLVM may inline as it likes, and the PTX on main is instruction-identical (only block-name comments move). The comments in the script and the README said the attribute was not propagated; they now say why the destructors carry none. Signed-off-by: nihalpasham --- .../examples/drop_glue/Cargo.toml | 17 +++++++++++++++++ .../examples/drop_glue/README.md | 5 ++++- .../examples/drop_glue/src/main.rs | 14 ++++++-------- .../examples/drop_glue/verify-code-shape.sh | 10 ++++++---- 4 files changed, 33 insertions(+), 13 deletions(-) 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