Skip to content

Commit 9a69d6d

Browse files
committed
specializer: allow unused fully-constrained interface variables.
1 parent f140464 commit 9a69d6d

File tree

5 files changed

+111
-1
lines changed

5 files changed

+111
-1
lines changed

crates/rustc_codegen_spirv/src/linker/specializer.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
5252
use crate::linker::ipo::CallGraph;
5353
use crate::spirv_type_constraints::{self, InstSig, StorageClassPat, TyListPat, TyPat};
54-
use indexmap::IndexMap;
54+
use indexmap::{IndexMap, IndexSet};
5555
use rspirv::dr::{Builder, Function, Instruction, Module, Operand};
5656
use rspirv::spirv::{Op, StorageClass, Word};
5757
use rustc_data_structures::captures::Captures;
@@ -138,6 +138,35 @@ pub fn specialize(module: Module, specialization: impl Specialization) -> Module
138138

139139
specializer.collect_generics(&module);
140140

141+
// "Generic" module-scoped variables can be fully constrained to the point
142+
// where we could theoretically always add an instance for them, in order
143+
// to preserve them, even if they would appear to otherwise be unused.
144+
// We do this here for fully-constrained variables used by `OpEntryPoint`s,
145+
// in order to avoid a failure in `Expander::expand_module` (see #723).
146+
let mut interface_concrete_instances = IndexSet::new();
147+
for inst in &module.entry_points {
148+
for interface_operand in &inst.operands[3..] {
149+
let interface_id = interface_operand.unwrap_id_ref();
150+
if let Some(generic) = specializer.generics.get(&interface_id) {
151+
if let Some(param_values) = &generic.param_values {
152+
if param_values.iter().all(|v| matches!(v, Value::Known(_))) {
153+
interface_concrete_instances.insert(Instance {
154+
generic_id: interface_id,
155+
generic_args: param_values
156+
.iter()
157+
.copied()
158+
.map(|v| match v {
159+
Value::Known(v) => v,
160+
_ => unreachable!(),
161+
})
162+
.collect(),
163+
});
164+
}
165+
}
166+
}
167+
}
168+
}
169+
141170
let call_graph = CallGraph::collect(&module);
142171
let mut non_generic_replacements = vec![];
143172
for func_idx in call_graph.post_order() {
@@ -148,6 +177,11 @@ pub fn specialize(module: Module, specialization: impl Specialization) -> Module
148177

149178
let mut expander = Expander::new(&specializer, module);
150179

180+
// See comment above on the loop collecting `interface_concrete_instances`.
181+
for interface_instance in interface_concrete_instances {
182+
expander.alloc_instance_id(interface_instance);
183+
}
184+
151185
// For non-"generic" functions, we can apply `replacements` right away,
152186
// though not before finishing inference for all functions first
153187
// (because `expander` needs to borrow `specializer` immutably).
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Test that interface (global) `OpVariable`s mentioned by `OpEntryPoint` don't
2+
// have to be used by the shader, for storage class inference to succeed.
3+
4+
// NOTE(eddyb) this test will likely become useless (won't fail without the fix)
5+
// once we start doing the copy out of the `Input` and into a `Function`-scoped
6+
// `OpVariable` (see #731), that's why there is another `issue-723-*` test.
7+
8+
// build-pass
9+
// compile-flags: -C llvm-args=--disassemble-globals
10+
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
11+
// normalize-stderr-test "OpExtension .SPV_KHR_vulkan_memory_model.\n" -> ""
12+
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"
13+
14+
use spirv_std as _;
15+
16+
#[spirv(fragment)]
17+
pub fn main(/* unused Input */ _: [f32; 3]) {}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
OpCapability Float64
2+
OpCapability Int16
3+
OpCapability Int64
4+
OpCapability Int8
5+
OpCapability Shader
6+
OpMemoryModel Logical Simple
7+
OpEntryPoint Fragment %1 "main" %2
8+
OpExecutionMode %1 OriginUpperLeft
9+
%3 = OpString "$OPSTRING_FILENAME/issue-723-indirect-input.rs"
10+
OpName %4 "issue_723_indirect_input::main"
11+
OpDecorate %5 ArrayStride 4
12+
OpDecorate %2 Location 0
13+
%6 = OpTypeVoid
14+
%7 = OpTypeFloat 32
15+
%8 = OpTypeInt 32 0
16+
%9 = OpConstant %8 3
17+
%5 = OpTypeArray %7 %9
18+
%10 = OpTypePointer Input %5
19+
%11 = OpTypeFunction %6
20+
%2 = OpVariable %10 Input

tests/ui/dis/issue-723-output.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Test that interface (global) `OpVariable`s mentioned by `OpEntryPoint` don't
2+
// have to be used by the shader, for storage class inference to succeed.
3+
4+
// NOTE(eddyb) this relies on two subtleties (in order to fail without the fix):
5+
// * disabling debuginfo (to prevent `%x.dbg.spill` stack slot generation)
6+
// * this could be alleviated in the future if we clean up how debuginfo
7+
// is handled in `rustc_codegen_ssa`, to not assume LLVM limitations
8+
// * it probably needs to stay like this, to ensure the parameter is unused
9+
// * `Output`s being handled with `&mut`, instead of by-value returns
10+
// * if this changes, this test will likely need >=1.4 SPIR-V, which supports
11+
// all interface `OpVariables` in `OpEntryPoint`, not just `Input`/`Output`
12+
13+
// build-pass
14+
// compile-flags: -C debuginfo=0 -C llvm-args=--disassemble-globals
15+
// normalize-stderr-test "OpCapability VulkanMemoryModel\n" -> ""
16+
// normalize-stderr-test "OpExtension .SPV_KHR_vulkan_memory_model.\n" -> ""
17+
// normalize-stderr-test "OpMemoryModel Logical Vulkan" -> "OpMemoryModel Logical Simple"
18+
19+
use spirv_std as _;
20+
21+
#[spirv(fragment)]
22+
pub fn main(/* unused Output */ _: &mut glam::Vec4) {}

tests/ui/dis/issue-723-output.stderr

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
OpCapability Float64
2+
OpCapability Int16
3+
OpCapability Int64
4+
OpCapability Int8
5+
OpCapability Shader
6+
OpMemoryModel Logical Simple
7+
OpEntryPoint Fragment %1 "main" %2
8+
OpExecutionMode %1 OriginUpperLeft
9+
%3 = OpString "$OPSTRING_FILENAME/issue-723-output.rs"
10+
OpName %4 "issue_723_output::main"
11+
OpDecorate %2 Location 0
12+
%5 = OpTypeVoid
13+
%6 = OpTypeFloat 32
14+
%7 = OpTypeVector %6 4
15+
%8 = OpTypePointer Output %7
16+
%9 = OpTypeFunction %5
17+
%2 = OpVariable %8 Output

0 commit comments

Comments
 (0)