Skip to content

Commit 78e965e

Browse files
committed
[naga] Remove non-essential override references via compaction
Adds a mode to compaction that removes unused functions, global variables, and named types and overrides. This mode is used everywhere except the compaction at the end of lowering, where it is important to preserve unused items for type checking and other validation of the module. Pruning all but the active entry point and then compacting makes `process_overrides` tolerant of missing values for overrides that are not used by the active entry point. Fixes #5885
1 parent 7622091 commit 78e965e

31 files changed

+726
-388
lines changed

CHANGELOG.md

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,7 @@ Bottom level categories:
6767

6868
#### Naga
6969

70-
Naga now infers the correct binding layout when a resource appears only in an assignment to `_`. By @andyleiserson in [#7540](https://github.com/gfx-rs/wgpu/pull/7540).
71-
70+
- Naga now infers the correct binding layout when a resource appears only in an assignment to `_`. By @andyleiserson in [#7540](https://github.com/gfx-rs/wgpu/pull/7540).
7271
- Implement `dot4U8Packed` and `dot4I8Packed` for all backends, using specialized intrinsics on SPIR-V, HSLS, and Metal if available, and polyfills everywhere else. By @robamler in [#7494](https://github.com/gfx-rs/wgpu/pull/7494), [#7574](https://github.com/gfx-rs/wgpu/pull/7574), and [#7653](https://github.com/gfx-rs/wgpu/pull/7653).
7372
- Add polyfilled `pack4x{I,U}8Clamped` built-ins to all backends and WGSL frontend. By @ErichDonGubler in [#7546](https://github.com/gfx-rs/wgpu/pull/7546).
7473
- Allow textureLoad's sample index arg to be unsigned. By @jimblandy in [#7625](https://github.com/gfx-rs/wgpu/pull/7625).
@@ -78,6 +77,7 @@ Naga now infers the correct binding layout when a resource appears only in an as
7877
- Properly evaluate `abs(most negative abstract int)`. By @jimblandy in [#7507](https://github.com/gfx-rs/wgpu/pull/7507).
7978
- Generate vectorized code for `[un]pack4x{I,U}8[Clamp]` on SPIR-V and MSL 2.1+. By @robamler in [#7664](https://github.com/gfx-rs/wgpu/pull/7664).
8079
- Fix typing for `select`, which had issues particularly with a lack of automatic type conversion. By @ErichDonGubler in [#7572](https://github.com/gfx-rs/wgpu/pull/7572).
80+
- Allow scalars as the first argument of the `distance` built-in function. By @bernhl in [#7530](https://github.com/gfx-rs/wgpu/pull/7530).
8181

8282
#### DX12
8383

@@ -102,6 +102,10 @@ Naga now infers the correct binding layout when a resource appears only in an as
102102
- `naga::back::hlsl::Writer::new` has a new `pipeline_options` argument. `hlsl::PipelineOptions::default()` can be passed as a default. The `shader_stage` and `entry_point` members of `pipeline_options` can be used to write only a single entry point when using the HLSL and MSL backends (GLSL and SPIR-V already had this functionality). The Metal and DX12 HALs now write only a single entry point when loading shaders. By @andyleiserson in [#7626](https://github.com/gfx-rs/wgpu/pull/7626).
103103
- Implemented `early_depth_test` for SPIR-V backend, enabling `SHADER_EARLY_DEPTH_TEST` for Vulkan. Additionally, fixed conservative depth optimizations when using `early_depth_test`. The syntax for forcing early depth tests is now `@early_depth_test(force)` instead of `@early_depth_test`. By @dzamkov in [#7676](https://github.com/gfx-rs/wgpu/pull/7676).
104104
- `ImplementedLanguageExtension::VARIANTS` is now implemented manually rather than derived using `strum` (allowing `strum` to become a dev-only dependency) so it is no longer a member of the `strum::VARIANTS` trait. Unless you are using this trait as a bound this should have no effect.
105+
- Compaction changes, by @andyleiserson in [#7703](https://github.com/gfx-rs/wgpu/pull/7703):
106+
- [`process_overrides`](https://docs.rs/naga/latest/naga/back/pipeline_constants/fn.process_overrides.html) now compacts the module to remove unused items. It is no longer necessary to supply values for overrides that are not used by the active entry point.
107+
- The `compact` Cargo feature has been removed. It is no longer possible to exclude compaction support from the build.
108+
- [`compact`](https://docs.rs/naga/latest/naga/compact/fn.compact.html) now has an additional argument that specifies whether to remove unused functions, globals, and named types and overrides. For the previous behavior, pass `KeepUnused::Yes`.
105109

106110
#### D3D12
107111

@@ -119,11 +123,6 @@ Naga now infers the correct binding layout when a resource appears only in an as
119123

120124
- Added initial `no_std` support to `wgpu-hal`. By @bushrat011899 in [#7599](https://github.com/gfx-rs/wgpu/pull/7599)
121125

122-
### Bug Fixes
123-
124-
#### Naga
125-
126-
- Allow scalars as the first argument of the `distance` built-in function. By @bernhl in [#7530](https://github.com/gfx-rs/wgpu/pull/7530).
127126

128127
### Documentation
129128

benches/benches/wgpu-benchmark/shader.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,8 @@ fn validation(c: &mut Criterion) {
269269
}
270270

271271
fn compact(c: &mut Criterion) {
272+
use naga::compact::{compact, KeepUnused};
273+
272274
let mut inputs = get_wgsl_inputs();
273275

274276
inputs.validate();
@@ -279,7 +281,7 @@ fn compact(c: &mut Criterion) {
279281
group.bench_function("shader: compact", |b| {
280282
b.iter(|| {
281283
for input in &mut inputs.inner {
282-
naga::compact::compact(input.module.as_mut().unwrap());
284+
compact(input.module.as_mut().unwrap(), KeepUnused::No);
283285
}
284286
});
285287
});

cts_runner/test.lst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ webgpu:api,operation,command_buffer,copyBufferToBuffer:state_transitions:*
77
webgpu:api,operation,command_buffer,copyBufferToBuffer:copy_order:*
88
webgpu:api,operation,compute,basic:memcpy:*
99
//FAIL: webgpu:api,operation,compute,basic:large_dispatch:*
10+
webgpu:api,operation,compute_pipeline,overrides:*
1011
webgpu:api,operation,device,lost:*
12+
webgpu:api,operation,render_pipeline,overrides:*
1113
webgpu:api,operation,rendering,basic:clear:*
1214
webgpu:api,operation,rendering,basic:fullscreen_quad:*
1315
//FAIL: webgpu:api,operation,rendering,basic:large_draw:*

naga-cli/src/bin/naga.rs

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -542,7 +542,7 @@ fn run() -> anyhow::Result<()> {
542542
write_output(&module, &info, &params, before_compaction)?;
543543
}
544544

545-
naga::compact::compact(&mut module);
545+
naga::compact::compact(&mut module, KeepUnused::No);
546546

547547
// Re-validate the IR after compaction.
548548
match naga::valid::Validator::new(params.validation_flags, validation_caps)
@@ -717,9 +717,13 @@ fn write_output(
717717
succeed, and it failed in a previous step",
718718
))?;
719719

720-
let (module, info) =
721-
naga::back::pipeline_constants::process_overrides(module, info, &params.overrides)
722-
.unwrap_pretty();
720+
let (module, info) = naga::back::pipeline_constants::process_overrides(
721+
module,
722+
info,
723+
None,
724+
&params.overrides,
725+
)
726+
.unwrap_pretty();
723727

724728
let pipeline_options = msl::PipelineOptions::default();
725729
let (msl, _) =
@@ -751,9 +755,13 @@ fn write_output(
751755
succeed, and it failed in a previous step",
752756
))?;
753757

754-
let (module, info) =
755-
naga::back::pipeline_constants::process_overrides(module, info, &params.overrides)
756-
.unwrap_pretty();
758+
let (module, info) = naga::back::pipeline_constants::process_overrides(
759+
module,
760+
info,
761+
None,
762+
&params.overrides,
763+
)
764+
.unwrap_pretty();
757765

758766
let spv =
759767
spv::write_vec(&module, &info, &params.spv_out, pipeline_options).unwrap_pretty();
@@ -788,9 +796,13 @@ fn write_output(
788796
succeed, and it failed in a previous step",
789797
))?;
790798

791-
let (module, info) =
792-
naga::back::pipeline_constants::process_overrides(module, info, &params.overrides)
793-
.unwrap_pretty();
799+
let (module, info) = naga::back::pipeline_constants::process_overrides(
800+
module,
801+
info,
802+
None,
803+
&params.overrides,
804+
)
805+
.unwrap_pretty();
794806

795807
let mut buffer = String::new();
796808
let mut writer = glsl::Writer::new(
@@ -819,9 +831,13 @@ fn write_output(
819831
succeed, and it failed in a previous step",
820832
))?;
821833

822-
let (module, info) =
823-
naga::back::pipeline_constants::process_overrides(module, info, &params.overrides)
824-
.unwrap_pretty();
834+
let (module, info) = naga::back::pipeline_constants::process_overrides(
835+
module,
836+
info,
837+
None,
838+
&params.overrides,
839+
)
840+
.unwrap_pretty();
825841

826842
let mut buffer = String::new();
827843
let pipeline_options = Default::default();
@@ -906,4 +922,4 @@ fn bulk_validate(args: Args, params: &Parameters) -> anyhow::Result<()> {
906922
}
907923

908924
use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
909-
use naga::FastHashMap;
925+
use naga::{compact::KeepUnused, FastHashMap};

naga/src/arena/handle_set.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ impl<T> HandleSet<T> {
7777
}
7878
}
7979

80+
/// Add all of the handles that can be included in this set.
81+
pub fn add_all(&mut self) {
82+
self.members.get_mut().set_all();
83+
}
84+
8085
pub fn contains(&self, handle: Handle<T>) -> bool {
8186
self.members.contains(handle.index())
8287
}
@@ -85,6 +90,23 @@ impl<T> HandleSet<T> {
8590
pub fn iter(&self) -> impl '_ + Iterator<Item = Handle<T>> {
8691
self.members.iter().map(Handle::from_usize)
8792
}
93+
94+
/// Removes and returns the numerically largest handle in the set, or `None`
95+
/// if the set is empty.
96+
pub fn pop(&mut self) -> Option<Handle<T>> {
97+
let members = core::mem::take(&mut self.members);
98+
let mut vec = members.into_bit_vec();
99+
let result = vec.iter_mut().enumerate().rev().find_map(|(i, mut bit)| {
100+
if *bit {
101+
*bit = false;
102+
Some(i)
103+
} else {
104+
None
105+
}
106+
});
107+
self.members = bit_set::BitSet::from_bit_vec(vec);
108+
result.map(Handle::from_usize)
109+
}
88110
}
89111

90112
impl<T> Default for HandleSet<T> {

naga/src/back/pipeline_constants.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use thiserror::Error;
1010
use super::PipelineConstants;
1111
use crate::{
1212
arena::HandleVec,
13+
compact::{compact, KeepUnused},
14+
ir,
1315
proc::{ConstantEvaluator, ConstantEvaluatorError, Emitter},
1416
valid::{Capabilities, ModuleInfo, ValidationError, ValidationFlags, Validator},
1517
Arena, Block, Constant, Expression, Function, Handle, Literal, Module, Override, Range, Scalar,
@@ -55,6 +57,7 @@ pub enum PipelineConstantError {
5557
pub fn process_overrides<'a>(
5658
module: &'a Module,
5759
module_info: &'a ModuleInfo,
60+
entry_point: Option<(ir::ShaderStage, &str)>,
5861
pipeline_constants: &PipelineConstants,
5962
) -> Result<(Cow<'a, Module>, Cow<'a, ModuleInfo>), PipelineConstantError> {
6063
if module.overrides.is_empty() {
@@ -63,6 +66,16 @@ pub fn process_overrides<'a>(
6366

6467
let mut module = module.clone();
6568

69+
// If an entry point was specified, compact the module to remove anything
70+
// not reachable from that entry point. This is necessary because we may not
71+
// have values for overrides that are not reachable from the entry point.
72+
if let Some((ep_stage, ep_name)) = entry_point {
73+
module
74+
.entry_points
75+
.retain(|ep| ep.stage == ep_stage && ep.name == ep_name);
76+
}
77+
compact(&mut module, KeepUnused::No);
78+
6679
// A map from override handles to the handles of the constants
6780
// we've replaced them with.
6881
let mut override_map = HandleVec::with_capacity(module.overrides.len());

naga/src/compact/expressions.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ pub struct ExpressionTracer<'tracer> {
1111
/// The used map for `types`.
1212
pub types_used: &'tracer mut HandleSet<crate::Type>,
1313

14+
/// The used map for global variables.
15+
pub global_variables_used: &'tracer mut HandleSet<crate::GlobalVariable>,
16+
1417
/// The used map for `constants`.
1518
pub constants_used: &'tracer mut HandleSet<crate::Constant>,
1619

@@ -76,9 +79,7 @@ impl ExpressionTracer<'_> {
7679
// Expressions that do not contain handles that need to be traced.
7780
Ex::Literal(_)
7881
| Ex::FunctionArgument(_)
79-
| Ex::GlobalVariable(_)
8082
| Ex::LocalVariable(_)
81-
| Ex::CallResult(_)
8283
| Ex::SubgroupBallotResult
8384
| Ex::RayQueryProceedResult => {}
8485

@@ -134,6 +135,9 @@ impl ExpressionTracer<'_> {
134135
} => {
135136
self.expressions_used.insert(vector);
136137
}
138+
Ex::GlobalVariable(handle) => {
139+
self.global_variables_used.insert(handle);
140+
}
137141
Ex::Load { pointer } => {
138142
self.expressions_used.insert(pointer);
139143
}
@@ -233,6 +237,10 @@ impl ExpressionTracer<'_> {
233237
Ex::ArrayLength(expr) => {
234238
self.expressions_used.insert(expr);
235239
}
240+
// `CallResult` expressions do contain a function handle, but any used
241+
// `CallResult` expression should have an associated `ir::Statement::Call`
242+
// that we will trace.
243+
Ex::CallResult(_) => {}
236244
Ex::AtomicResult { ty, comparison: _ }
237245
| Ex::WorkGroupUniformLoadResult { ty }
238246
| Ex::SubgroupOperationResult { ty } => {
@@ -267,9 +275,7 @@ impl ModuleMap {
267275
// Expressions that do not contain handles that need to be adjusted.
268276
Ex::Literal(_)
269277
| Ex::FunctionArgument(_)
270-
| Ex::GlobalVariable(_)
271278
| Ex::LocalVariable(_)
272-
| Ex::CallResult(_)
273279
| Ex::SubgroupBallotResult
274280
| Ex::RayQueryProceedResult => {}
275281

@@ -306,6 +312,7 @@ impl ModuleMap {
306312
ref mut vector,
307313
pattern: _,
308314
} => adjust(vector),
315+
Ex::GlobalVariable(ref mut handle) => self.globals.adjust(handle),
309316
Ex::Load { ref mut pointer } => adjust(pointer),
310317
Ex::ImageSample {
311318
ref mut image,
@@ -392,6 +399,9 @@ impl ModuleMap {
392399
kind: _,
393400
convert: _,
394401
} => adjust(expr),
402+
Ex::CallResult(ref mut function) => {
403+
self.functions.adjust(function);
404+
}
395405
Ex::AtomicResult {
396406
ref mut ty,
397407
comparison: _,

naga/src/compact/functions.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ pub struct FunctionTracer<'a> {
66
pub constants: &'a crate::Arena<crate::Constant>,
77
pub overrides: &'a crate::Arena<crate::Override>,
88

9+
pub functions_pending: &'a mut HandleSet<crate::Function>,
10+
pub functions_used: &'a mut HandleSet<crate::Function>,
911
pub types_used: &'a mut HandleSet<crate::Type>,
12+
pub global_variables_used: &'a mut HandleSet<crate::GlobalVariable>,
1013
pub constants_used: &'a mut HandleSet<crate::Constant>,
1114
pub overrides_used: &'a mut HandleSet<crate::Override>,
1215
pub global_expressions_used: &'a mut HandleSet<crate::Expression>,
@@ -16,6 +19,13 @@ pub struct FunctionTracer<'a> {
1619
}
1720

1821
impl FunctionTracer<'_> {
22+
pub fn trace_call(&mut self, function: crate::Handle<crate::Function>) {
23+
if !self.functions_used.contains(function) {
24+
self.functions_used.insert(function);
25+
self.functions_pending.insert(function);
26+
}
27+
}
28+
1929
pub fn trace(&mut self) {
2030
for argument in self.function.arguments.iter() {
2131
self.types_used.insert(argument.ty);
@@ -53,6 +63,7 @@ impl FunctionTracer<'_> {
5363
expressions: &self.function.expressions,
5464

5565
types_used: self.types_used,
66+
global_variables_used: self.global_variables_used,
5667
constants_used: self.constants_used,
5768
overrides_used: self.overrides_used,
5869
expressions_used: &mut self.expressions_used,
@@ -105,6 +116,6 @@ impl FunctionMap {
105116
assert!(reuse.is_empty());
106117

107118
// Adjust statements.
108-
self.adjust_body(function);
119+
self.adjust_body(function, &module_map.functions);
109120
}
110121
}

naga/src/compact/handle_set_map.rs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,43 @@ use crate::arena::{Arena, Handle, HandleSet, Range};
44

55
type Index = crate::non_max_u32::NonMaxU32;
66

7-
/// A map from old handle indices to new, compressed handle indices.
8-
pub struct HandleMap<T> {
7+
/// A map keyed by handles.
8+
///
9+
/// In most cases, this is used to map from old handle indices to new,
10+
/// compressed handle indices.
11+
#[derive(Debug)]
12+
pub struct HandleMap<T, U = Index> {
913
/// The indices assigned to handles in the compacted module.
1014
///
1115
/// If `new_index[i]` is `Some(n)`, then `n` is the `Index` of the
1216
/// compacted `Handle` corresponding to the pre-compacted `Handle`
1317
/// whose index is `i`.
14-
new_index: Vec<Option<Index>>,
18+
new_index: Vec<Option<U>>,
1519

1620
/// This type is indexed by values of type `T`.
1721
as_keys: core::marker::PhantomData<T>,
1822
}
1923

24+
impl<T, U> HandleMap<T, U> {
25+
pub fn with_capacity(capacity: usize) -> Self {
26+
Self {
27+
new_index: Vec::with_capacity(capacity),
28+
as_keys: core::marker::PhantomData,
29+
}
30+
}
31+
32+
pub fn get(&self, handle: Handle<T>) -> Option<&U> {
33+
self.new_index.get(handle.index()).unwrap_or(&None).as_ref()
34+
}
35+
36+
pub fn insert(&mut self, handle: Handle<T>, value: U) -> Option<U> {
37+
if self.new_index.len() <= handle.index() {
38+
self.new_index.resize_with(handle.index() + 1, || None);
39+
}
40+
core::mem::replace(&mut self.new_index[handle.index()], Some(value))
41+
}
42+
}
43+
2044
impl<T: 'static> HandleMap<T> {
2145
pub fn from_set(set: HandleSet<T>) -> Self {
2246
let mut next_index = Index::new(0).unwrap();

0 commit comments

Comments
 (0)