Skip to content

Commit e56cc4d

Browse files
committed
Minimal SPIR-T-based validation of extensions and capabilities (esp. IntN/FloatN).
1 parent b3d3a80 commit e56cc4d

File tree

5 files changed

+507
-87
lines changed

5 files changed

+507
-87
lines changed

crates/rustc_codegen_spirv/src/linker/mod.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -481,16 +481,6 @@ pub fn link(
481481
simple_passes::remove_non_uniform_decorations(sess, &mut output)?;
482482
}
483483

484-
{
485-
let _timer = sess.timer("link_remove_unused_type_capabilities");
486-
simple_passes::remove_unused_type_capabilities(&mut output);
487-
}
488-
489-
{
490-
let _timer = sess.timer("link_type_capability_check");
491-
simple_passes::check_type_capabilities(sess, &output)?;
492-
}
493-
494484
// NOTE(eddyb) SPIR-T pipeline is entirely limited to this block.
495485
{
496486
let (spv_words, module_or_err, lower_from_spv_timer) =
@@ -563,10 +553,16 @@ pub fn link(
563553
module,
564554
&opts.spirt_passes,
565555
|name, _module| before_pass(name),
566-
after_pass,
556+
&mut after_pass,
567557
);
568558
}
569559

560+
{
561+
let timer = before_pass("spirt_passes::validate");
562+
spirt_passes::validate::validate(module);
563+
after_pass("validate", module, timer);
564+
}
565+
570566
{
571567
let _timer = before_pass("spirt_passes::diagnostics::report_diagnostics");
572568
spirt_passes::diagnostics::report_diagnostics(sess, opts, module).map_err(
@@ -634,6 +630,11 @@ pub fn link(
634630
}
635631
}
636632

633+
{
634+
let _timer = sess.timer("link_remove_unused_type_capabilities");
635+
simple_passes::remove_unused_type_capabilities(&mut output);
636+
}
637+
637638
{
638639
let _timer = sess.timer("link_gather_all_interface_vars_from_uses");
639640
entry_interface::gather_all_interface_vars_from_uses(&mut output);

crates/rustc_codegen_spirv/src/linker/simple_passes.rs

Lines changed: 3 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -293,74 +293,14 @@ pub fn check_fragment_insts(sess: &Session, module: &Module) -> super::Result<()
293293
}
294294
}
295295

296-
/// Check that types requiring specific capabilities have those capabilities declared.
297-
///
298-
/// This function validates that if a module uses types like u8/i8 (requiring Int8),
299-
/// u16/i16 (requiring Int16), etc., the corresponding capabilities are declared.
300-
pub fn check_type_capabilities(sess: &Session, module: &Module) -> super::Result<()> {
301-
use rspirv::spirv::Capability;
302-
303-
// Collect declared capabilities
304-
let declared_capabilities: FxHashSet<Capability> = module
305-
.capabilities
306-
.iter()
307-
.map(|inst| inst.operands[0].unwrap_capability())
308-
.collect();
309-
310-
let mut missing_caps = vec![];
311-
312-
for inst in &module.types_global_values {
313-
let (prefix, width, maybe_required_cap) = match inst.class.opcode {
314-
Op::TypeInt => {
315-
let width = inst.operands[0].unwrap_literal_bit32();
316-
let signed = inst.operands[1].unwrap_literal_bit32() != 0;
317-
318-
(
319-
if signed { "i" } else { "u" },
320-
width,
321-
capability_for_int_width(width),
322-
)
323-
}
324-
Op::TypeFloat => {
325-
let width = inst.operands[0].unwrap_literal_bit32();
326-
327-
("f", width, capability_for_float_width(width))
328-
}
329-
_ => continue,
330-
};
331-
332-
match maybe_required_cap {
333-
Err(UnsupportedType) => {
334-
sess.dcx()
335-
.err(format!("`{prefix}{width}` unsupported in SPIR-V"));
336-
}
337-
Ok(Some(required_cap)) if !declared_capabilities.contains(&required_cap) => {
338-
missing_caps.push(format!(
339-
"`{prefix}{width}` type used without `OpCapability {required_cap:?}`"
340-
));
341-
}
342-
Ok(_) => {}
343-
}
344-
}
345-
346-
if !missing_caps.is_empty() {
347-
let mut err = sess
348-
.dcx()
349-
.struct_err("missing required capabilities for types");
350-
for msg in missing_caps {
351-
err.note(msg);
352-
}
353-
Err(err.emit())
354-
} else {
355-
Ok(())
356-
}
357-
}
358-
359296
/// Remove type-related capabilities that are not required by any types in the module.
360297
///
361298
/// This function specifically targets Int8, Int16, Int64, Float16, and Float64 capabilities,
362299
/// removing them if no types in the module require them. All other capabilities are preserved.
363300
/// This is part of the fix for issue #300 where constant casts were creating unnecessary types.
301+
//
302+
// FIXME(eddyb) move this to a SPIR-T pass (potentially even using sets of used
303+
// exts/caps that validation itself can collect while traversing the module).
364304
pub fn remove_unused_type_capabilities(module: &mut Module) {
365305
use rspirv::spirv::Capability;
366306

crates/rustc_codegen_spirv/src/linker/spirt_passes/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pub(crate) mod debuginfo;
55
pub(crate) mod diagnostics;
66
mod fuse_selects;
77
mod reduce;
8+
pub(crate) mod validate;
89

910
use lazy_static::lazy_static;
1011
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
@@ -63,7 +64,7 @@ macro_rules! def_spv_spec_with_extra_well_known {
6364
let spv_spec = spv::spec::Spec::get();
6465
let wk = &spv_spec.well_known;
6566

66-
let decorations = match &spv_spec.operand_kinds[wk.Decoration] {
67+
let decorations = match wk.Decoration.def() {
6768
spv::spec::OperandKindDef::ValueEnum { variants } => variants,
6869
_ => unreachable!(),
6970
};
@@ -101,7 +102,9 @@ def_spv_spec_with_extra_well_known! {
101102
OpCompositeExtract,
102103
],
103104
operand_kind: spv::spec::OperandKind = [
105+
Capability,
104106
ExecutionModel,
107+
ImageFormat,
105108
],
106109
decoration: u32 = [
107110
UserTypeGOOGLE,

0 commit comments

Comments
 (0)