Skip to content

Commit 9a9f4f2

Browse files
fluffysquirrelsFirestar99
authored andcommitted
dedup builtins: add linker pass remove_duplicate_builtin_variables
1 parent 7cd7800 commit 9a9f4f2

File tree

3 files changed

+151
-66
lines changed

3 files changed

+151
-66
lines changed

crates/rustc_codegen_spirv/src/linker/duplicates.rs

Lines changed: 107 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::custom_insts::{self, CustomOp};
22
use rspirv::binary::Assemble;
33
use rspirv::dr::{Instruction, Module, Operand};
4-
use rspirv::spirv::{Op, Word};
4+
use rspirv::spirv::{BuiltIn, Decoration, Op, Word};
55
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
66
use rustc_middle::bug;
77
use smallvec::SmallVec;
@@ -104,6 +104,7 @@ fn gather_annotations(annotations: &[Instruction]) -> FxHashMap<Word, Vec<u32>>
104104
.collect()
105105
}
106106

107+
/// Returns a map from an ID to its debug name (given by `OpName`).
107108
fn gather_names(debug_names: &[Instruction]) -> FxHashMap<Word, String> {
108109
debug_names
109110
.iter()
@@ -173,17 +174,34 @@ fn make_dedupe_key(
173174
}
174175

175176
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 {
177178
// If the rewrite rules contain this ID, replace with the mapped value, otherwise don't touch it.
178179
*id = rules.get(id).copied().unwrap_or(*id);
179180
}
181+
if let Some(ref mut type_id) = inst.result_type {
182+
*type_id = rules.get(type_id).copied().unwrap_or(*type_id);
183+
}
180184
for op in &mut inst.operands {
181185
if let Some(id) = op.id_ref_any_mut() {
182186
*id = rules.get(id).copied().unwrap_or(*id);
183187
}
184188
}
185189
}
186190

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+
187205
pub fn remove_duplicate_types(module: &mut Module) {
188206
// Keep in mind, this algorithm requires forward type references to not exist - i.e. it's a valid spir-v module.
189207

@@ -259,17 +277,9 @@ pub fn remove_duplicate_types(module: &mut Module) {
259277
module
260278
.annotations
261279
.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);
273283
}
274284

275285
pub fn remove_duplicate_debuginfo(module: &mut Module) {
@@ -541,3 +551,87 @@ pub fn remove_duplicate_debuginfo(module: &mut Module) {
541551
}
542552
}
543553
}
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+
}

crates/rustc_codegen_spirv/src/linker/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ pub fn link(
290290
duplicates::remove_duplicate_capabilities(&mut output);
291291
duplicates::remove_duplicate_ext_inst_imports(&mut output);
292292
duplicates::remove_duplicate_types(&mut output);
293+
duplicates::remove_duplicate_builtin_variables(&mut output);
293294
// jb-todo: strip identical OpDecoration / OpDecorationGroups
294295
}
295296

