diff --git a/crates/rustc_codegen_spirv/src/builder/ext_inst.rs b/crates/rustc_codegen_spirv/src/builder/ext_inst.rs index e0dec8cf2d..7c4997de76 100644 --- a/crates/rustc_codegen_spirv/src/builder/ext_inst.rs +++ b/crates/rustc_codegen_spirv/src/builder/ext_inst.rs @@ -1,8 +1,8 @@ use super::Builder; use crate::builder_spirv::{SpirvValue, SpirvValueExt}; use crate::custom_insts; +use rspirv::dr::Operand; use rspirv::spirv::{GLOp, Word}; -use rspirv::{dr::Operand, spirv::Capability}; const GLSL_STD_450: &str = "GLSL.std.450"; @@ -13,7 +13,6 @@ pub struct ExtInst { custom: Option, glsl: Option, - integer_functions_2_intel: bool, } impl ExtInst { @@ -38,32 +37,11 @@ impl ExtInst { id } } - - pub fn require_integer_functions_2_intel(&mut self, bx: &Builder<'_, '_>, to_zombie: Word) { - if !self.integer_functions_2_intel { - self.integer_functions_2_intel = true; - if !bx - .builder - .has_capability(Capability::IntegerFunctions2INTEL) - { - bx.zombie(to_zombie, "capability IntegerFunctions2INTEL is required"); - } - if !bx - .builder - .has_extension(bx.sym.spv_intel_shader_integer_functions2) - { - bx.zombie( - to_zombie, - "extension SPV_INTEL_shader_integer_functions2 is required", - ); - } - } - } } impl<'a, 'tcx> Builder<'a, 'tcx> { pub fn custom_inst( - &mut self, + &self, result_type: Word, inst: custom_insts::CustomInst, ) -> SpirvValue { @@ -80,12 +58,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .with_type(result_type) } - pub fn gl_op( - &mut self, - op: GLOp, - result_type: Word, - args: impl AsRef<[SpirvValue]>, - ) -> SpirvValue { + pub fn gl_op(&self, op: GLOp, result_type: Word, args: impl AsRef<[SpirvValue]>) -> SpirvValue { let args = args.as_ref(); let glsl = self.ext_inst.borrow_mut().import_glsl(self); self.emit() diff --git a/crates/rustc_codegen_spirv/src/builder/intrinsics.rs b/crates/rustc_codegen_spirv/src/builder/intrinsics.rs index 45757888e9..9fbbf4106e 100644 --- a/crates/rustc_codegen_spirv/src/builder/intrinsics.rs +++ b/crates/rustc_codegen_spirv/src/builder/intrinsics.rs @@ -211,46 +211,15 @@ impl<'a, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'tcx> { self.rotate(val, shift, is_left) } - // TODO: Do we want to manually implement these instead of using intel instructions? - sym::ctlz | sym::ctlz_nonzero => { - let result = self - .emit() - .u_count_leading_zeros_intel( - args[0].immediate().ty, - None, - args[0].immediate().def(self), - ) - .unwrap(); - self.ext_inst - .borrow_mut() - .require_integer_functions_2_intel(self, result); - result.with_type(args[0].immediate().ty) - } - sym::cttz | sym::cttz_nonzero => { - let result = self - .emit() - .u_count_trailing_zeros_intel( - args[0].immediate().ty, - None, - args[0].immediate().def(self), - ) - .unwrap(); - self.ext_inst - .borrow_mut() - .require_integer_functions_2_intel(self, result); - result.with_type(args[0].immediate().ty) + sym::ctlz => self.count_leading_trailing_zeros(args[0].immediate(), false, false), + sym::ctlz_nonzero => { + self.count_leading_trailing_zeros(args[0].immediate(), false, true) } + sym::cttz => self.count_leading_trailing_zeros(args[0].immediate(), true, false), + sym::cttz_nonzero => self.count_leading_trailing_zeros(args[0].immediate(), true, true), - sym::ctpop => self - .emit() - .bit_count(args[0].immediate().ty, None, args[0].immediate().def(self)) - .unwrap() - .with_type(args[0].immediate().ty), - sym::bitreverse => self - .emit() - .bit_reverse(args[0].immediate().ty, None, args[0].immediate().def(self)) - .unwrap() - .with_type(args[0].immediate().ty), + sym::ctpop => self.count_ones(args[0].immediate()), + sym::bitreverse => self.bit_reverse(args[0].immediate()), sym::bswap => { // https://github.com/KhronosGroup/SPIRV-LLVM/pull/221/files // TODO: Definitely add tests to make sure this impl is right. @@ -398,6 +367,244 @@ impl<'a, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'tcx> { } impl Builder<'_, '_> { + pub fn count_ones(&self, arg: SpirvValue) -> SpirvValue { + let ty = arg.ty; + match self.cx.lookup_type(ty) { + SpirvType::Integer(bits, false) => { + let u32 = SpirvType::Integer(32, false).def(self.span(), self); + + match bits { + 8 | 16 => { + let arg = arg.def(self); + let arg = self.emit().u_convert(u32, None, arg).unwrap(); + self.emit().bit_count(u32, None, arg).unwrap() + } + 32 => self.emit().bit_count(u32, None, arg.def(self)).unwrap(), + 64 => { + let u32_32 = self.constant_u32(self.span(), 32).def(self); + let arg = arg.def(self); + let lower = self.emit().u_convert(u32, None, arg).unwrap(); + let higher = self + .emit() + .shift_right_logical(ty, None, arg, u32_32) + .unwrap(); + let higher = self.emit().u_convert(u32, None, higher).unwrap(); + + let lower_bits = self.emit().bit_count(u32, None, lower).unwrap(); + let higher_bits = self.emit().bit_count(u32, None, higher).unwrap(); + self.emit() + .i_add(u32, None, lower_bits, higher_bits) + .unwrap() + } + _ => { + let undef = self.undef(ty).def(self); + self.zombie( + undef, + &format!("count_ones() on unsupported {ty:?} bit integer type"), + ); + undef + } + } + .with_type(u32) + } + _ => self.fatal(format!( + "count_ones() expected an unsigned integer type, got {:?}", + self.cx.lookup_type(ty) + )), + } + } + + pub fn bit_reverse(&self, arg: SpirvValue) -> SpirvValue { + let ty = arg.ty; + match self.cx.lookup_type(ty) { + SpirvType::Integer(bits, false) => { + let u32 = SpirvType::Integer(32, false).def(self.span(), self); + let uint = SpirvType::Integer(bits, false).def(self.span(), self); + + match bits { + 8 | 16 => { + let arg = arg.def(self); + let arg = self.emit().u_convert(u32, None, arg).unwrap(); + + let reverse = self.emit().bit_reverse(u32, None, arg).unwrap(); + let shift = self.constant_u32(self.span(), 32 - bits).def(self); + let reverse = self + .emit() + .shift_right_logical(u32, None, reverse, shift) + .unwrap(); + self.emit().u_convert(uint, None, reverse).unwrap() + } + 32 => self.emit().bit_reverse(u32, None, arg.def(self)).unwrap(), + 64 => { + let u32_32 = self.constant_u32(self.span(), 32).def(self); + let arg = arg.def(self); + let lower = self.emit().u_convert(u32, None, arg).unwrap(); + let higher = self + .emit() + .shift_right_logical(ty, None, arg, u32_32) + .unwrap(); + let higher = self.emit().u_convert(u32, None, higher).unwrap(); + + // note that higher and lower have swapped + let higher_bits = self.emit().bit_reverse(u32, None, lower).unwrap(); + let lower_bits = self.emit().bit_reverse(u32, None, higher).unwrap(); + + let higher_bits = self.emit().u_convert(uint, None, higher_bits).unwrap(); + let higher_bits = self + .emit() + .shift_left_logical(uint, None, higher_bits, u32_32) + .unwrap(); + let lower_bits = self.emit().u_convert(uint, None, lower_bits).unwrap(); + + self.emit() + .bitwise_or(ty, None, lower_bits, higher_bits) + .unwrap() + } + _ => { + let undef = self.undef(ty).def(self); + self.zombie( + undef, + &format!("bit_reverse() on unsupported {ty:?} bit integer type"), + ); + undef + } + } + .with_type(ty) + } + _ => self.fatal(format!( + "bit_reverse() expected an unsigned integer type, got {:?}", + self.cx.lookup_type(ty) + )), + } + } + + pub fn count_leading_trailing_zeros( + &self, + arg: SpirvValue, + trailing: bool, + non_zero: bool, + ) -> SpirvValue { + let ty = arg.ty; + match self.cx.lookup_type(ty) { + SpirvType::Integer(bits, false) => { + let bool = SpirvType::Bool.def(self.span(), self); + let u32 = SpirvType::Integer(32, false).def(self.span(), self); + + let glsl = self.ext_inst.borrow_mut().import_glsl(self); + let find_xsb = |arg| { + if trailing { + self.emit() + .ext_inst(u32, None, glsl, GLOp::FindILsb as u32, [Operand::IdRef( + arg, + )]) + .unwrap() + } else { + // rust is always unsigned, so FindUMsb + let msb_bit = self + .emit() + .ext_inst(u32, None, glsl, GLOp::FindUMsb as u32, [Operand::IdRef( + arg, + )]) + .unwrap(); + // the glsl op returns the Msb bit, not the amount of leading zeros of this u32 + // leading zeros = 31 - Msb bit + let u32_31 = self.constant_u32(self.span(), 31).def(self); + self.emit().i_sub(u32, None, u32_31, msb_bit).unwrap() + } + }; + + let converted = match bits { + 8 | 16 => { + if trailing { + let arg = self.emit().u_convert(u32, None, arg.def(self)).unwrap(); + find_xsb(arg) + } else { + let arg = arg.def(self); + let arg = self.emit().u_convert(u32, None, arg).unwrap(); + let xsb = find_xsb(arg); + let subtrahend = self.constant_u32(self.span(), 32 - bits).def(self); + self.emit().i_sub(u32, None, xsb, subtrahend).unwrap() + } + } + 32 => find_xsb(arg.def(self)), + 64 => { + let u32_0 = self.constant_int(u32, 0).def(self); + let u32_32 = self.constant_u32(self.span(), 32).def(self); + + let arg = arg.def(self); + let lower = self.emit().u_convert(u32, None, arg).unwrap(); + let higher = self + .emit() + .shift_right_logical(ty, None, arg, u32_32) + .unwrap(); + let higher = self.emit().u_convert(u32, None, higher).unwrap(); + + let lower_bits = find_xsb(lower); + let higher_bits = find_xsb(higher); + + if trailing { + let use_lower = self.emit().i_equal(bool, None, higher, u32_0).unwrap(); + let lower_bits = + self.emit().i_add(u32, None, lower_bits, u32_32).unwrap(); + self.emit() + .select(u32, None, use_lower, lower_bits, higher_bits) + .unwrap() + } else { + let use_higher = self.emit().i_equal(bool, None, lower, u32_0).unwrap(); + let higher_bits = + self.emit().i_add(u32, None, higher_bits, u32_32).unwrap(); + self.emit() + .select(u32, None, use_higher, higher_bits, lower_bits) + .unwrap() + } + } + _ => { + let undef = self.undef(ty).def(self); + self.zombie(undef, &format!( + "count_leading_trailing_zeros() on unsupported {ty:?} bit integer type" + )); + undef + } + }; + + if non_zero { + converted + } else { + let int_0 = self.constant_int(ty, 0).def(self); + let int_bits = self.constant_int(u32, bits as u128).def(self); + let is_0 = self + .emit() + .i_equal(bool, None, arg.def(self), int_0) + .unwrap(); + self.emit() + .select(u32, None, is_0, int_bits, converted) + .unwrap() + } + .with_type(u32) + } + SpirvType::Integer(bits, true) => { + // rustc wants `[i8,i16,i32,i64]::leading_zeros()` with `non_zero: true` for some reason. I do not know + // how these are reachable, marking them as zombies makes none of our compiletests fail. + let unsigned = SpirvType::Integer(bits, false).def(self.span(), self); + let arg = self + .emit() + .bitcast(unsigned, None, arg.def(self)) + .unwrap() + .with_type(unsigned); + let result = self.count_leading_trailing_zeros(arg, trailing, non_zero); + self.emit() + .bitcast(ty, None, result.def(self)) + .unwrap() + .with_type(ty) + } + e => { + self.fatal(format!( + "count_leading_trailing_zeros(trailing: {trailing}, non_zero: {non_zero}) expected an integer type, got {e:?}", + )); + } + } + } + pub fn abort_with_kind_and_message_debug_printf( &mut self, kind: &str, diff --git a/crates/rustc_codegen_spirv/src/builder_spirv.rs b/crates/rustc_codegen_spirv/src/builder_spirv.rs index 3c91b48872..4c87f0584d 100644 --- a/crates/rustc_codegen_spirv/src/builder_spirv.rs +++ b/crates/rustc_codegen_spirv/src/builder_spirv.rs @@ -425,7 +425,6 @@ pub struct BuilderSpirv<'tcx> { debug_file_cache: RefCell>>, enabled_capabilities: FxHashSet, - enabled_extensions: FxHashSet, } impl<'tcx> BuilderSpirv<'tcx> { @@ -443,7 +442,6 @@ impl<'tcx> BuilderSpirv<'tcx> { builder.module_mut().header.as_mut().unwrap().generator = 0x001B_0000; let mut enabled_capabilities = FxHashSet::default(); - let mut enabled_extensions = FxHashSet::default(); fn add_cap( builder: &mut Builder, @@ -455,11 +453,10 @@ impl<'tcx> BuilderSpirv<'tcx> { builder.capability(cap); enabled_capabilities.insert(cap); } - fn add_ext(builder: &mut Builder, enabled_extensions: &mut FxHashSet, ext: Symbol) { + fn add_ext(builder: &mut Builder, ext: Symbol) { // This should be the only callsite of Builder::extension (aside from tests), to make // sure the hashset stays in sync. builder.extension(ext.as_str()); - enabled_extensions.insert(ext); } for feature in features { @@ -468,7 +465,7 @@ impl<'tcx> BuilderSpirv<'tcx> { add_cap(&mut builder, &mut enabled_capabilities, cap); } TargetFeature::Extension(ext) => { - add_ext(&mut builder, &mut enabled_extensions, ext); + add_ext(&mut builder, ext); } } } @@ -476,11 +473,7 @@ impl<'tcx> BuilderSpirv<'tcx> { add_cap(&mut builder, &mut enabled_capabilities, Capability::Shader); if memory_model == MemoryModel::Vulkan { if version < (1, 5) { - add_ext( - &mut builder, - &mut enabled_extensions, - sym.spv_khr_vulkan_memory_model, - ); + add_ext(&mut builder, sym.spv_khr_vulkan_memory_model); } add_cap( &mut builder, @@ -502,7 +495,6 @@ impl<'tcx> BuilderSpirv<'tcx> { id_to_const: Default::default(), debug_file_cache: Default::default(), enabled_capabilities, - enabled_extensions, } } @@ -541,10 +533,6 @@ impl<'tcx> BuilderSpirv<'tcx> { self.enabled_capabilities.contains(&capability) } - pub fn has_extension(&self, extension: Symbol) -> bool { - self.enabled_extensions.contains(&extension) - } - pub fn select_function_by_id(&self, id: Word) -> BuilderCursor { let mut builder = self.builder.borrow_mut(); for (index, func) in builder.module_ref().functions.iter().enumerate() { diff --git a/crates/rustc_codegen_spirv/src/symbols.rs b/crates/rustc_codegen_spirv/src/symbols.rs index dd1d2b397c..e3d4dc2232 100644 --- a/crates/rustc_codegen_spirv/src/symbols.rs +++ b/crates/rustc_codegen_spirv/src/symbols.rs @@ -21,7 +21,6 @@ pub struct Symbols { pub spirv: Symbol, pub libm: Symbol, pub entry_point_name: Symbol, - pub spv_intel_shader_integer_functions2: Symbol, pub spv_khr_vulkan_memory_model: Symbol, descriptor_set: Symbol, @@ -411,9 +410,6 @@ impl Symbols { spirv: Symbol::intern("spirv"), libm: Symbol::intern("libm"), entry_point_name: Symbol::intern("entry_point_name"), - spv_intel_shader_integer_functions2: Symbol::intern( - "SPV_INTEL_shader_integer_functions2", - ), spv_khr_vulkan_memory_model: Symbol::intern("SPV_KHR_vulkan_memory_model"), descriptor_set: Symbol::intern("descriptor_set"), diff --git a/tests/ui/lang/u32/bit_reverse.rs b/tests/ui/lang/u32/bit_reverse.rs new file mode 100644 index 0000000000..41bfa8aac1 --- /dev/null +++ b/tests/ui/lang/u32/bit_reverse.rs @@ -0,0 +1,69 @@ +// Test all trailing and leading zeros. No need to test ones, they just call the zero variant with !value + +// build-pass + +use spirv_std::spirv; + +#[spirv(fragment)] +pub fn count_zeros_u8( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &u8, + out: &mut u8, +) { + *out = u8::reverse_bits(*buffer); +} + +#[spirv(fragment)] +pub fn count_zeros_u16( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &u16, + out: &mut u16, +) { + *out = u16::reverse_bits(*buffer); +} + +#[spirv(fragment)] +pub fn count_zeros_u32( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &u32, + out: &mut u32, +) { + *out = u32::reverse_bits(*buffer); +} + +#[spirv(fragment)] +pub fn count_zeros_u64( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &u64, + out: &mut u64, +) { + *out = u64::reverse_bits(*buffer); +} + +#[spirv(fragment)] +pub fn count_zeros_i8( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &i8, + out: &mut i8, +) { + *out = i8::reverse_bits(*buffer); +} + +#[spirv(fragment)] +pub fn count_zeros_i16( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &i16, + out: &mut i16, +) { + *out = i16::reverse_bits(*buffer); +} + +#[spirv(fragment)] +pub fn count_zeros_i32( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &i32, + out: &mut i32, +) { + *out = i32::reverse_bits(*buffer); +} + +#[spirv(fragment)] +pub fn count_zeros_i64( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &i64, + out: &mut i64, +) { + *out = i64::reverse_bits(*buffer); +} diff --git a/tests/ui/lang/u32/count_ones.rs b/tests/ui/lang/u32/count_ones.rs new file mode 100644 index 0000000000..11058eea4c --- /dev/null +++ b/tests/ui/lang/u32/count_ones.rs @@ -0,0 +1,69 @@ +// Test all trailing and leading zeros. No need to test ones, they just call the zero variant with !value + +// build-pass + +use spirv_std::spirv; + +#[spirv(fragment)] +pub fn count_ones_u8( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &u8, + out: &mut u32, +) { + *out = u8::count_ones(*buffer); +} + +#[spirv(fragment)] +pub fn count_ones_u16( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &u16, + out: &mut u32, +) { + *out = u16::count_ones(*buffer); +} + +#[spirv(fragment)] +pub fn count_ones_u32( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &u32, + out: &mut u32, +) { + *out = u32::count_ones(*buffer); +} + +#[spirv(fragment)] +pub fn count_ones_u64( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &u64, + out: &mut u32, +) { + *out = u64::count_ones(*buffer); +} + +#[spirv(fragment)] +pub fn count_ones_i8( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &i8, + out: &mut u32, +) { + *out = i8::count_ones(*buffer); +} + +#[spirv(fragment)] +pub fn count_ones_i16( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &i16, + out: &mut u32, +) { + *out = i16::count_ones(*buffer); +} + +#[spirv(fragment)] +pub fn count_ones_i32( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &i32, + out: &mut u32, +) { + *out = i32::count_ones(*buffer); +} + +#[spirv(fragment)] +pub fn count_ones_i64( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &i64, + out: &mut u32, +) { + *out = i64::count_ones(*buffer); +} diff --git a/tests/ui/lang/u32/leading_zeros.rs b/tests/ui/lang/u32/leading_zeros.rs new file mode 100644 index 0000000000..d157dfe73f --- /dev/null +++ b/tests/ui/lang/u32/leading_zeros.rs @@ -0,0 +1,69 @@ +// Test all trailing and leading zeros. No need to test ones, they just call the zero variant with !value + +// build-pass + +use spirv_std::spirv; + +#[spirv(fragment)] +pub fn leading_zeros_u8( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &u8, + out: &mut u32, +) { + *out = u8::leading_zeros(*buffer); +} + +#[spirv(fragment)] +pub fn leading_zeros_u16( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &u16, + out: &mut u32, +) { + *out = u16::leading_zeros(*buffer); +} + +#[spirv(fragment)] +pub fn leading_zeros_u32( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &u32, + out: &mut u32, +) { + *out = u32::leading_zeros(*buffer); +} + +#[spirv(fragment)] +pub fn leading_zeros_u64( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &u64, + out: &mut u32, +) { + *out = u64::leading_zeros(*buffer); +} + +#[spirv(fragment)] +pub fn leading_zeros_i8( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &i8, + out: &mut u32, +) { + *out = i8::leading_zeros(*buffer); +} + +#[spirv(fragment)] +pub fn leading_zeros_i16( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &i16, + out: &mut u32, +) { + *out = i16::leading_zeros(*buffer); +} + +#[spirv(fragment)] +pub fn leading_zeros_i32( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &i32, + out: &mut u32, +) { + *out = i32::leading_zeros(*buffer); +} + +#[spirv(fragment)] +pub fn leading_zeros_i64( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &i64, + out: &mut u32, +) { + *out = i64::leading_zeros(*buffer); +} diff --git a/tests/ui/lang/u32/trailing_zeros.rs b/tests/ui/lang/u32/trailing_zeros.rs new file mode 100644 index 0000000000..314d60d323 --- /dev/null +++ b/tests/ui/lang/u32/trailing_zeros.rs @@ -0,0 +1,69 @@ +// Test all trailing and leading zeros. No need to test ones, they just call the zero variant with !value + +// build-pass + +use spirv_std::spirv; + +#[spirv(fragment)] +pub fn trailing_zeros_u8( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &u8, + out: &mut u32, +) { + *out = u8::trailing_zeros(*buffer); +} + +#[spirv(fragment)] +pub fn trailing_zeros_u16( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &u16, + out: &mut u32, +) { + *out = u16::trailing_zeros(*buffer); +} + +#[spirv(fragment)] +pub fn trailing_zeros_u32( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &u32, + out: &mut u32, +) { + *out = u32::trailing_zeros(*buffer); +} + +#[spirv(fragment)] +pub fn trailing_zeros_u64( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &u64, + out: &mut u32, +) { + *out = u64::trailing_zeros(*buffer); +} + +#[spirv(fragment)] +pub fn trailing_zeros_i8( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &i8, + out: &mut u32, +) { + *out = i8::trailing_zeros(*buffer); +} + +#[spirv(fragment)] +pub fn trailing_zeros_i16( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &i16, + out: &mut u32, +) { + *out = i16::trailing_zeros(*buffer); +} + +#[spirv(fragment)] +pub fn trailing_zeros_i32( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &i32, + out: &mut u32, +) { + *out = i32::trailing_zeros(*buffer); +} + +#[spirv(fragment)] +pub fn trailing_zeros_i64( + #[spirv(descriptor_set = 0, binding = 0, storage_buffer)] buffer: &i64, + out: &mut u32, +) { + *out = i64::trailing_zeros(*buffer); +}