@@ -7,6 +7,25 @@ use rustc_session::Session;
7
7
use std:: iter:: once;
8
8
use std:: mem:: take;
9
9
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
+
10
29
pub fn shift_ids ( module : & mut Module , add : u32 ) {
11
30
module. all_inst_iter_mut ( ) . for_each ( |inst| {
12
31
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<()> {
266
285
}
267
286
}
268
287
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
+
269
393
/// Remove all [`Decoration::NonUniform`] if this module does *not* have [`Capability::ShaderNonUniform`].
270
394
/// This allows image asm to always declare `NonUniform` and not worry about conditional compilation.
271
395
pub fn remove_non_uniform_decorations ( _sess : & Session , module : & mut Module ) -> Result < ( ) > {
0 commit comments