Lines changed: 43 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,63 @@
11
OpCapability Shader
22
OpMemoryModel Logical Simple
3-
OpEntryPoint GLCompute %1 "entry_1" %2 %3
4-
OpEntryPoint GLCompute %4 "entry_2" %2 %3
3+
OpEntryPoint GLCompute %1 "entry_1" %2
4+
OpEntryPoint GLCompute %3 "entry_2" %2
55
OpExecutionMode %1 LocalSize 1 1 1
6-
OpExecutionMode %4 LocalSize 1 1 1
7-
OpName %6 "builtin_duplicates::entry_1"
8-
OpName %7 "builtin_duplicates::sub_1"
9-
OpName %8 "builtin_duplicates::sub_2"
10-
OpName %9 "builtin_duplicates::entry_2"
6+
OpExecutionMode %3 LocalSize 1 1 1
7+
OpName %5 "builtin_duplicates::entry_1"
8+
OpName %6 "builtin_duplicates::sub_1"
9+
OpName %7 "builtin_duplicates::sub_2"
10+
OpName %8 "builtin_duplicates::entry_2"
1111
OpDecorate %2 BuiltIn LocalInvocationIndex
12-
OpDecorate %3 BuiltIn LocalInvocationIndex
13-
%10 = OpTypeInt 32 0
14-
%11 = OpTypePointer Input %10
15-
%12 = OpTypeVoid
16-
%13 = OpTypeFunction %12
17-
%2 = OpVariable %11 Input
18-
%14 = OpTypeFunction %12 %10
19-
%3 = OpVariable %11 Input
20-
%15 = OpTypeFunction %10
21-
%1 = OpFunction %12 None %13
22-
%16 = OpLabel
23-
%17 = OpLoad %10 %2
24-
%18 = OpFunctionCall %12 %6 %17
12+
%9 = OpTypeInt 32 0
13+
%10 = OpTypePointer Input %9
14+
%11 = OpTypeVoid
15+
%12 = OpTypeFunction %11
16+
%2 = OpVariable %10 Input
17+
%13 = OpTypeFunction %11 %9
18+
%14 = OpTypeFunction %9
19+
%1 = OpFunction %11 None %12
20+
%15 = OpLabel
21+
%16 = OpLoad %9 %2
22+
%17 = OpFunctionCall %11 %5 %16
2523
OpNoLine
2624
OpReturn
2725
OpFunctionEnd
28-
%6 = OpFunction %12 None %14
29-
%19 = OpFunctionParameter %10
30-
%20 = OpLabel
31-
%21 = OpLoad %10 %3
32-
%22 = OpLoad %10 %3
33-
%23 = OpFunctionCall %10 %7
34-
%24 = OpFunctionCall %10 %8
26+
%5 = OpFunction %11 None %13
27+
%18 = OpFunctionParameter %9
28+
%19 = OpLabel
29+
%20 = OpLoad %9 %2
30+
%21 = OpLoad %9 %2
31+
%22 = OpFunctionCall %9 %6
32+
%23 = OpFunctionCall %9 %7
3533
OpNoLine
3634
OpReturn
3735
OpFunctionEnd
38-
%7 = OpFunction %10 DontInline %15
39-
%25 = OpLabel
40-
%26 = OpLoad %10 %3
36+
%6 = OpFunction %9 DontInline %14
37+
%24 = OpLabel
38+
%25 = OpLoad %9 %2
4139
OpNoLine
42-
OpReturnValue %26
40+
OpReturnValue %25
4341
OpFunctionEnd
44-
%8 = OpFunction %10 DontInline %15
45-
%27 = OpLabel
46-
%28 = OpLoad %10 %3
42+
%7 = OpFunction %9 DontInline %14
43+
%26 = OpLabel
44+
%27 = OpLoad %9 %2
4745
OpNoLine
48-
OpReturnValue %28
46+
OpReturnValue %27
4947
OpFunctionEnd
50-
%4 = OpFunction %12 None %13
51-
%29 = OpLabel
52-
%30 = OpLoad %10 %2
53-
%31 = OpFunctionCall %12 %9 %30
48+
%3 = OpFunction %11 None %12
49+
%28 = OpLabel
50+
%29 = OpLoad %9 %2
51+
%30 = OpFunctionCall %11 %8 %29
5452
OpNoLine
5553
OpReturn
5654
OpFunctionEnd
57-
%9 = OpFunction %12 None %14
58-
%32 = OpFunctionParameter %10
59-
%33 = OpLabel
60-
%34 = OpLoad %10 %3
61-
%35 = OpFunctionCall %10 %7
62-
%36 = OpFunctionCall %10 %8
55+
%8 = OpFunction %11 None %13
56+
%31 = OpFunctionParameter %9
57+
%32 = OpLabel
58+
%33 = OpLoad %9 %2
59+
%34 = OpFunctionCall %9 %6
60+
%35 = OpFunctionCall %9 %7
6361
OpNoLine
6462
OpReturn
6563
OpFunctionEnd
66-
error: error:0:0 - [VUID-StandaloneSpirv-OpEntryPoint-09658] OpEntryPoint contains duplicate input variables with LocalInvocationIndex builtin
67-
%gl_LocalInvocationIndex = OpVariable %_ptr_Input_uint Input
68-
|
69-
= note: spirv-val failed
70-
= note: module `$TEST_BUILD_DIR/entry/builtin_duplicates.vulkan1.2`
71-
72-
error: aborting due to 1 previous error
73-

0 commit comments

Comments
 (0)