mirror of
https://github.com/EmbarkStudios/rust-gpu.git
synced 2024-11-22 06:45:13 +00:00
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
This commit is contained in:
parent
9673f39967
commit
d26716b881
@ -81,7 +81,17 @@ fn apply_rewrite_rules(rewrite_rules: &FxHashMap<Word, Word>, blocks: &mut [Bloc
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_names(module: &Module) -> FxHashMap<Word, &str> {
|
fn get_names(module: &Module) -> FxHashMap<Word, &str> {
|
||||||
module
|
let entry_names = module
|
||||||
|
.entry_points
|
||||||
|
.iter()
|
||||||
|
.filter(|i| i.class.opcode == Op::EntryPoint)
|
||||||
|
.map(|i| {
|
||||||
|
(
|
||||||
|
i.operands[1].unwrap_id_ref(),
|
||||||
|
i.operands[2].unwrap_literal_string(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
let debug_names = module
|
||||||
.debug_names
|
.debug_names
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|i| i.class.opcode == Op::Name)
|
.filter(|i| i.class.opcode == Op::Name)
|
||||||
@ -90,8 +100,9 @@ fn get_names(module: &Module) -> FxHashMap<Word, &str> {
|
|||||||
i.operands[0].unwrap_id_ref(),
|
i.operands[0].unwrap_id_ref(),
|
||||||
i.operands[1].unwrap_literal_string(),
|
i.operands[1].unwrap_literal_string(),
|
||||||
)
|
)
|
||||||
})
|
});
|
||||||
.collect()
|
// items later on take priority
|
||||||
|
entry_names.chain(debug_names).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_name<'a>(names: &FxHashMap<Word, &'a str>, id: Word) -> Cow<'a, str> {
|
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
|
|||||||
import_export_link::run(sess, &mut output)?;
|
import_export_link::run(sess, &mut output)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let _timer = sess.timer("link_fragment_inst_check");
|
||||||
|
simple_passes::check_fragment_insts(sess, &output)?;
|
||||||
|
}
|
||||||
|
|
||||||
// HACK(eddyb) this has to run before the `remove_zombies` pass, so that any
|
// HACK(eddyb) this has to run before the `remove_zombies` pass, so that any
|
||||||
// zombies that are passed as call arguments, but eventually unused, won't
|
// zombies that are passed as call arguments, but eventually unused, won't
|
||||||
// be (incorrectly) considered used.
|
// be (incorrectly) considered used.
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
|
use super::{get_name, get_names, Result};
|
||||||
use rspirv::dr::{Block, Function, Module};
|
use rspirv::dr::{Block, Function, Module};
|
||||||
use rspirv::spirv::{Op, Word};
|
use rspirv::spirv::{ExecutionModel, Op, Word};
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||||
|
use rustc_errors::ErrorReported;
|
||||||
|
use rustc_session::Session;
|
||||||
|
use std::iter::once;
|
||||||
use std::mem::take;
|
use std::mem::take;
|
||||||
|
|
||||||
pub fn shift_ids(module: &mut Module, add: u32) {
|
pub fn shift_ids(module: &mut Module, add: u32) {
|
||||||
@ -151,3 +155,107 @@ pub fn name_variables_pass(module: &mut Module) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Some instructions are only valid in fragment shaders. Check them.
|
||||||
|
pub fn check_fragment_insts(sess: &Session, module: &Module) -> Result<()> {
|
||||||
|
let mut visited = vec![false; module.functions.len()];
|
||||||
|
let mut stack = Vec::new();
|
||||||
|
let mut names = None;
|
||||||
|
let func_id_to_idx: FxHashMap<Word, usize> = module
|
||||||
|
.functions
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(index, func)| (func.def_id().unwrap(), index))
|
||||||
|
.collect();
|
||||||
|
let entries = module
|
||||||
|
.entry_points
|
||||||
|
.iter()
|
||||||
|
.filter(|i| i.operands[0].unwrap_execution_model() != ExecutionModel::Fragment)
|
||||||
|
.map(|i| func_id_to_idx[&i.operands[1].unwrap_id_ref()]);
|
||||||
|
let mut okay = true;
|
||||||
|
for entry in entries {
|
||||||
|
okay &= visit(
|
||||||
|
sess,
|
||||||
|
module,
|
||||||
|
&mut visited,
|
||||||
|
&mut stack,
|
||||||
|
&mut names,
|
||||||
|
entry,
|
||||||
|
&func_id_to_idx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return if okay { Ok(()) } else { Err(ErrorReported) };
|
||||||
|
|
||||||
|
// returns false if error
|
||||||
|
fn visit<'m>(
|
||||||
|
sess: &Session,
|
||||||
|
module: &'m Module,
|
||||||
|
visited: &mut Vec<bool>,
|
||||||
|
stack: &mut Vec<Word>,
|
||||||
|
names: &mut Option<FxHashMap<Word, &'m str>>,
|
||||||
|
index: usize,
|
||||||
|
func_id_to_idx: &FxHashMap<Word, usize>,
|
||||||
|
) -> bool {
|
||||||
|
if visited[index] {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
visited[index] = true;
|
||||||
|
stack.push(module.functions[index].def_id().unwrap());
|
||||||
|
let mut okay = true;
|
||||||
|
for inst in module.functions[index].all_inst_iter() {
|
||||||
|
if inst.class.opcode == Op::FunctionCall {
|
||||||
|
let called_func = func_id_to_idx[&inst.operands[0].unwrap_id_ref()];
|
||||||
|
okay &= visit(
|
||||||
|
sess,
|
||||||
|
module,
|
||||||
|
visited,
|
||||||
|
stack,
|
||||||
|
names,
|
||||||
|
called_func,
|
||||||
|
func_id_to_idx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if matches!(
|
||||||
|
inst.class.opcode,
|
||||||
|
Op::ImageSampleImplicitLod
|
||||||
|
| Op::ImageSampleDrefImplicitLod
|
||||||
|
| Op::ImageSampleProjImplicitLod
|
||||||
|
| Op::ImageSampleProjDrefImplicitLod
|
||||||
|
| Op::ImageQueryLod
|
||||||
|
| Op::ImageSparseSampleImplicitLod
|
||||||
|
| Op::ImageSparseSampleDrefImplicitLod
|
||||||
|
| Op::DPdx
|
||||||
|
| Op::DPdy
|
||||||
|
| Op::Fwidth
|
||||||
|
| Op::DPdxFine
|
||||||
|
| Op::DPdyFine
|
||||||
|
| Op::FwidthFine
|
||||||
|
| Op::DPdxCoarse
|
||||||
|
| Op::DPdyCoarse
|
||||||
|
| Op::FwidthCoarse
|
||||||
|
| Op::Kill
|
||||||
|
) {
|
||||||
|
// These instructions are (usually) in system functions - if we get an error, allow
|
||||||
|
// the system function to be visited again from elsewhere to emit another error
|
||||||
|
// from another callsite.
|
||||||
|
visited[index] = false;
|
||||||
|
|
||||||
|
let names = names.get_or_insert_with(|| get_names(module));
|
||||||
|
let stack = stack.iter().rev().map(|&s| get_name(names, s).into_owned());
|
||||||
|
let note = once("Stack:".to_string())
|
||||||
|
.chain(stack)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n");
|
||||||
|
sess.struct_err(&format!(
|
||||||
|
"{} cannot be used outside a fragment shader",
|
||||||
|
inst.class.opname
|
||||||
|
))
|
||||||
|
.note(¬e)
|
||||||
|
.emit();
|
||||||
|
okay = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stack.pop();
|
||||||
|
okay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
23
tests/ui/image/implicit_not_in_fragment.rs
Normal file
23
tests/ui/image/implicit_not_in_fragment.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// build-fail
|
||||||
|
|
||||||
|
use spirv_std::{arch, Image, Sampler};
|
||||||
|
|
||||||
|
fn deeper_stack(image2d: &Image!(2D, type=f32, sampled), sampler: &Sampler) -> glam::Vec4 {
|
||||||
|
let v2 = glam::Vec2::new(0.0, 1.0);
|
||||||
|
image2d.sample(*sampler, v2)
|
||||||
|
}
|
||||||
|
fn deep_stack(image2d: &Image!(2D, type=f32, sampled), sampler: &Sampler) -> glam::Vec4 {
|
||||||
|
deeper_stack(image2d, sampler)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[spirv(vertex)]
|
||||||
|
pub fn main(
|
||||||
|
#[spirv(descriptor_set = 0, binding = 0)] image2d: &Image!(2D, type=f32, sampled),
|
||||||
|
#[spirv(descriptor_set = 0, binding = 1)] sampler: &Sampler,
|
||||||
|
output: &mut glam::Vec4,
|
||||||
|
) {
|
||||||
|
let v2 = glam::Vec2::new(0.0, 1.0);
|
||||||
|
let r0 = deep_stack(image2d, sampler);
|
||||||
|
let r1: glam::Vec4 = image2d.sample(*sampler, v2);
|
||||||
|
*output = r0 + r1;
|
||||||
|
}
|
18
tests/ui/image/implicit_not_in_fragment.stderr
Normal file
18
tests/ui/image/implicit_not_in_fragment.stderr
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
error: ImageSampleImplicitLod cannot be used outside a fragment shader
|
||||||
|
|
|
||||||
|
= note: Stack:
|
||||||
|
<spirv_std::image::Image<f32, 1, 2, 0, 0, 1, 0>>::sample::<f32, glam::vec4::Vec4, glam::vec2::Vec2>
|
||||||
|
implicit_not_in_fragment::deeper_stack
|
||||||
|
implicit_not_in_fragment::deep_stack
|
||||||
|
implicit_not_in_fragment::main
|
||||||
|
main
|
||||||
|
|
||||||
|
error: ImageSampleImplicitLod cannot be used outside a fragment shader
|
||||||
|
|
|
||||||
|
= note: Stack:
|
||||||
|
<spirv_std::image::Image<f32, 1, 2, 0, 0, 1, 0>>::sample::<f32, glam::vec4::Vec4, glam::vec2::Vec2>
|
||||||
|
implicit_not_in_fragment::main
|
||||||
|
main
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
@ -6,7 +6,7 @@ error: constant arrays/structs cannot contain pointers to other constants
|
|||||||
|
|
|
|
||||||
= note: Stack:
|
= note: Stack:
|
||||||
nested_ref_in_composite::main_pair
|
nested_ref_in_composite::main_pair
|
||||||
Unnamed function ID %24
|
main_pair
|
||||||
|
|
||||||
error: constant arrays/structs cannot contain pointers to other constants
|
error: constant arrays/structs cannot contain pointers to other constants
|
||||||
--> $DIR/nested-ref-in-composite.rs:27:19
|
--> $DIR/nested-ref-in-composite.rs:27:19
|
||||||
@ -16,7 +16,7 @@ error: constant arrays/structs cannot contain pointers to other constants
|
|||||||
|
|
|
|
||||||
= note: Stack:
|
= note: Stack:
|
||||||
nested_ref_in_composite::main_array3
|
nested_ref_in_composite::main_array3
|
||||||
Unnamed function ID %33
|
main_array3
|
||||||
|
|
||||||
error: error:0:0 - No OpEntryPoint instruction was found. This is only allowed if the Linkage capability is being used.
|
error: error:0:0 - No OpEntryPoint instruction was found. This is only allowed if the Linkage capability is being used.
|
||||||
|
|
|
|
||||||
|
@ -2,7 +2,7 @@ error: pointer has non-null integer address
|
|||||||
|
|
|
|
||||||
= note: Stack:
|
= note: Stack:
|
||||||
allocate_const_scalar::main
|
allocate_const_scalar::main
|
||||||
Unnamed function ID %4
|
main
|
||||||
|
|
||||||
error: error:0:0 - No OpEntryPoint instruction was found. This is only allowed if the Linkage capability is being used.
|
error: error:0:0 - No OpEntryPoint instruction was found. This is only allowed if the Linkage capability is being used.
|
||||||
|
|
|
|
||||||
|
Loading…
Reference in New Issue
Block a user