|
1 | 1 | use crate::custom_insts::{self, CustomOp}; |
2 | 2 | use rspirv::binary::Assemble; |
3 | 3 | use rspirv::dr::{Instruction, Module, Operand}; |
4 | | -use rspirv::spirv::{Op, Word}; |
| 4 | +use rspirv::spirv::{BuiltIn, Decoration, Op, Word}; |
5 | 5 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
6 | 6 | use rustc_middle::bug; |
7 | 7 | use smallvec::SmallVec; |
@@ -104,6 +104,7 @@ fn gather_annotations(annotations: &[Instruction]) -> FxHashMap<Word, Vec<u32>> |
104 | 104 | .collect() |
105 | 105 | } |
106 | 106 |
|
| 107 | +/// Returns a map from an ID to its debug name (given by `OpName`). |
107 | 108 | fn gather_names(debug_names: &[Instruction]) -> FxHashMap<Word, String> { |
108 | 109 | debug_names |
109 | 110 | .iter() |
@@ -173,17 +174,34 @@ fn make_dedupe_key( |
173 | 174 | } |
174 | 175 |
|
175 | 176 | fn rewrite_inst_with_rules(inst: &mut Instruction, rules: &FxHashMap<u32, u32>) { |
176 | | - if let Some(ref mut id) = inst.result_type { |
| 177 | + if let Some(ref mut id) = inst.result_id { |
177 | 178 | // If the rewrite rules contain this ID, replace with the mapped value, otherwise don't touch it. |
178 | 179 | *id = rules.get(id).copied().unwrap_or(*id); |
179 | 180 | } |
| 181 | + if let Some(ref mut type_id) = inst.result_type { |
| 182 | + *type_id = rules.get(type_id).copied().unwrap_or(*type_id); |
| 183 | + } |
180 | 184 | for op in &mut inst.operands { |
181 | 185 | if let Some(id) = op.id_ref_any_mut() { |
182 | 186 | *id = rules.get(id).copied().unwrap_or(*id); |
183 | 187 | } |
184 | 188 | } |
185 | 189 | } |
186 | 190 |
|
| 191 | +/// Remove duplicate `OpName` and `OpMemberName` instructions from module debug names section. |
| 192 | +fn remove_duplicate_debug_names(debug_names: &mut Vec<Instruction>) { |
| 193 | + let mut name_ids = FxHashSet::default(); |
| 194 | + let mut member_name_ids = FxHashSet::default(); |
| 195 | + debug_names.retain(|inst| { |
| 196 | + (inst.class.opcode != Op::Name || name_ids.insert(inst.operands[0].unwrap_id_ref())) |
| 197 | + && (inst.class.opcode != Op::MemberName |
| 198 | + || member_name_ids.insert(( |
| 199 | + inst.operands[0].unwrap_id_ref(), |
| 200 | + inst.operands[1].unwrap_literal_bit32(), |
| 201 | + ))) |
| 202 | + }); |
| 203 | +} |
| 204 | + |
187 | 205 | pub fn remove_duplicate_types(module: &mut Module) { |
188 | 206 | // Keep in mind, this algorithm requires forward type references to not exist - i.e. it's a valid spir-v module. |
189 | 207 |
|
@@ -259,17 +277,9 @@ pub fn remove_duplicate_types(module: &mut Module) { |
259 | 277 | module |
260 | 278 | .annotations |
261 | 279 | .retain(|inst| anno_set.insert(inst.assemble())); |
262 | | - // Same thing with OpName |
263 | | - let mut name_ids = FxHashSet::default(); |
264 | | - let mut member_name_ids = FxHashSet::default(); |
265 | | - module.debug_names.retain(|inst| { |
266 | | - (inst.class.opcode != Op::Name || name_ids.insert(inst.operands[0].unwrap_id_ref())) |
267 | | - && (inst.class.opcode != Op::MemberName |
268 | | - || member_name_ids.insert(( |
269 | | - inst.operands[0].unwrap_id_ref(), |
270 | | - inst.operands[1].unwrap_literal_bit32(), |
271 | | - ))) |
272 | | - }); |
| 280 | + |
| 281 | + // Same thing with debug names section |
| 282 | + remove_duplicate_debug_names(&mut module.debug_names); |
273 | 283 | } |
274 | 284 |
|
275 | 285 | pub fn remove_duplicate_debuginfo(module: &mut Module) { |
@@ -541,3 +551,87 @@ pub fn remove_duplicate_debuginfo(module: &mut Module) { |
541 | 551 | } |
542 | 552 | } |
543 | 553 | } |
| 554 | + |
| 555 | +pub fn remove_duplicate_builtin_variables(module: &mut Module) { |
| 556 | + // Find the variables decorated as builtins, and any duplicates of each builtin. |
| 557 | + |
| 558 | + // A map from deleted duplicate variable ID to the new, de-duplicated ID. |
| 559 | + let duplicate_vars: FxHashMap<Word, Word> = { |
| 560 | + // A map from builtin to a de-duplicated variable decorated as that builtin. |
| 561 | + let mut builtin_to_var_id = FxHashMap::<BuiltIn, Word>::default(); |
| 562 | + |
| 563 | + let mut duplicate_vars = FxHashMap::<Word, Word>::default(); |
| 564 | + |
| 565 | + for inst in module.annotations.iter() { |
| 566 | + if inst.class.opcode == Op::Decorate |
| 567 | + && let [ |
| 568 | + Operand::IdRef(var_id), |
| 569 | + Operand::Decoration(Decoration::BuiltIn), |
| 570 | + Operand::BuiltIn(builtin), |
| 571 | + ] = inst.operands[..] |
| 572 | + { |
| 573 | + match builtin_to_var_id.entry(builtin) { |
| 574 | + // first variable we've seen for this builtin, |
| 575 | + // record it in the builtins map. |
| 576 | + hash_map::Entry::Vacant(vacant) => { |
| 577 | + vacant.insert(var_id); |
| 578 | + } |
| 579 | + |
| 580 | + // this builtin already has a variable, |
| 581 | + // record it in the duplicates map. |
| 582 | + hash_map::Entry::Occupied(occupied) => { |
| 583 | + duplicate_vars.insert(var_id, *occupied.get()); |
| 584 | + } |
| 585 | + }; |
| 586 | + } |
| 587 | + } |
| 588 | + // Rebind as immutable in fn scope. |
| 589 | + duplicate_vars |
| 590 | + }; |
| 591 | + |
| 592 | + // Rewrite entry points, removing duplicate variables. |
| 593 | + for entry in &mut module.entry_points { |
| 594 | + if entry.class.opcode != Op::EntryPoint { |
| 595 | + continue; |
| 596 | + } |
| 597 | + |
| 598 | + entry |
| 599 | + .operands |
| 600 | + .retain(|op| !matches!(op, Operand::IdRef(id) if duplicate_vars.contains_key(id))); |
| 601 | + } |
| 602 | + |
| 603 | + // Remove duplicate debug names after merging variables. |
| 604 | + remove_duplicate_debug_names(&mut module.debug_names); |
| 605 | + |
| 606 | + // Rewrite annotations for duplicates to point at de-duplicated variables; |
| 607 | + // this will merge the annotations sets but produce duplicate annotations |
| 608 | + // (which we will remove next). |
| 609 | + for inst in &mut module.annotations { |
| 610 | + rewrite_inst_with_rules(inst, &duplicate_vars); |
| 611 | + } |
| 612 | + |
| 613 | + // Merge the annotations for duplicate vars. |
| 614 | + { |
| 615 | + let mut annotations_set = FxHashSet::default(); |
| 616 | + module |
| 617 | + .annotations |
| 618 | + // Note: insert returns true when an annotation is inserted for the first time. |
| 619 | + .retain(|inst| annotations_set.insert(inst.assemble())); |
| 620 | + } |
| 621 | + |
| 622 | + // Remove the duplicate variable definitions. |
| 623 | + module.types_global_values.retain(|inst| { |
| 624 | + !matches!(inst.result_id, |
| 625 | + Some(id) if duplicate_vars.contains_key(&id)) |
| 626 | + }); |
| 627 | + |
| 628 | + // Rewrite function blocks to use de-duplicated variables. |
| 629 | + for inst in &mut module |
| 630 | + .functions |
| 631 | + .iter_mut() |
| 632 | + .flat_map(|f| &mut f.blocks) |
| 633 | + .flat_map(|b| &mut b.instructions) |
| 634 | + { |
| 635 | + rewrite_inst_with_rules(inst, &duplicate_vars); |
| 636 | + } |
| 637 | +} |
0 commit comments