diff --git a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs index d86db1cbd00..bab81620165 100644 --- a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs +++ b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs @@ -37,6 +37,8 @@ use std::ops::{BitAnd, BitOr, BitXor, Not, RangeInclusive}; use tracing::{Level, instrument, span}; use tracing::{trace, warn}; +const LARGE_ARRAY_MEMSET_WARN_THRESHOLD: usize = 1024; + enum ConstValue { Unsigned(u128), Signed(i128), @@ -425,8 +427,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .def(self) } SpirvType::Array { element, count } => { + if fill_byte == 0 { + return self.constant_null(ty.def(self.span(), self)).def(self); + } let elem_pat = self.memset_const_pattern(&self.lookup_type(element), fill_byte); let count = self.builder.lookup_const_scalar(count).unwrap() as usize; + if count > LARGE_ARRAY_MEMSET_WARN_THRESHOLD { + self.warn(format!( + "large array of {count} elements with a non-zero fill will generate a \ + SPIR-V constant with {count} operands, which may be slow to compile; \ + consider using a storage buffer for large data instead" + )); + } self.constant_composite(ty.def(self.span(), self), iter::repeat_n(elem_pat, count)) .def(self) } @@ -1928,6 +1940,13 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> { count: u64, dest: PlaceRef<'tcx, Self::Value>, ) { + if count > LARGE_ARRAY_MEMSET_WARN_THRESHOLD as u64 { + self.warn(format!( + "large array of {count} elements with a non-zero fill will generate {count} \ + SPIR-V store instructions, which may be slow to compile; \ + consider using a storage buffer for large data instead" + )); + } let zero = self.const_usize(0); let start = dest.project_index(self, zero).val.llval; diff --git a/crates/rustc_codegen_spirv/src/builder/mod.rs b/crates/rustc_codegen_spirv/src/builder/mod.rs index ba4af829a98..8ab3f8b8e5b 100644 --- a/crates/rustc_codegen_spirv/src/builder/mod.rs +++ b/crates/rustc_codegen_spirv/src/builder/mod.rs @@ -101,6 +101,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + #[track_caller] + pub fn warn(&self, msg: impl Into) { + if let Some(current_span) = self.current_span { + self.tcx.dcx().span_warn(current_span, msg); + } else { + self.tcx.dcx().warn(msg); + } + } + #[track_caller] pub fn err(&self, msg: impl Into) { if let Some(current_span) = self.current_span { diff --git a/tests/compiletests/ui/lang/consts/large_array_nonzero_fill.rs b/tests/compiletests/ui/lang/consts/large_array_nonzero_fill.rs new file mode 100644 index 00000000000..0659f9ff148 --- /dev/null +++ b/tests/compiletests/ui/lang/consts/large_array_nonzero_fill.rs @@ -0,0 +1,17 @@ +// Tests that large non-zero-initialized arrays emit a compile warning. +// A `[v; N]` array with non-zero v generates N SPIR-V instructions (either +// store ops or OpConstantComposite operands), which is inherently slow. + +// build-pass + +#![no_std] +use spirv_std::glam::Vec4; +use spirv_std::spirv; + +#[spirv(vertex)] +pub fn test_vs(#[spirv(push_constant)] index: &u32, #[spirv(position)] out_pos: &mut Vec4) { + let nonzeroed = [1.0f32; 2048]; + + let i = *index as usize % 2048; + *out_pos = Vec4::new(nonzeroed[i], nonzeroed[i], 0.0, 1.0); +} diff --git a/tests/compiletests/ui/lang/consts/large_array_nonzero_fill.stderr b/tests/compiletests/ui/lang/consts/large_array_nonzero_fill.stderr new file mode 100644 index 00000000000..ba82de239d2 --- /dev/null +++ b/tests/compiletests/ui/lang/consts/large_array_nonzero_fill.stderr @@ -0,0 +1,8 @@ +warning: large array of 2048 elements with a non-zero fill will generate 2048 SPIR-V store instructions, which may be slow to compile; consider using a storage buffer for large data instead + --> $DIR/large_array_nonzero_fill.rs:13:21 + | +LL | let nonzeroed = [1.0f32; 2048]; + | ^^^^^^^^^^^^^^ + +warning: 1 warning emitted + diff --git a/tests/compiletests/ui/lang/consts/large_array_zero_fill.rs b/tests/compiletests/ui/lang/consts/large_array_zero_fill.rs new file mode 100644 index 00000000000..331a3bafa78 --- /dev/null +++ b/tests/compiletests/ui/lang/consts/large_array_zero_fill.rs @@ -0,0 +1,17 @@ +// Tests that zero-initialized large arrays compile without warnings. +// A `[0; N]` array goes through `memset_const_pattern` with fill_byte=0, +// which should emit a single OpConstantNull + +// build-pass + +#![no_std] +use spirv_std::glam::Vec4; +use spirv_std::spirv; + +#[spirv(vertex)] +pub fn test_vs(#[spirv(push_constant)] index: &u32, #[spirv(position)] out_pos: &mut Vec4) { + let zeroed = [0.0f32; 2048]; + + let i = *index as usize % 2048; + *out_pos = Vec4::new(zeroed[i], zeroed[i], 0.0, 1.0); +}