From 37df3bfec268cb0ead2d4627dca0bc2471f7804c Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Wed, 7 Oct 2026 00:52:25 +0200 Subject: [PATCH] Use `core::intrinsics::powi{f32,f64}` for `powi` instead of `num-traits` This undoes 8eecf61e0f3a0d9a822206673dab092985fe398b code changes, except for the `powi.rs` test, which is retained. To provide `powi` in a no-`std` environment, instead of pulling it from the `num-traits` crate and defining a custom intrinsic for it, we just define `powi` methods on `f32` and `f64` directly. In these methods, we just emit `core::intrinsics::powif32` and `core::intrinsics::powif64`, respectively. We can do this thanks to the `#[rustc_allow_incoherent_impl]` internal rustc attribute, which allows us to add inherent `impl` blocks on types defined in other crates, which would otherwise violate coherence rules. (This trait is similarly used inside the Rust compiler to have `core` module provide some alternative implementations of `std`'s methods in a no-`std` environment.) In a normal Rust library, the use of such an internal rustc attribute would have been unacceptable, but since Rust-GPU is a compiler, it seems reasonable. Unlike `f32` and `f64`, there is no need to implement `powi` for `f16` and `f128` because these already have `powi` methods correctly implemented in `core` (see https://doc.rust-lang.org/core/primitive.f16.html#method.powi and https://doc.rust-lang.org/core/primitive.f128.html#method.powi). And `f32` and `f64` actually will have these too, once Rust issue #137578 (https://github.com/rust-lang/rust/issues/137578) is resolved, rendering this commit obsolete. But noone knows how many years it will take them to get that going, so let's just fix the problem on Rust-GPU's end. There are other mathematical methods, e.g. `powf`, `sqrt`, and many others, that Rust-GPU still needs `num-traits` for. I will be moving their implementations to Rust-GPU and removing `num-traits` altogether in subsequent commits, after and if this PR is accepted -- this is just the first step to see if this solution is acceptable at all. I ran `cargo test`, `cargo compiletest`, `cargo difftest`. Tests pass. `powi.rs` test's output changed, but only marginally -- I have blessed it. Reference: https://github.com/Rust-GPU/rust-gpu/issues/520 --- .../src/builder/builder_methods.rs | 4 +--- .../src/builder/libm_intrinsics.rs | 10 --------- .../src/codegen_cx/declare.rs | 10 --------- .../rustc_codegen_spirv/src/codegen_cx/mod.rs | 3 --- crates/rustc_codegen_spirv/src/symbols.rs | 10 --------- crates/spirv-std/src/float.rs | 22 +++++++++++++++++++ crates/spirv-std/src/lib.rs | 2 +- tests/compiletests/ui/dis/powi.rs | 1 - tests/compiletests/ui/dis/powi.stderr | 12 +++++----- 9 files changed, 30 insertions(+), 44 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs index b2b50694428..975332690d1 100644 --- a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs +++ b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs @@ -3389,8 +3389,6 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> { let libm_intrinsic = instance_def_id.and_then(|def_id| self.libm_intrinsics.borrow().get(&def_id).copied()); - let num_traits_intrinsics = instance_def_id - .and_then(|def_id| self.num_traits_intrinsics.borrow().get(&def_id).copied()); let buffer_load_intrinsic = instance_def_id .is_some_and(|def_id| self.buffer_load_intrinsics.borrow().contains(&def_id)); let buffer_store_intrinsic = instance_def_id @@ -3400,7 +3398,7 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> { let from_trait_impl = instance_def_id.and_then(|def_id| self.from_trait_impls.borrow().get(&def_id).copied()); - if let Some(libm_intrinsic) = libm_intrinsic.or(num_traits_intrinsics) { + if let Some(libm_intrinsic) = libm_intrinsic { let result = self.call_libm_intrinsic(libm_intrinsic, result_type, args); if result_type != result.ty { bug!( diff --git a/crates/rustc_codegen_spirv/src/builder/libm_intrinsics.rs b/crates/rustc_codegen_spirv/src/builder/libm_intrinsics.rs index e723b752d98..926d161baa9 100644 --- a/crates/rustc_codegen_spirv/src/builder/libm_intrinsics.rs +++ b/crates/rustc_codegen_spirv/src/builder/libm_intrinsics.rs @@ -30,7 +30,6 @@ pub enum LibmCustomIntrinsic { Tgamma, Log1p, NextAfter, - Powi, Remainder, RemQuo, Scalbn, @@ -199,9 +198,6 @@ pub const LIBM_TABLE: &[(&str, LibmIntrinsic)] = &[ ("truncf", LibmIntrinsic::GLOp(GLOp::Trunc)), ]; -pub const NUM_TRAITS_TABLE: &[(&str, LibmIntrinsic)] = - &[("powi", LibmIntrinsic::Custom(LibmCustomIntrinsic::Powi))]; - impl Builder<'_, '_> { pub fn call_libm_intrinsic( &mut self, @@ -310,12 +306,6 @@ impl Builder<'_, '_> { LibmIntrinsic::Custom(LibmCustomIntrinsic::NextAfter) => { self.undef_zombie(result_type, "NextAfter not supported yet") } - LibmIntrinsic::Custom(LibmCustomIntrinsic::Powi) => { - assert_eq!(args.len(), 2); - // Convert integer exponent to float, then use GLOp::Pow - let float_exp = self.sitofp(args[1], args[0].ty); - self.gl_op(GLOp::Pow, result_type, [args[0], float_exp]) - } LibmIntrinsic::Custom(LibmCustomIntrinsic::Remainder) => { self.undef_zombie(result_type, "Remainder not supported yet") } diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs b/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs index 15c064c250c..b01f6761eb5 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs @@ -181,16 +181,6 @@ impl<'tcx> CodegenCx<'tcx> { } } - // Check for usage of `num_traits` intrinsics (like Float::powi) that we can optimize - if self.tcx.crate_name(def_id.krate) == self.sym.num_traits && !def_id.is_local() { - let item_name = self.tcx.item_name(def_id); - if let Some(&intrinsic) = self.sym.num_traits_intrinsics.get(&item_name) { - self.num_traits_intrinsics - .borrow_mut() - .insert(def_id, intrinsic); - } - } - // Check if this is a From trait implementation if let Some(impl_def_id) = self.tcx.impl_of_assoc(def_id) && let Some(trait_ref) = self.tcx.impl_opt_trait_ref(impl_def_id) diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs b/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs index f08cdb819f8..a22dbf02d6e 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs @@ -89,8 +89,6 @@ pub struct CodegenCx<'tcx> { // FIXME(eddyb) should the maps exist at all, now that the `DefId` is known // at `call` time, and presumably its high-level details can be looked up? pub libm_intrinsics: RefCell>, - pub num_traits_intrinsics: - RefCell>, /// All `panic!(...)`s and builtin panics (from MIR `Assert`s) call into one /// of these lang items, which we always replace with an "abort". @@ -239,7 +237,6 @@ impl<'tcx> CodegenCx<'tcx> { sym, instruction_table: InstructionTable::new(), libm_intrinsics: Default::default(), - num_traits_intrinsics: Default::default(), panic_entry_points: Default::default(), fmt_args_new_fn_ids: Default::default(), fmt_rt_arg_new_fn_ids_to_ty_and_spec: Default::default(), diff --git a/crates/rustc_codegen_spirv/src/symbols.rs b/crates/rustc_codegen_spirv/src/symbols.rs index bae57f976c0..badba9a8502 100644 --- a/crates/rustc_codegen_spirv/src/symbols.rs +++ b/crates/rustc_codegen_spirv/src/symbols.rs @@ -18,7 +18,6 @@ pub struct Symbols { pub vector: Symbol, pub v1: Symbol, pub libm: Symbol, - pub num_traits: Symbol, pub entry_point_name: Symbol, pub spv_khr_vulkan_memory_model: Symbol, @@ -34,7 +33,6 @@ pub struct Symbols { pub attributes: FxHashMap, pub execution_modes: FxHashMap, pub libm_intrinsics: FxHashMap, - pub num_traits_intrinsics: FxHashMap, } const BUILTINS: &[(&str, BuiltIn)] = { @@ -416,12 +414,6 @@ impl Symbols { assert!(old.is_none()); } - let mut num_traits_intrinsics = FxHashMap::default(); - for &(a, b) in libm_intrinsics::NUM_TRAITS_TABLE { - let old = num_traits_intrinsics.insert(Symbol::intern(a), b); - assert!(old.is_none()); - } - Self { discriminant: Symbol::intern("discriminant"), rust_gpu: Symbol::intern("rust_gpu"), @@ -429,7 +421,6 @@ impl Symbols { vector: Symbol::intern("vector"), v1: Symbol::intern("v1"), libm: Symbol::intern("libm"), - num_traits: Symbol::intern("num_traits"), entry_point_name: Symbol::intern("entry_point_name"), spv_khr_vulkan_memory_model: Symbol::intern("SPV_KHR_vulkan_memory_model"), @@ -445,7 +436,6 @@ impl Symbols { attributes, execution_modes, libm_intrinsics, - num_traits_intrinsics, } } diff --git a/crates/spirv-std/src/float.rs b/crates/spirv-std/src/float.rs index 2c58992811e..0f957c28416 100644 --- a/crates/spirv-std/src/float.rs +++ b/crates/spirv-std/src/float.rs @@ -3,6 +3,28 @@ use crate::glam::{Vec2, Vec4}; #[cfg(target_arch = "spirv")] use core::arch::asm; +#[cfg(target_arch = "spirv")] +use core::intrinsics; + +#[cfg(target_arch = "spirv")] +impl f32 { + /// Raises a number to an integer power. + #[inline] + #[rustc_allow_incoherent_impl] + pub fn powi(self, n: i32) -> f32 { + intrinsics::powif32(self, n) + } +} + +#[cfg(target_arch = "spirv")] +impl f64 { + /// Raises a number to an integer power. + #[inline] + #[rustc_allow_incoherent_impl] + pub fn powi(self, n: i32) -> f64 { + intrinsics::powif64(self, n) + } +} /// Converts two f32 values (floats) into two f16 values (halfs). The result is a u32, with the low /// 16 bits being the first f16, and the high 16 bits being the second f16. diff --git a/crates/spirv-std/src/lib.rs b/crates/spirv-std/src/lib.rs index f3c19b2eac4..b1aa707db5c 100644 --- a/crates/spirv-std/src/lib.rs +++ b/crates/spirv-std/src/lib.rs @@ -2,7 +2,7 @@ #![cfg_attr( target_arch = "spirv", allow(internal_features), - feature(asm_experimental_arch, lang_items) + feature(asm_experimental_arch, core_intrinsics, lang_items, rustc_attrs) )] // FIXME(eddyb) update/review these lints. // diff --git a/tests/compiletests/ui/dis/powi.rs b/tests/compiletests/ui/dis/powi.rs index dd179529ebc..93fa5b6850d 100644 --- a/tests/compiletests/ui/dis/powi.rs +++ b/tests/compiletests/ui/dis/powi.rs @@ -4,7 +4,6 @@ // build-pass // compile-flags: -C llvm-args=--disassemble-entry=main -use spirv_std::num_traits::Float; use spirv_std::spirv; #[spirv(fragment)] diff --git a/tests/compiletests/ui/dis/powi.stderr b/tests/compiletests/ui/dis/powi.stderr index d815ceb0bd7..4d189108626 100644 --- a/tests/compiletests/ui/dis/powi.stderr +++ b/tests/compiletests/ui/dis/powi.stderr @@ -1,12 +1,12 @@ %1 = OpFunction %2 None %3 %4 = OpLabel - OpLine %5 11 12 + OpLine %5 10 12 %6 = OpLoad %7 %8 - OpLine %5 12 20 - %9 = OpConvertSToF %7 %10 - %11 = OpExtInst %7 %12 26 %6 %9 - OpLine %5 12 4 - OpStore %13 %11 + OpLine %9 15 8 + %10 = OpConvertSToF %7 %11 + %12 = OpExtInst %7 %13 26 %6 %10 + OpLine %5 11 4 + OpStore %14 %12 OpNoLine OpReturn OpFunctionEnd