Skip to content

Commit d26716b

Browse files
authored
Add error for insts not allowed outside fragment (#821)
* Add error for insts not allowed outside fragment * make get_name return entry point names too
1 parent 9673f39 commit d26716b

File tree

6 files changed

+172
-7
lines changed

6 files changed

+172
-7
lines changed

crates/rustc_codegen_spirv/src/linker/mod.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,17 @@ fn apply_rewrite_rules(rewrite_rules: &FxHashMap<Word, Word>, blocks: &mut [Bloc
8181
}
8282

8383
fn get_names(module: &Module) -> FxHashMap<Word, &str> {
84-
module
84+
let entry_names = module
85+
.entry_points
86+
.iter()
87+
.filter(|i| i.class.opcode == Op::EntryPoint)
88+
.map(|i| {
89+
(
90+
i.operands[1].unwrap_id_ref(),
91+
i.operands[2].unwrap_literal_string(),
92+
)
93+
});
94+
let debug_names = module
8595
.debug_names
8696
.iter()
8797
.filter(|i| i.class.opcode == Op::Name)
@@ -90,8 +100,9 @@ fn get_names(module: &Module) -> FxHashMap<Word, &str> {
90100
i.operands[0].unwrap_id_ref(),
91101
i.operands[1].unwrap_literal_string(),
92102
)
93-
})
94-
.collect()
103+
});
104+
// items later on take priority
105+
entry_names.chain(debug_names).collect()
95106
}
96107

97108
fn get_name<'a>(names: &FxHashMap<Word, &'a str>, id: Word) -> Cow<'a, str> {
@@ -157,6 +168,11 @@ pub fn link(sess: &Session, mut inputs: Vec<Module>, opts: &Options) -> Result<L
157168
import_export_link::run(sess, &mut output)?;
158169
}
159170

171+
{
172+
let _timer = sess.timer("link_fragment_inst_check");
173+
simple_passes::check_fragment_insts(sess, &output)?;
174+
}
175+
160176
// HACK(eddyb) this has to run before the `remove_zombies` pass, so that any
161177
// zombies that are passed as call arguments, but eventually unused, won't
162178
// be (incorrectly) considered used.

crates/rustc_codegen_spirv/src/linker/simple_passes.rs

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
use super::{get_name, get_names, Result};
12
use rspirv::dr::{Block, Function, Module};
2-
use rspirv::spirv::{Op, Word};
3+
use rspirv::spirv::{ExecutionModel, Op, Word};
34
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
5+
use rustc_errors::ErrorReported;
6+
use rustc_session::Session;
7+
use std::iter::once;
48
use std::mem::take;
59

