@@ -7,6 +7,25 @@ use rustc_session::Session;
77use std:: iter:: once;
88use std:: mem:: take;
99
10+ /// Returns the capability required for an integer type of the given width, if any.
11+ fn capability_for_int_width ( width : u32 ) -> Option < rspirv:: spirv:: Capability > {
12+ match width {
13+ 8 => Some ( rspirv:: spirv:: Capability :: Int8 ) ,
14+ 16 => Some ( rspirv:: spirv:: Capability :: Int16 ) ,
15+ 64 => Some ( rspirv:: spirv:: Capability :: Int64 ) ,
16+ _ => None ,
17+ }
18+ }
19+
20+ /// Returns the capability required for a float type of the given width, if any.
21+ fn capability_for_float_width ( width : u32 ) -> Option < rspirv:: spirv:: Capability > {
22+ match width {
23+ 16 => Some ( rspirv:: spirv:: Capability :: Float16 ) ,
24+ 64 => Some ( rspirv:: spirv:: Capability :: Float64 ) ,
25+ _ => None ,
26+ }
27+ }
28+
1029pub fn shift_ids ( module : & mut Module , add : u32 ) {
1130 module. all_inst_iter_mut ( ) . for_each ( |inst| {
1231 if let Some ( ref mut result_id) = & mut inst. result_id {
@@ -266,6 +285,111 @@ pub fn check_fragment_insts(sess: &Session, module: &Module) -> Result<()> {
266285 }
267286}
268287
288+ /// Check that types requiring specific capabilities have those capabilities declared.
289+ ///
290+ /// This function validates that if a module uses types like u8/i8 (requiring Int8),
291+ /// u16/i16 (requiring Int16), etc., the corresponding capabilities are declared.
292+ pub fn check_type_capabilities ( sess : & Session , module : & Module ) -> Result < ( ) > {
293+ use rspirv:: spirv:: Capability ;
294+
295+ // Collect declared capabilities
296+ let declared_capabilities: FxHashSet < Capability > = module
297+ . capabilities
298+ . iter ( )
299+ . map ( |inst| inst. operands [ 0 ] . unwrap_capability ( ) )
300+ . collect ( ) ;
301+
302+ let mut errors = Vec :: new ( ) ;
303+
304+ for inst in & module. types_global_values {
305+ match inst. class . opcode {
306+ Op :: TypeInt => {
307+ let width = inst. operands [ 0 ] . unwrap_literal_bit32 ( ) ;
308+ let signedness = inst. operands [ 1 ] . unwrap_literal_bit32 ( ) != 0 ;
309+ let type_name = if signedness { "i" } else { "u" } ;
310+
311+ if let Some ( required_cap) = capability_for_int_width ( width) {
312+ if !declared_capabilities. contains ( & required_cap) {
313+ errors. push ( format ! (
314+ "`{type_name}{width}` type used without `OpCapability {required_cap:?}`"
315+ ) ) ;
316+ }
317+ }
318+ }
319+ Op :: TypeFloat => {
320+ let width = inst. operands [ 0 ] . unwrap_literal_bit32 ( ) ;
321+
322+ if let Some ( required_cap) = capability_for_float_width ( width) {
323+ if !declared_capabilities. contains ( & required_cap) {
324+ errors. push ( format ! (
325+ "`f{width}` type used without `OpCapability {required_cap:?}`"
326+ ) ) ;
327+ }
328+ }
329+ }
330+ _ => { }
331+ }
332+ }
333+
334+ if !errors. is_empty ( ) {
335+ let mut err = sess
336+ . dcx ( )
337+ . struct_err ( "Missing required capabilities for types" ) ;
338+ for error in errors {
339+ err = err. with_note ( error) ;
340+ }
341+ Err ( err. emit ( ) )
342+ } else {
343+ Ok ( ( ) )
344+ }
345+ }
346+
347+ /// Remove type-related capabilities that are not required by any types in the module.
348+ ///
349+ /// This function specifically targets Int8, Int16, Int64, Float16, and Float64 capabilities,
350+ /// removing them if no types in the module require them. All other capabilities are preserved.
351+ /// This is part of the fix for issue #300 where constant casts were creating unnecessary types.
352+ pub fn remove_unused_type_capabilities ( module : & mut Module ) {
353+ use rspirv:: spirv:: Capability ;
354+
355+ // Collect type-related capabilities that are actually needed
356+ let mut needed_type_capabilities = FxHashSet :: default ( ) ;
357+
358+ // Scan all types to determine which type-related capabilities are needed
359+ for inst in & module. types_global_values {
360+ match inst. class . opcode {
361+ Op :: TypeInt => {
362+ let width = inst. operands [ 0 ] . unwrap_literal_bit32 ( ) ;
363+ if let Some ( cap) = capability_for_int_width ( width) {
364+ needed_type_capabilities. insert ( cap) ;
365+ }
366+ }
367+ Op :: TypeFloat => {
368+ let width = inst. operands [ 0 ] . unwrap_literal_bit32 ( ) ;
369+ if let Some ( cap) = capability_for_float_width ( width) {
370+ needed_type_capabilities. insert ( cap) ;
371+ }
372+ }
373+ _ => { }
374+ }
375+ }
376+
377+ // Remove only type-related capabilities that aren't needed
378+ module. capabilities . retain ( |inst| {
379+ let cap = inst. operands [ 0 ] . unwrap_capability ( ) ;
380+ match cap {
381+ // Only remove these type-related capabilities if they're not used
382+ Capability :: Int8
383+ | Capability :: Int16
384+ | Capability :: Int64
385+ | Capability :: Float16
386+ | Capability :: Float64 => needed_type_capabilities. contains ( & cap) ,
387+ // Keep all other capabilities
388+ _ => true ,
389+ }
390+ } ) ;
391+ }
392+
269393/// Remove all [`Decoration::NonUniform`] if this module does *not* have [`Capability::ShaderNonUniform`].
270394/// This allows image asm to always declare `NonUniform` and not worry about conditional compilation.
271395pub fn remove_non_uniform_decorations ( _sess : & Session , module : & mut Module ) -> Result < ( ) > {
0 commit comments