From 6b66d1442a09f4804f66f8f54e4084ac8fbbae65 Mon Sep 17 00:00:00 2001 From: sunteng Date: Thu, 4 Jun 2026 21:36:36 +0800 Subject: [PATCH 1/7] feat: add cbrt intrinsic support for f32 and f64 Signed-off-by: sunteng --- crates/dialect-mir/src/rust_intrinsics.rs | 4 + .../terminator/intrinsics/float_math.rs | 31 ++ crates/mir-lower/src/convert/ops/call.rs | 16 + .../examples/cbrt_smoke/Cargo.lock | 424 ++++++++++++++++++ .../examples/cbrt_smoke/Cargo.toml | 13 + .../examples/cbrt_smoke/LICENSE | 190 ++++++++ .../examples/cbrt_smoke/README.md | 35 ++ .../examples/cbrt_smoke/src/main.rs | 180 ++++++++ crates/rustc-codegen-cuda/src/collector.rs | 4 + 9 files changed, 897 insertions(+) create mode 100644 crates/rustc-codegen-cuda/examples/cbrt_smoke/Cargo.lock create mode 100644 crates/rustc-codegen-cuda/examples/cbrt_smoke/Cargo.toml create mode 100644 crates/rustc-codegen-cuda/examples/cbrt_smoke/LICENSE create mode 100644 crates/rustc-codegen-cuda/examples/cbrt_smoke/README.md create mode 100644 crates/rustc-codegen-cuda/examples/cbrt_smoke/src/main.rs diff --git a/crates/dialect-mir/src/rust_intrinsics.rs b/crates/dialect-mir/src/rust_intrinsics.rs index e9af999dbb..e3eeba8335 100644 --- a/crates/dialect-mir/src/rust_intrinsics.rs +++ b/crates/dialect-mir/src/rust_intrinsics.rs @@ -143,3 +143,7 @@ pub const CALLEE_ATAN2_F64: &str = placeholder!("atan2f64"); pub const CALLEE_ATAN_F32: &str = placeholder!("atanf32"); /// Placeholder call used for `f64::atan` / `std::sys::cmath::atan`. pub const CALLEE_ATAN_F64: &str = placeholder!("atanf64"); +/// Placeholder call used for `f32::cbrt` / `std::sys::cmath::cbrtf`. +pub const CALLEE_CBRT_F32: &str = placeholder!("cbrtf32"); +/// Placeholder call used for `f64::cbrt` / `std::sys::cmath::cbrt`. +pub const CALLEE_CBRT_F64: &str = placeholder!("cbrtf64"); diff --git a/crates/mir-importer/src/translator/terminator/intrinsics/float_math.rs b/crates/mir-importer/src/translator/terminator/intrinsics/float_math.rs index 7bfd720bbd..d3c2c23d4e 100644 --- a/crates/mir-importer/src/translator/terminator/intrinsics/float_math.rs +++ b/crates/mir-importer/src/translator/terminator/intrinsics/float_math.rs @@ -113,6 +113,10 @@ pub enum RustFloatMathIntrinsic { AtanF32, /// `f64::atan` / `std::sys::cmath::atan`. AtanF64, + /// `f32::cbrt` / `std::sys::cmath::cbrtf`. + CbrtF32, + /// `f64::cbrt` / `std::sys::cmath::cbrt`. + CbrtF64, } impl RustFloatMathIntrinsic { @@ -182,6 +186,10 @@ impl RustFloatMathIntrinsic { "std::sys::cmath::atan2" => Some(Self::Atan2F64), "std::sys::cmath::atanf" => Some(Self::AtanF32), "std::sys::cmath::atan" => Some(Self::AtanF64), + "std::sys::cmath::cbrtf" => Some(Self::CbrtF32), + "std::sys::cmath::cbrt" => Some(Self::CbrtF64), + "core::num::imp::libm::cbrtf" => Some(Self::CbrtF32), + "core::num::imp::libm::cbrt" => Some(Self::CbrtF64), _ => None, } } @@ -236,6 +244,8 @@ impl RustFloatMathIntrinsic { Self::Atan2F64 => rust_intrinsics::CALLEE_ATAN2_F64, Self::AtanF32 => rust_intrinsics::CALLEE_ATAN_F32, Self::AtanF64 => rust_intrinsics::CALLEE_ATAN_F64, + Self::CbrtF32 => rust_intrinsics::CALLEE_CBRT_F32, + Self::CbrtF64 => rust_intrinsics::CALLEE_CBRT_F64, } } } @@ -328,6 +338,27 @@ mod tests { ); } + /// `f{32,64}::cbrt` reaches device codegen as either the `std::sys::cmath` + /// C shim or the in-tree pure-Rust libm path, depending on toolchain. + /// Both must map to the libdevice-backed `Cbrt*` variants; pin them so a + /// rustc rename surfaces as a test failure rather than an undefined-symbol + /// PTX verification error. + #[test] + fn from_core_path_recognizes_cbrt_via_cmath_and_libm() { + for (path, expected) in [ + ("std::sys::cmath::cbrtf", RustFloatMathIntrinsic::CbrtF32), + ("std::sys::cmath::cbrt", RustFloatMathIntrinsic::CbrtF64), + ("core::num::imp::libm::cbrtf", RustFloatMathIntrinsic::CbrtF32), + ("core::num::imp::libm::cbrt", RustFloatMathIntrinsic::CbrtF64), + ] { + assert_eq!( + RustFloatMathIntrinsic::from_core_path(path), + Some(expected), + "`{path}` did not map to the expected cbrt intrinsic" + ); + } + } + #[test] fn maxnum_minnum_nsz_placeholders_round_trip_through_dialect_mir() { // The placeholder names must match between this importer crate and diff --git a/crates/mir-lower/src/convert/ops/call.rs b/crates/mir-lower/src/convert/ops/call.rs index b3d7636a54..1177b2b2a9 100644 --- a/crates/mir-lower/src/convert/ops/call.rs +++ b/crates/mir-lower/src/convert/ops/call.rs @@ -198,6 +198,8 @@ enum RustFloatMathIntrinsic { Atan2F64, AtanF32, AtanF64, + CbrtF32, + CbrtF64, } impl RustFloatMathIntrinsic { @@ -251,6 +253,8 @@ impl RustFloatMathIntrinsic { rust_intrinsics::CALLEE_ATAN2_F64 => Some(Self::Atan2F64), rust_intrinsics::CALLEE_ATAN_F32 => Some(Self::AtanF32), rust_intrinsics::CALLEE_ATAN_F64 => Some(Self::AtanF64), + rust_intrinsics::CALLEE_CBRT_F32 => Some(Self::CbrtF32), + rust_intrinsics::CALLEE_CBRT_F64 => Some(Self::CbrtF64), _ => None, } } @@ -315,6 +319,8 @@ impl RustFloatMathIntrinsic { Self::Atan2F64 => Ok("__nv_atan2"), Self::AtanF32 => Ok("__nv_atanf"), Self::AtanF64 => Ok("__nv_atan"), + Self::CbrtF32 => Ok("__nv_cbrtf"), + Self::CbrtF64 => Ok("__nv_cbrt"), } } @@ -1195,6 +1201,14 @@ mod tests { rust_intrinsics::CALLEE_ATAN_F64, RustFloatMathIntrinsic::AtanF64, ), + ( + rust_intrinsics::CALLEE_CBRT_F32, + RustFloatMathIntrinsic::CbrtF32, + ), + ( + rust_intrinsics::CALLEE_CBRT_F64, + RustFloatMathIntrinsic::CbrtF64, + ), ]; for (name, expected) in cases { @@ -1223,6 +1237,8 @@ mod tests { assert_eq!(RustFloatMathIntrinsic::Atan2F64.arg_count(), 2); assert_eq!(RustFloatMathIntrinsic::AtanF32.arg_count(), 1); assert_eq!(RustFloatMathIntrinsic::AtanF64.arg_count(), 1); + assert_eq!(RustFloatMathIntrinsic::CbrtF32.arg_count(), 1); + assert_eq!(RustFloatMathIntrinsic::CbrtF64.arg_count(), 1); assert_eq!(RustFloatMathIntrinsic::FmaF32.arg_count(), 3); assert_eq!(RustFloatMathIntrinsic::FmuladdF64.arg_count(), 3); assert_eq!(RustFloatMathIntrinsic::MaxNumNszF32.arg_count(), 2); diff --git a/crates/rustc-codegen-cuda/examples/cbrt_smoke/Cargo.lock b/crates/rustc-codegen-cuda/examples/cbrt_smoke/Cargo.lock new file mode 100644 index 0000000000..468437ddbf --- /dev/null +++ b/crates/rustc-codegen-cuda/examples/cbrt_smoke/Cargo.lock @@ -0,0 +1,424 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "bindgen" +version = "0.71.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" + +[[package]] +name = "cbrt_smoke" +version = "0.1.0" +dependencies = [ + "cuda-core", + "cuda-device", + "cuda-host", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "cuda-bindings" +version = "0.1.0" +dependencies = [ + "bindgen", +] + +[[package]] +name = "cuda-core" +version = "0.1.0" +dependencies = [ + "anyhow", + "cuda-bindings", + "half", + "oxide-artifacts", +] + +[[package]] +name = "cuda-device" +version = "0.1.0" +dependencies = [ + "cuda-macros", +] + +[[package]] +name = "cuda-host" +version = "0.1.0" +dependencies = [ + "cuda-core", + "cuda-macros", + "half", + "libnvvm-sys", + "nvjitlink-sys", + "thiserror", +] + +[[package]] +name = "cuda-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "reserved-oxide-symbols", + "syn", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libnvvm-sys" +version = "0.1.0" +dependencies = [ + "libloading", + "thiserror", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nvjitlink-sys" +version = "0.1.0" +dependencies = [ + "libloading", + "thiserror", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "crc32fast", + "hashbrown 0.15.5", + "indexmap", + "memchr", +] + +[[package]] +name = "oxide-artifacts" +version = "0.1.0" +dependencies = [ + "object", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "reserved-oxide-symbols" +version = "0.1.0" + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/crates/rustc-codegen-cuda/examples/cbrt_smoke/Cargo.toml b/crates/rustc-codegen-cuda/examples/cbrt_smoke/Cargo.toml new file mode 100644 index 0000000000..c925c4707d --- /dev/null +++ b/crates/rustc-codegen-cuda/examples/cbrt_smoke/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "cbrt_smoke" +version = "0.1.0" +edition = "2024" +license = "Apache-2.0" + +# Standalone crate (not part of parent workspace). +[workspace] + +[dependencies] +cuda-device = { path = "../../../cuda-device" } +cuda-host = { path = "../../../cuda-host" } +cuda-core = { path = "../../../cuda-core" } diff --git a/crates/rustc-codegen-cuda/examples/cbrt_smoke/LICENSE b/crates/rustc-codegen-cuda/examples/cbrt_smoke/LICENSE new file mode 100644 index 0000000000..7cdf362c1a --- /dev/null +++ b/crates/rustc-codegen-cuda/examples/cbrt_smoke/LICENSE @@ -0,0 +1,190 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2024-2026 NVIDIA CORPORATION + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/crates/rustc-codegen-cuda/examples/cbrt_smoke/README.md b/crates/rustc-codegen-cuda/examples/cbrt_smoke/README.md new file mode 100644 index 0000000000..1bd8ab5610 --- /dev/null +++ b/crates/rustc-codegen-cuda/examples/cbrt_smoke/README.md @@ -0,0 +1,35 @@ +# cbrt_smoke + +## `cbrt` via libdevice + +This example demonstrates `f32::cbrt` and `f64::cbrt` in device code +lowering to NVIDIA libdevice (`__nv_cbrtf`, `__nv_cbrt`). + +## What This Example Does + +- Two small kernels — one per width — compute `x.cbrt()` per element using + the native Rust method syntax. +- The host runs the kernels over 16 inputs spanning negative, zero, small + and large magnitudes (plus perfect cubes whose root is exactly + representable), then compares each result against the same expression + evaluated with stdlib `f{32,64}::cbrt` on the host. +- Unlike `sqrt`, `cbrt` is defined for negative operands, so the + sign-cross cases (`-8 -> -2`, `-64 -> -4`, `-0.0`) are the interesting + part of the check. +- Tolerance: 2 ULP, matching the bound `math_atan` / `primitive_stress` + use for the other libdevice transcendentals. + +Exits 0 on PASS, 1 on FAIL. + +## Pipeline + +Because the kernels emit `__nv_*` calls, the cuda-oxide pipeline stops at +NVVM-IR (skipping `llc`). `ltoir::load_kernel_module` then drives libNVVM +(linking `libdevice.10.bc`) and nvJitLink to produce a cubin on first +launch. + +## Run + +```bash +cargo oxide run cbrt_smoke +``` diff --git a/crates/rustc-codegen-cuda/examples/cbrt_smoke/src/main.rs b/crates/rustc-codegen-cuda/examples/cbrt_smoke/src/main.rs new file mode 100644 index 0000000000..559e3dba43 --- /dev/null +++ b/crates/rustc-codegen-cuda/examples/cbrt_smoke/src/main.rs @@ -0,0 +1,180 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +//! Smoke test for `f32::cbrt` / `f64::cbrt` → libdevice lowering. +//! +//! `f32::cbrt` / `f64::cbrt` lower to `std::sys::cmath::cbrtf` / `cbrt` +//! shims in MIR. Before this example was added, cuda-oxide had no handler +//! for those shims and the calls would fall out of the pipeline as an +//! unresolved symbol. After the matching entries were added to: +//! +//! * `dialect-mir::rust_intrinsics` +//! * `mir-importer::translator::terminator::intrinsics::float_math` +//! * `mir-lower::convert::ops::call` +//! * `rustc-codegen-cuda::collector` (cmath shim allowlist) +//! +//! the calls lower to libdevice `__nv_cbrtf` / `__nv_cbrt`, which the +//! auto-detected libNVVM + nvJitLink pipeline resolves transparently. +//! +//! The host computes the same expression with stdlib `f{32,64}::cbrt` and +//! compares within a 2-ULP tolerance (matching the bound `math_atan` and +//! `primitive_stress` use for the other libdevice transcendentals). Inputs +//! deliberately span negative, zero, small and large magnitudes — unlike +//! `sqrt`, `cbrt` is defined for negative operands, so the sign-cross cases +//! are the interesting part of the check. +//! +//! Run: +//! cargo oxide run cbrt_smoke +//! +//! Exits 0 on PASS, 1 on FAIL. + +use cuda_core::{CudaContext, DeviceBuffer, LaunchConfig}; +use cuda_device::{DisjointSlice, kernel, thread}; +use cuda_host::{cuda_launch, ltoir}; + +#[kernel] +pub fn cbrt_f32(xs: &[f32], mut out: DisjointSlice) { + let idx = thread::index_1d(); + let i = idx.get(); + if i < xs.len() + && let Some(slot) = out.get_mut(idx) + { + *slot = xs[i].cbrt(); + } +} + +#[kernel] +pub fn cbrt_f64(xs: &[f64], mut out: DisjointSlice) { + let idx = thread::index_1d(); + let i = idx.get(); + if i < xs.len() + && let Some(slot) = out.get_mut(idx) + { + *slot = xs[i].cbrt(); + } +} + +/// IEEE-754 ULP distance for finite operands of a given width. +/// `cbrt` of finite real inputs is finite and sign-preserving, so we don't +/// need to handle NaN/Inf here. +fn ulp_distance(a_bits: u64, b_bits: u64, sign_mask: u64, body_mask: u64) -> u64 { + let map = |bits: u64| { + if bits & sign_mask != 0 { + sign_mask - (bits & body_mask) + } else { + sign_mask + (bits & body_mask) + } + }; + map(a_bits).abs_diff(map(b_bits)) +} + +fn ulp_diff_f32(a: f32, b: f32) -> u64 { + ulp_distance( + a.to_bits() as u64, + b.to_bits() as u64, + 0x8000_0000, + 0x7FFF_FFFF, + ) +} + +fn ulp_diff_f64(a: f64, b: f64) -> u64 { + ulp_distance( + a.to_bits(), + b.to_bits(), + 0x8000_0000_0000_0000, + 0x7FFF_FFFF_FFFF_FFFF, + ) +} + +fn main() -> Result<(), Box> { + println!("=== cbrt_smoke: f32/f64 cbrt via libdevice ===\n"); + + let ctx = CudaContext::new(0)?; + let stream = ctx.default_stream(); + // `__nv_*` calls in the kernel force the NVVM-IR output flavor; the + // first launch builds a cubin via libNVVM + nvJitLink. + let module = ltoir::load_kernel_module(&ctx, "cbrt_smoke")?; + + // Negative / zero / small / large magnitudes, plus perfect cubes whose + // root is exactly representable (8 -> 2, 27 -> 3, -64 -> -4, 1000 -> 10). + // Values are f32-representable so the same array doubles as f64 input + // after a widening cast. + let xs_f32: Vec = vec![ + 0.0, -0.0, 1.0, -1.0, 8.0, -8.0, 27.0, -64.0, 1000.0, 0.125, -0.125, 2.0, -2.0, 1e-3, + -1e6, 1e6, + ]; + let xs_f64: Vec = xs_f32.iter().map(|&v| v as f64).collect(); + let n = xs_f32.len(); + let cfg = LaunchConfig::for_num_elems(n as u32); + + let xs32 = DeviceBuffer::from_host(&stream, &xs_f32)?; + let xs64 = DeviceBuffer::from_host(&stream, &xs_f64)?; + + let mut out_cbrt_f32 = DeviceBuffer::::zeroed(&stream, n)?; + let mut out_cbrt_f64 = DeviceBuffer::::zeroed(&stream, n)?; + + cuda_launch! { + kernel: cbrt_f32, + stream: stream, module: module, config: cfg, + args: [slice(xs32), slice_mut(out_cbrt_f32)] + }?; + cuda_launch! { + kernel: cbrt_f64, + stream: stream, module: module, config: cfg, + args: [slice(xs64), slice_mut(out_cbrt_f64)] + }?; + + let got_cbrt_f32 = out_cbrt_f32.to_host_vec(&stream)?; + let got_cbrt_f64 = out_cbrt_f64.to_host_vec(&stream)?; + + // libdevice transcendentals are typically within 1 ULP of host libm; + // 2 ULP matches the bound `math_atan` / `primitive_stress` use for the + // other libdevice transcendentals. + const ULP_LIMIT: u64 = 2; + + let mut failures = 0usize; + for i in 0..n { + let exp_cbrt_f32 = xs_f32[i].cbrt(); + let exp_cbrt_f64 = xs_f64[i].cbrt(); + + let d_cbrt_f32 = ulp_diff_f32(got_cbrt_f32[i], exp_cbrt_f32); + let d_cbrt_f64 = ulp_diff_f64(got_cbrt_f64[i], exp_cbrt_f64); + + let ok = d_cbrt_f32 <= ULP_LIMIT && d_cbrt_f64 <= ULP_LIMIT; + + if !ok { + failures += 1; + if failures <= 8 { + eprintln!( + "[{i}] x={:>10.4} | \ + cbrt_f32 ulp={d_cbrt_f32} (gpu={:e} cpu={:e}) | \ + cbrt_f64 ulp={d_cbrt_f64} (gpu={:e} cpu={:e})", + xs_f32[i], got_cbrt_f32[i], exp_cbrt_f32, got_cbrt_f64[i], exp_cbrt_f64, + ); + } + } + } + + // A handful of representative samples printed regardless of pass/fail. + for &i in &[3usize, 4, 7] { + println!( + "[{i}] x={x32:>8.3} cbrt_f32 gpu={c32} cpu={ec32} \ + cbrt_f64 gpu={c64} cpu={ec64}", + x32 = xs_f32[i], + c32 = got_cbrt_f32[i], + ec32 = xs_f32[i].cbrt(), + c64 = got_cbrt_f64[i], + ec64 = xs_f64[i].cbrt(), + ); + } + + if failures == 0 { + println!("\nSUCCESS: {n} cases × 2 variants within {ULP_LIMIT} ULP of host libm"); + Ok(()) + } else { + eprintln!("\nFAILED: {failures}/{n} cases out of tolerance"); + std::process::exit(1); + } +} diff --git a/crates/rustc-codegen-cuda/src/collector.rs b/crates/rustc-codegen-cuda/src/collector.rs index 4d4694eeb2..1e0af1a163 100644 --- a/crates/rustc-codegen-cuda/src/collector.rs +++ b/crates/rustc-codegen-cuda/src/collector.rs @@ -427,6 +427,10 @@ fn is_intrinsic_lowered_cmath_shim(fn_path: &str) -> bool { | "std::sys::cmath::atan2" | "std::sys::cmath::atanf" | "std::sys::cmath::atan" + | "std::sys::cmath::cbrtf" + | "std::sys::cmath::cbrt" + | "core::num::imp::libm::cbrtf" + | "core::num::imp::libm::cbrt" ) } From c3fbbc951b0e395c105bc240ba3c2d6c01be0b7d Mon Sep 17 00:00:00 2001 From: nihalpasham Date: Fri, 12 Jun 2026 12:15:37 +0530 Subject: [PATCH 2/7] refactor(examples): port cbrt_smoke off bare cuda_launch! to #[cuda_module] Main made bare cuda_launch! a caller-unsafe API (#171) after this example was written, so its host side no longer compiles (E0133). The typed #[cuda_module] launch path is the safe replacement the other math examples already use; the 16-value x 2-width host comparison and the SUCCESS marker are unchanged. --- .../examples/cbrt_smoke/src/main.rs | 60 +++++++++---------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/crates/rustc-codegen-cuda/examples/cbrt_smoke/src/main.rs b/crates/rustc-codegen-cuda/examples/cbrt_smoke/src/main.rs index 559e3dba43..6e2e5d02c1 100644 --- a/crates/rustc-codegen-cuda/examples/cbrt_smoke/src/main.rs +++ b/crates/rustc-codegen-cuda/examples/cbrt_smoke/src/main.rs @@ -32,27 +32,32 @@ use cuda_core::{CudaContext, DeviceBuffer, LaunchConfig}; use cuda_device::{DisjointSlice, kernel, thread}; -use cuda_host::{cuda_launch, ltoir}; - -#[kernel] -pub fn cbrt_f32(xs: &[f32], mut out: DisjointSlice) { - let idx = thread::index_1d(); - let i = idx.get(); - if i < xs.len() - && let Some(slot) = out.get_mut(idx) - { - *slot = xs[i].cbrt(); +use cuda_host::{cuda_module, ltoir}; + +#[cuda_module] +mod kernels { + use super::*; + + #[kernel] + pub fn cbrt_f32(xs: &[f32], mut out: DisjointSlice) { + let idx = thread::index_1d(); + let i = idx.get(); + if i < xs.len() + && let Some(slot) = out.get_mut(idx) + { + *slot = xs[i].cbrt(); + } } -} -#[kernel] -pub fn cbrt_f64(xs: &[f64], mut out: DisjointSlice) { - let idx = thread::index_1d(); - let i = idx.get(); - if i < xs.len() - && let Some(slot) = out.get_mut(idx) - { - *slot = xs[i].cbrt(); + #[kernel] + pub fn cbrt_f64(xs: &[f64], mut out: DisjointSlice) { + let idx = thread::index_1d(); + let i = idx.get(); + if i < xs.len() + && let Some(slot) = out.get_mut(idx) + { + *slot = xs[i].cbrt(); + } } } @@ -96,14 +101,15 @@ fn main() -> Result<(), Box> { // `__nv_*` calls in the kernel force the NVVM-IR output flavor; the // first launch builds a cubin via libNVVM + nvJitLink. let module = ltoir::load_kernel_module(&ctx, "cbrt_smoke")?; + let module = kernels::from_module(module)?; // Negative / zero / small / large magnitudes, plus perfect cubes whose // root is exactly representable (8 -> 2, 27 -> 3, -64 -> -4, 1000 -> 10). // Values are f32-representable so the same array doubles as f64 input // after a widening cast. let xs_f32: Vec = vec![ - 0.0, -0.0, 1.0, -1.0, 8.0, -8.0, 27.0, -64.0, 1000.0, 0.125, -0.125, 2.0, -2.0, 1e-3, - -1e6, 1e6, + 0.0, -0.0, 1.0, -1.0, 8.0, -8.0, 27.0, -64.0, 1000.0, 0.125, -0.125, 2.0, -2.0, 1e-3, -1e6, + 1e6, ]; let xs_f64: Vec = xs_f32.iter().map(|&v| v as f64).collect(); let n = xs_f32.len(); @@ -115,16 +121,8 @@ fn main() -> Result<(), Box> { let mut out_cbrt_f32 = DeviceBuffer::::zeroed(&stream, n)?; let mut out_cbrt_f64 = DeviceBuffer::::zeroed(&stream, n)?; - cuda_launch! { - kernel: cbrt_f32, - stream: stream, module: module, config: cfg, - args: [slice(xs32), slice_mut(out_cbrt_f32)] - }?; - cuda_launch! { - kernel: cbrt_f64, - stream: stream, module: module, config: cfg, - args: [slice(xs64), slice_mut(out_cbrt_f64)] - }?; + module.cbrt_f32(&stream, cfg, &xs32, &mut out_cbrt_f32)?; + module.cbrt_f64(&stream, cfg, &xs64, &mut out_cbrt_f64)?; let got_cbrt_f32 = out_cbrt_f32.to_host_vec(&stream)?; let got_cbrt_f64 = out_cbrt_f64.to_host_vec(&stream)?; From ce0846d4fed6bae0a0382157d14e0bc3a0c86e16 Mon Sep 17 00:00:00 2001 From: nihalpasham Date: Fri, 12 Jun 2026 12:15:44 +0530 Subject: [PATCH 3/7] chore(examples): refresh stale cbrt_smoke lockfile The committed lock pinned the cuda-* path deps at 0.1.0; the workspace has been at 0.2.1 since the version bump, so any build rewrites the lock and dirties the tree. Commit the regenerated lock. --- .../examples/cbrt_smoke/Cargo.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/rustc-codegen-cuda/examples/cbrt_smoke/Cargo.lock b/crates/rustc-codegen-cuda/examples/cbrt_smoke/Cargo.lock index 468437ddbf..767e168265 100644 --- a/crates/rustc-codegen-cuda/examples/cbrt_smoke/Cargo.lock +++ b/crates/rustc-codegen-cuda/examples/cbrt_smoke/Cargo.lock @@ -95,14 +95,14 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "cuda-bindings" -version = "0.1.0" +version = "0.2.1" dependencies = [ "bindgen", ] [[package]] name = "cuda-core" -version = "0.1.0" +version = "0.2.1" dependencies = [ "anyhow", "cuda-bindings", @@ -112,14 +112,14 @@ dependencies = [ [[package]] name = "cuda-device" -version = "0.1.0" +version = "0.2.1" dependencies = [ "cuda-macros", ] [[package]] name = "cuda-host" -version = "0.1.0" +version = "0.2.1" dependencies = [ "cuda-core", "cuda-macros", @@ -131,7 +131,7 @@ dependencies = [ [[package]] name = "cuda-macros" -version = "0.1.0" +version = "0.2.1" dependencies = [ "proc-macro2", "quote", @@ -226,7 +226,7 @@ dependencies = [ [[package]] name = "libnvvm-sys" -version = "0.1.0" +version = "0.2.1" dependencies = [ "libloading", "thiserror", @@ -262,7 +262,7 @@ dependencies = [ [[package]] name = "nvjitlink-sys" -version = "0.1.0" +version = "0.2.1" dependencies = [ "libloading", "thiserror", @@ -282,7 +282,7 @@ dependencies = [ [[package]] name = "oxide-artifacts" -version = "0.1.0" +version = "0.2.1" dependencies = [ "object", ] @@ -346,7 +346,7 @@ checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "reserved-oxide-symbols" -version = "0.1.0" +version = "0.2.1" [[package]] name = "rustc-hash" From e2825b73d42f3ae93af852ada42471e3236be7f3 Mon Sep 17 00:00:00 2001 From: nihalpasham Date: Fri, 12 Jun 2026 12:15:51 +0530 Subject: [PATCH 4/7] style: rustfmt the cbrt importer test array CI runs a workspace-wide cargo fmt check; the new test table in float_math.rs was not rustfmt-clean. --- .../src/translator/terminator/intrinsics/float_math.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/mir-importer/src/translator/terminator/intrinsics/float_math.rs b/crates/mir-importer/src/translator/terminator/intrinsics/float_math.rs index d3c2c23d4e..ec24eafa0a 100644 --- a/crates/mir-importer/src/translator/terminator/intrinsics/float_math.rs +++ b/crates/mir-importer/src/translator/terminator/intrinsics/float_math.rs @@ -348,8 +348,14 @@ mod tests { for (path, expected) in [ ("std::sys::cmath::cbrtf", RustFloatMathIntrinsic::CbrtF32), ("std::sys::cmath::cbrt", RustFloatMathIntrinsic::CbrtF64), - ("core::num::imp::libm::cbrtf", RustFloatMathIntrinsic::CbrtF32), - ("core::num::imp::libm::cbrt", RustFloatMathIntrinsic::CbrtF64), + ( + "core::num::imp::libm::cbrtf", + RustFloatMathIntrinsic::CbrtF32, + ), + ( + "core::num::imp::libm::cbrt", + RustFloatMathIntrinsic::CbrtF64, + ), ] { assert_eq!( RustFloatMathIntrinsic::from_core_path(path), From 90bfc80b25bc6dec69b95a197d46da99602bd41d Mon Sep 17 00:00:00 2001 From: nihalpasham Date: Fri, 12 Jun 2026 12:15:59 +0530 Subject: [PATCH 5/7] docs(examples): lead with the core libm path in cbrt_smoke module docs On the pinned toolchain f{32,64}::cbrt inlines down to the core::num::imp::libm extern declarations, not the std::sys::cmath shims (those carry atan but only cbrtf128 for cbrt). Reword so the actually-taken path leads and the cmath spelling is the historical note. --- .../examples/cbrt_smoke/src/main.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/crates/rustc-codegen-cuda/examples/cbrt_smoke/src/main.rs b/crates/rustc-codegen-cuda/examples/cbrt_smoke/src/main.rs index 6e2e5d02c1..f4e1269b98 100644 --- a/crates/rustc-codegen-cuda/examples/cbrt_smoke/src/main.rs +++ b/crates/rustc-codegen-cuda/examples/cbrt_smoke/src/main.rs @@ -5,15 +5,18 @@ //! Smoke test for `f32::cbrt` / `f64::cbrt` → libdevice lowering. //! -//! `f32::cbrt` / `f64::cbrt` lower to `std::sys::cmath::cbrtf` / `cbrt` -//! shims in MIR. Before this example was added, cuda-oxide had no handler -//! for those shims and the calls would fall out of the pipeline as an -//! unresolved symbol. After the matching entries were added to: +//! On the pinned toolchain, `f32::cbrt` / `f64::cbrt` inline down to the +//! `core::num::imp::libm::cbrtf` / `cbrt` extern declarations, so that is +//! the call shape device codegen sees in MIR (older toolchains routed the +//! same calls through `std::sys::cmath` shims; the importer recognizes +//! both spellings). Before this example was added, cuda-oxide had no +//! handler for those paths and the calls would fall out of the pipeline +//! as an unresolved symbol. After the matching entries were added to: //! //! * `dialect-mir::rust_intrinsics` //! * `mir-importer::translator::terminator::intrinsics::float_math` //! * `mir-lower::convert::ops::call` -//! * `rustc-codegen-cuda::collector` (cmath shim allowlist) +//! * `rustc-codegen-cuda::collector` (std cmath shim allowlist) //! //! the calls lower to libdevice `__nv_cbrtf` / `__nv_cbrt`, which the //! auto-detected libNVVM + nvJitLink pipeline resolves transparently. @@ -21,7 +24,7 @@ //! The host computes the same expression with stdlib `f{32,64}::cbrt` and //! compares within a 2-ULP tolerance (matching the bound `math_atan` and //! `primitive_stress` use for the other libdevice transcendentals). Inputs -//! deliberately span negative, zero, small and large magnitudes — unlike +//! deliberately span negative, zero, small and large magnitudes: unlike //! `sqrt`, `cbrt` is defined for negative operands, so the sign-cross cases //! are the interesting part of the check. //! From fc5430c77f64a847fd73d18dab0d77156844d2d1 Mon Sep 17 00:00:00 2001 From: nihalpasham Date: Fri, 12 Jun 2026 12:16:07 +0530 Subject: [PATCH 6/7] refactor(collector): drop unreachable core libm arms from the cmath shim guard is_intrinsic_lowered_cmath_shim is only consulted inside the forbidden-std branch of should_collect_from_crate, where every def path starts with std::, so the core::num::imp::libm arms could never match. They are also unneeded: core is an allowed crate and those libm shims are extern "C" declarations with no MIR, so the collector's existing no-MIR skip already excludes them (verbose build of cbrt_smoke confirms the 'Skipping extern/intrinsic (no MIR)' path fires for both). Document the std-only constraint on the guard. --- crates/rustc-codegen-cuda/src/collector.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/rustc-codegen-cuda/src/collector.rs b/crates/rustc-codegen-cuda/src/collector.rs index 11ed1ad495..9dfc410740 100644 --- a/crates/rustc-codegen-cuda/src/collector.rs +++ b/crates/rustc-codegen-cuda/src/collector.rs @@ -420,8 +420,16 @@ pub fn is_fully_monomorphized<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) /// emits an `__nv_*` libdevice call instead, and the collector silently /// skips them here so the std-crate guard doesn't fire. /// -/// Keep this list in sync with the matches in +/// Keep this list in sync with the `std::sys::cmath` matches in /// `mir-importer/src/translator/terminator/intrinsics/float_math.rs`. +/// +/// Only `std::`-crate paths belong here: the guard is consulted solely +/// inside the forbidden-std branch of `should_collect_from_crate`, where +/// every def path starts with `std::`. Intrinsic-lowered shims that live +/// in `core` (e.g. the `core::num::imp::libm::cbrtf` / `cbrt` extern +/// declarations that back `f{32,64}::cbrt` on the pinned toolchain) never +/// need an entry: `core` is an allowed crate, and the declarations have +/// no MIR, so the collector's no-MIR skip already excludes them. fn is_intrinsic_lowered_cmath_shim(fn_path: &str) -> bool { matches!( fn_path, @@ -431,8 +439,6 @@ fn is_intrinsic_lowered_cmath_shim(fn_path: &str) -> bool { | "std::sys::cmath::atan" | "std::sys::cmath::cbrtf" | "std::sys::cmath::cbrt" - | "core::num::imp::libm::cbrtf" - | "core::num::imp::libm::cbrt" ) } From c14ace5c1f980d844415844f5b2a3fb747b9c50f Mon Sep 17 00:00:00 2001 From: nihalpasham Date: Fri, 12 Jun 2026 14:56:29 +0530 Subject: [PATCH 7/7] feat(mir-importer): map libm::cbrt{,f} onto the cbrt intrinsics Completes the coordination between the libm interception table (#142) and the cbrt lowering (#148): the libm-crate spellings now reach the same libdevice __nv_cbrt{,f} path as the core ones. --- .../src/translator/terminator/intrinsics/float_math.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/mir-importer/src/translator/terminator/intrinsics/float_math.rs b/crates/mir-importer/src/translator/terminator/intrinsics/float_math.rs index b88adf5793..3bf1031d97 100644 --- a/crates/mir-importer/src/translator/terminator/intrinsics/float_math.rs +++ b/crates/mir-importer/src/translator/terminator/intrinsics/float_math.rs @@ -253,6 +253,8 @@ impl RustFloatMathIntrinsic { "atan2" => Some(Self::Atan2F64), "atanf" => Some(Self::AtanF32), "atan" => Some(Self::AtanF64), + "cbrtf" => Some(Self::CbrtF32), + "cbrt" => Some(Self::CbrtF64), _ => None, } } @@ -584,6 +586,8 @@ mod tests { "core::num::imp::libm::cbrt", RustFloatMathIntrinsic::CbrtF64, ), + ("libm::cbrtf", RustFloatMathIntrinsic::CbrtF32), + ("libm::math::cbrt::cbrt", RustFloatMathIntrinsic::CbrtF64), ] { assert_eq!( RustFloatMathIntrinsic::from_core_path(path),