610
pub fn shift_ids(module: &mut Module, add: u32) {
@@ -151,3 +155,107 @@ pub fn name_variables_pass(module: &mut Module) {
151155
}
152156
}
153157
}
158+
159+
// Some instructions are only valid in fragment shaders. Check them.
160+
pub fn check_fragment_insts(sess: &Session, module: &Module) -> Result<()> {
161+
let mut visited = vec![false; module.functions.len()];
162+
let mut stack = Vec::new();
163+
let mut names = None;
164+
let func_id_to_idx: FxHashMap<Word, usize> = module
165+
.functions
166+
.iter()
167+
.enumerate()
168+
.map(|(index, func)| (func.def_id().unwrap(), index))
169+
.collect();
170+
let entries = module
171+
.entry_points
172+
.iter()
173+
.filter(|i| i.operands[0].unwrap_execution_model() != ExecutionModel::Fragment)
174+
.map(|i| func_id_to_idx[&i.operands[1].unwrap_id_ref()]);
175+
let mut okay = true;
176+
for entry in entries {
177+
okay &= visit(
178+
sess,
179+
module,
180+
&mut visited,
181+
&mut stack,
182+
&mut names,
183+
entry,
184+
&func_id_to_idx,
185+
);
186+
}
187+
return if okay { Ok(()) } else { Err(ErrorReported) };
188+
189+
// returns false if error
190+
fn visit<'m>(
191+
sess: &Session,
192+
module: &'m Module,
193+
visited: &mut Vec<bool>,
194+
stack: &mut Vec<Word>,
195+
names: &mut Option<FxHashMap<Word, &'m str>>,
196+
index: usize,
197+
func_id_to_idx: &FxHashMap<Word, usize>,
198+
) -> bool {
199+
if visited[index] {
200+
return true;
201+
}
202+
visited[index] = true;
203+
stack.push(module.functions[index].def_id().unwrap());
204+
let mut okay = true;
205+
for inst in module.functions[index].all_inst_iter() {
206+
if inst.class.opcode == Op::FunctionCall {
207+
let called_func = func_id_to_idx[&inst.operands[0].unwrap_id_ref()];
208+
okay &= visit(
209+
sess,
210+
module,
211+
visited,
212+
stack,
213+
names,
214+
called_func,
215+
func_id_to_idx,
216+
);
217+
}
218+
if matches!(
219+
inst.class.opcode,
220+
Op::ImageSampleImplicitLod
221+
| Op::ImageSampleDrefImplicitLod
222+
| Op::ImageSampleProjImplicitLod
223+
| Op::ImageSampleProjDrefImplicitLod
224+
| Op::ImageQueryLod
225+
| Op::ImageSparseSampleImplicitLod
226+
| Op::ImageSparseSampleDrefImplicitLod
227+
| Op::DPdx
228+
| Op::DPdy
229+
| Op::Fwidth
230+
| Op::DPdxFine
231+
| Op::DPdyFine
232+
| Op::FwidthFine
233+
| Op::DPdxCoarse
234+
| Op::DPdyCoarse
235+
| Op::FwidthCoarse
236+
| Op::Kill
237+
) {
238+
// These instructions are (usually) in system functions - if we get an error, allow
239+
// the system function to be visited again from elsewhere to emit another error
240+
// from another callsite.
241+
visited[index] = false;
242+
243+
let names = names.get_or_insert_with(|| get_names(module));
244+
let stack = stack.iter().rev().map(|&s| get_name(names, s).into_owned());
245+
let note = once("Stack:".to_string())
246+
.chain(stack)
247+
.collect::<Vec<_>>()
248+
.join("\n");
249+
sess.struct_err(&format!(
250+
"{} cannot be used outside a fragment shader",
251+
inst.class.opname
252+
))
253+
.note(&note)
254+
.emit();
255+
okay = false;
256+
}
257+
}
258+
stack.pop();
259+
okay
260+
}
261+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// build-fail
2+
3+
use spirv_std::{arch, Image, Sampler};
4+
5+
fn deeper_stack(image2d: &Image!(2D, type=f32, sampled), sampler: &Sampler) -> glam::Vec4 {
6+
let v2 = glam::Vec2::new(0.0, 1.0);
7+
image2d.sample(*sampler, v2)
8+
}
9+
fn deep_stack(image2d: &Image!(2D, type=f32, sampled), sampler: &Sampler) -> glam::Vec4 {
10+
deeper_stack(image2d, sampler)
11+
}
12+
13+
#[spirv(vertex)]
14+
pub fn main(
15+
#[spirv(descriptor_set = 0, binding = 0)] image2d: &Image!(2D, type=f32, sampled),
16+
#[spirv(descriptor_set = 0, binding = 1)] sampler: &Sampler,
17+
output: &mut glam::Vec4,
18+
) {
19+
let v2 = glam::Vec2::new(0.0, 1.0);
20+
let r0 = deep_stack(image2d, sampler);
21+
let r1: glam::Vec4 = image2d.sample(*sampler, v2);
22+
*output = r0 + r1;
23+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: ImageSampleImplicitLod cannot be used outside a fragment shader
2+
|
3+
= note: Stack:
4+
<spirv_std::image::Image<f32, 1, 2, 0, 0, 1, 0>>::sample::<f32, glam::vec4::Vec4, glam::vec2::Vec2>
5+
implicit_not_in_fragment::deeper_stack
6+
implicit_not_in_fragment::deep_stack
7+
implicit_not_in_fragment::main
8+
main
9+
10+
error: ImageSampleImplicitLod cannot be used outside a fragment shader
11+
|
12+
= note: Stack:
13+
<spirv_std::image::Image<f32, 1, 2, 0, 0, 1, 0>>::sample::<f32, glam::vec4::Vec4, glam::vec2::Vec2>
14+
implicit_not_in_fragment::main
15+
main
16+
17+
error: aborting due to 2 previous errors
18+

tests/ui/lang/consts/nested-ref-in-composite.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ error: constant arrays/structs cannot contain pointers to other constants
66
|
77
= note: Stack:
88
nested_ref_in_composite::main_pair
9-
Unnamed function ID %24
9+
main_pair
1010

1111
error: constant arrays/structs cannot contain pointers to other constants
1212
--> $DIR/nested-ref-in-composite.rs:27:19
@@ -16,7 +16,7 @@ error: constant arrays/structs cannot contain pointers to other constants
1616
|
1717
= note: Stack:
1818
nested_ref_in_composite::main_array3
19-
Unnamed function ID %33
19+
main_array3
2020

2121
error: error:0:0 - No OpEntryPoint instruction was found. This is only allowed if the Linkage capability is being used.
2222
|

tests/ui/lang/core/ptr/allocate_const_scalar.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: pointer has non-null integer address
22
|
33
= note: Stack:
44
allocate_const_scalar::main
5-
Unnamed function ID %4
5+
main
66

77
error: error:0:0 - No OpEntryPoint instruction was found. This is only allowed if the Linkage capability is being used.
88
|

0 commit comments

Comments
 (0)