From c602fec95b116b07850e1fccf8d4b64da9d632d2 Mon Sep 17 00:00:00 2001 From: Christian Legnitto Date: Wed, 21 Jan 2026 08:57:09 -0800 Subject: [PATCH 1/2] Optimize num_traits::Float::powi to use GLSL.std.450 Pow Fixes https://github.com/Rust-GPU/rust-gpu/issues/516. --- .../src/builder/libm_intrinsics.rs | 8 ++++++++ .../rustc_codegen_spirv/src/codegen_cx/declare.rs | 8 ++++++++ crates/rustc_codegen_spirv/src/symbols.rs | 2 ++ tests/compiletests/ui/dis/powi.rs | 13 +++++++++++++ tests/compiletests/ui/dis/powi.stderr | 12 ++++++++++++ 5 files changed, 43 insertions(+) create mode 100644 tests/compiletests/ui/dis/powi.rs create mode 100644 tests/compiletests/ui/dis/powi.stderr diff --git a/crates/rustc_codegen_spirv/src/builder/libm_intrinsics.rs b/crates/rustc_codegen_spirv/src/builder/libm_intrinsics.rs index eb302e87b2d..a0965c9ccbe 100644 --- a/crates/rustc_codegen_spirv/src/builder/libm_intrinsics.rs +++ b/crates/rustc_codegen_spirv/src/builder/libm_intrinsics.rs @@ -30,6 +30,7 @@ pub enum LibmCustomIntrinsic { Tgamma, Log1p, NextAfter, + Powi, Remainder, RemQuo, Scalbn, @@ -157,6 +158,7 @@ pub const TABLE: &[(&str, LibmIntrinsic)] = &[ ), ("pow", LibmIntrinsic::GLOp(GLOp::Pow)), ("powf", LibmIntrinsic::GLOp(GLOp::Pow)), + ("powi", LibmIntrinsic::Custom(LibmCustomIntrinsic::Powi)), ( "remainder", LibmIntrinsic::Custom(LibmCustomIntrinsic::Remainder), @@ -306,6 +308,12 @@ 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 af14f8b037b..ab255a8a25a 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs @@ -166,6 +166,14 @@ 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.libm_intrinsics.get(&item_name) { + self.libm_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_method(def_id) && let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) diff --git a/crates/rustc_codegen_spirv/src/symbols.rs b/crates/rustc_codegen_spirv/src/symbols.rs index 7660f37b5a6..1eb1b11dd9e 100644 --- a/crates/rustc_codegen_spirv/src/symbols.rs +++ b/crates/rustc_codegen_spirv/src/symbols.rs @@ -18,6 +18,7 @@ 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, @@ -416,6 +417,7 @@ 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"), diff --git a/tests/compiletests/ui/dis/powi.rs b/tests/compiletests/ui/dis/powi.rs new file mode 100644 index 00000000000..dd179529ebc --- /dev/null +++ b/tests/compiletests/ui/dis/powi.rs @@ -0,0 +1,13 @@ +// Test that `Float::powi` uses GLSL.std.450 Pow instead of a loop-based implementation. +// See https://github.com/Rust-GPU/rust-gpu/issues/516 + +// build-pass +// compile-flags: -C llvm-args=--disassemble-entry=main + +use spirv_std::num_traits::Float; +use spirv_std::spirv; + +#[spirv(fragment)] +pub fn main(input: f32, output: &mut f32) { + *output = input.powi(2); +} diff --git a/tests/compiletests/ui/dis/powi.stderr b/tests/compiletests/ui/dis/powi.stderr new file mode 100644 index 00000000000..d4d0b9b0e2b --- /dev/null +++ b/tests/compiletests/ui/dis/powi.stderr @@ -0,0 +1,12 @@ +%1 = OpFunction %2 None %3 +%4 = OpLabel +OpLine %5 11 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 +OpNoLine +OpReturn +OpFunctionEnd From 2ba2af5918fb6b532c9cf67a22a509be591f0b78 Mon Sep 17 00:00:00 2001 From: firestar99 Date: Mon, 2 Feb 2026 11:38:47 +0100 Subject: [PATCH 2/2] Separate tables for `libm` and `num_traits` overwrites --- .../src/builder/builder_methods.rs | 4 +++- .../src/builder/libm_intrinsics.rs | 6 ++++-- crates/rustc_codegen_spirv/src/codegen_cx/declare.rs | 6 ++++-- crates/rustc_codegen_spirv/src/codegen_cx/mod.rs | 3 +++ crates/rustc_codegen_spirv/src/symbols.rs | 11 ++++++++++- 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs index 840a206174e..d8712343ace 100644 --- a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs +++ b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs @@ -3319,6 +3319,8 @@ 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 @@ -3328,7 +3330,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 { + if let Some(libm_intrinsic) = libm_intrinsic.or(num_traits_intrinsics) { 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 a0965c9ccbe..e7fa37fe158 100644 --- a/crates/rustc_codegen_spirv/src/builder/libm_intrinsics.rs +++ b/crates/rustc_codegen_spirv/src/builder/libm_intrinsics.rs @@ -43,7 +43,7 @@ pub enum LibmIntrinsic { Custom(LibmCustomIntrinsic), } -pub const TABLE: &[(&str, LibmIntrinsic)] = &[ +pub const LIBM_TABLE: &[(&str, LibmIntrinsic)] = &[ ("acos", LibmIntrinsic::GLOp(GLOp::Acos)), ("acosf", LibmIntrinsic::GLOp(GLOp::Acos)), ("acosh", LibmIntrinsic::GLOp(GLOp::Acosh)), @@ -158,7 +158,6 @@ pub const TABLE: &[(&str, LibmIntrinsic)] = &[ ), ("pow", LibmIntrinsic::GLOp(GLOp::Pow)), ("powf", LibmIntrinsic::GLOp(GLOp::Pow)), - ("powi", LibmIntrinsic::Custom(LibmCustomIntrinsic::Powi)), ( "remainder", LibmIntrinsic::Custom(LibmCustomIntrinsic::Remainder), @@ -200,6 +199,9 @@ pub const 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, diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs b/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs index ab255a8a25a..a568b80783b 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs @@ -169,8 +169,10 @@ 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.libm_intrinsics.get(&item_name) { - self.libm_intrinsics.borrow_mut().insert(def_id, intrinsic); + if let Some(&intrinsic) = self.sym.num_traits_intrinsics.get(&item_name) { + self.num_traits_intrinsics + .borrow_mut() + .insert(def_id, intrinsic); } } diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs b/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs index d36e789b1c7..9d1382b06fa 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs @@ -66,6 +66,8 @@ 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". @@ -202,6 +204,7 @@ 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 1eb1b11dd9e..0f89af629cb 100644 --- a/crates/rustc_codegen_spirv/src/symbols.rs +++ b/crates/rustc_codegen_spirv/src/symbols.rs @@ -34,6 +34,7 @@ pub struct Symbols { pub attributes: FxHashMap, pub execution_modes: FxHashMap, pub libm_intrinsics: FxHashMap, + pub num_traits_intrinsics: FxHashMap, } const BUILTINS: &[(&str, BuiltIn)] = { @@ -406,10 +407,17 @@ impl Symbols { } let mut libm_intrinsics = FxHashMap::default(); - for &(a, b) in libm_intrinsics::TABLE { + for &(a, b) in libm_intrinsics::LIBM_TABLE { let old = libm_intrinsics.insert(Symbol::intern(a), b); 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"), @@ -433,6 +441,7 @@ impl Symbols { attributes, execution_modes, libm_intrinsics, + num_traits_intrinsics, } }