|
| 1 | +use super::{get_name, get_names, Result}; |
1 | 2 | use rspirv::dr::{Block, Function, Module}; |
2 | | -use rspirv::spirv::{Op, Word}; |
| 3 | +use rspirv::spirv::{ExecutionModel, Op, Word}; |
3 | 4 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
| 5 | +use rustc_errors::ErrorReported; |
| 6 | +use rustc_session::Session; |
| 7 | +use std::iter::once; |
4 | 8 | use std::mem::take; |
5 | 9 |
|
6 | 10 | pub fn shift_ids(module: &mut Module, add: u32) { |
@@ -151,3 +155,107 @@ pub fn name_variables_pass(module: &mut Module) { |
151 | 155 | } |
152 | 156 | } |
153 | 157 | } |
| 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(¬e) |
| 254 | + .emit(); |
| 255 | + okay = false; |
| 256 | + } |
| 257 | + } |
| 258 | + stack.pop(); |
| 259 | + okay |
| 260 | + } |
| 261 | +} |
0 commit comments