Skip to content

Commit b81fcb4

Browse files
refactor(wgsl-in): track diagnostic directives in func. analysis
1 parent 290ea15 commit b81fcb4

40 files changed

+303
-12
lines changed

naga/src/diagnostic_filter.rs

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,24 @@
11
//! [`DiagnosticFilter`]s and supporting functionality.
22
3-
use crate::Handle;
43
#[cfg(feature = "wgsl-in")]
54
use crate::Span;
5+
use crate::{Arena, Handle};
6+
#[cfg(feature = "arbitrary")]
7+
use arbitrary::Arbitrary;
68
#[cfg(feature = "wgsl-in")]
79
use indexmap::IndexMap;
10+
#[cfg(feature = "deserialize")]
11+
use serde::Deserialize;
12+
#[cfg(feature = "serialize")]
13+
use serde::Serialize;
814

915
/// A severity set on a [`DiagnosticFilter`].
1016
///
1117
/// <https://www.w3.org/TR/WGSL/#diagnostic-severity>
1218
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
19+
#[cfg_attr(feature = "serialize", derive(Serialize))]
20+
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
21+
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
1322
pub enum Severity {
1423
Off,
1524
Info,
@@ -63,6 +72,9 @@ impl Severity {
6372
///
6473
/// <https://www.w3.org/TR/WGSL/#filterable-triggering-rules>
6574
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
75+
#[cfg_attr(feature = "serialize", derive(Serialize))]
76+
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
77+
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
6678
pub enum FilterableTriggeringRule {
6779
DerivativeUniformity,
6880
}
@@ -84,12 +96,26 @@ impl FilterableTriggeringRule {
8496
Self::DerivativeUniformity => Self::DERIVATIVE_UNIFORMITY,
8597
}
8698
}
99+
100+
/// The default severity associated with this triggering rule.
101+
///
102+
/// See <https://www.w3.org/TR/WGSL/#filterable-triggering-rules> for a table of default
103+
/// severities.
104+
#[allow(dead_code)]
105+
pub(crate) const fn default_severity(self) -> Severity {
106+
match self {
107+
FilterableTriggeringRule::DerivativeUniformity => Severity::Error,
108+
}
109+
}
87110
}
88111

89112
/// A filter that modifies how diagnostics are emitted for shaders.
90113
///
91114
/// <https://www.w3.org/TR/WGSL/#diagnostic-filter>
92115
#[derive(Clone, Debug)]
116+
#[cfg_attr(feature = "serialize", derive(Serialize))]
117+
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
118+
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
93119
pub struct DiagnosticFilter {
94120
pub new_severity: Severity,
95121
pub triggering_rule: FilterableTriggeringRule,
@@ -140,6 +166,18 @@ impl DiagnosticFilterMap {
140166
}
141167
}
142168

169+
#[cfg(feature = "wgsl-in")]
170+
impl IntoIterator for DiagnosticFilterMap {
171+
type Item = (FilterableTriggeringRule, (Severity, Span));
172+
173+
type IntoIter = indexmap::map::IntoIter<FilterableTriggeringRule, (Severity, Span)>;
174+
175+
fn into_iter(self) -> Self::IntoIter {
176+
let Self(this) = self;
177+
this.into_iter()
178+
}
179+
}
180+
143181
/// An error returned by [`DiagnosticFilterMap::add`] when it encounters conflicting rules.
144182
#[cfg(feature = "wgsl-in")]
145183
#[derive(Clone, Debug)]
@@ -175,7 +213,41 @@ pub(crate) struct ConflictingDiagnosticRuleError {
175213
/// - `d` is the first leaf consulted by validation in `c_and_d_func`.
176214
/// - `e` is the first leaf consulted by validation in `e_func`.
177215
#[derive(Clone, Debug)]
216+
#[cfg_attr(feature = "serialize", derive(Serialize))]
217+
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
218+
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
178219
pub struct DiagnosticFilterNode {
179220
pub inner: DiagnosticFilter,
180221
pub parent: Option<Handle<DiagnosticFilterNode>>,
181222
}
223+
224+
impl DiagnosticFilterNode {
225+
/// Finds the most specific filter rule applicable to `triggering_rule` from the chain of
226+
/// diagnostic filter rules in `arena`, starting with `node`, and returns its severity. If none
227+
/// is found, return the value of [`FilterableTriggeringRule::default_severity`].
228+
///
229+
/// When `triggering_rule` is not applicable to this node, its parent is consulted recursively.
230+
#[allow(dead_code)]
231+
pub(crate) fn search(
232+
node: Option<Handle<Self>>,
233+
arena: &Arena<Self>,
234+
triggering_rule: FilterableTriggeringRule,
235+
) -> Severity {
236+
let mut next = node;
237+
while let Some(handle) = next {
238+
let node = &arena[handle];
239+
let &Self { ref inner, parent } = node;
240+
let &DiagnosticFilter {
241+
triggering_rule: rule,
242+
new_severity,
243+
} = inner;
244+
245+
if rule == triggering_rule {
246+
return new_severity;
247+
}
248+
249+
next = parent;
250+
}
251+
triggering_rule.default_severity()
252+
}
253+
}

naga/src/front/wgsl/lower/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1013,7 +1013,11 @@ impl<'source, 'temp> Lowerer<'source, 'temp> {
10131013
&mut self,
10141014
tu: &'temp ast::TranslationUnit<'source>,
10151015
) -> Result<crate::Module, Error<'source>> {
1016-
let mut module = crate::Module::default();
1016+
let mut module = crate::Module {
1017+
diagnostic_filters: tu.diagnostic_filters.clone(),
1018+
diagnostic_filter_leaf: tu.diagnostic_filter_leaf,
1019+
..Default::default()
1020+
};
10171021

10181022
let mut ctx = GlobalContext {
10191023
ast_expressions: &tu.expressions,

naga/src/front/wgsl/parse/ast.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::diagnostic_filter::DiagnosticFilterNode;
12
use crate::front::wgsl::parse::directive::enable_extension::EnableExtensions;
23
use crate::front::wgsl::parse::number::Number;
34
use crate::front::wgsl::Scalar;
@@ -26,6 +27,17 @@ pub struct TranslationUnit<'a> {
2627
/// These are referred to by `Handle<ast::Type<'a>>` values.
2728
/// User-defined types are referred to by name until lowering.
2829
pub types: Arena<Type<'a>>,
30+
31+
/// Arena for all diagnostic filter rules parsed in this module, including those in functions.
32+
///
33+
/// See [`DiagnosticFilterNode`] for details on how the tree is represented and used in
34+
/// validation.
35+
pub diagnostic_filters: Arena<DiagnosticFilterNode>,
36+
/// The leaf of all `diagnostic(…)` directives in this module.
37+
///
38+
/// See [`DiagnosticFilterNode`] for details on how the tree is represented and used in
39+
/// validation.
40+
pub diagnostic_filter_leaf: Option<Handle<DiagnosticFilterNode>>,
2941
}
3042

3143
#[derive(Debug, Clone, Copy)]

naga/src/front/wgsl/parse/mod.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::diagnostic_filter::{
2-
self, DiagnosticFilter, DiagnosticFilterMap, FilterableTriggeringRule,
2+
self, DiagnosticFilter, DiagnosticFilterMap, DiagnosticFilterNode, FilterableTriggeringRule,
33
};
44
use crate::front::wgsl::error::{Error, ExpectedToken};
55
use crate::front::wgsl::parse::directive::enable_extension::{
@@ -2581,6 +2581,8 @@ impl Parser {
25812581

25822582
lexer.enable_extensions = enable_extensions.clone();
25832583
tu.enable_extensions = enable_extensions;
2584+
tu.diagnostic_filter_leaf =
2585+
Self::write_diagnostic_filters(&mut tu.diagnostic_filters, diagnostic_filters, None);
25842586

25852587
loop {
25862588
match self.global_decl(&mut lexer, &mut tu) {
@@ -2672,4 +2674,25 @@ impl Parser {
26722674

26732675
Ok(filter)
26742676
}
2677+
2678+
pub(crate) fn write_diagnostic_filters(
2679+
arena: &mut Arena<DiagnosticFilterNode>,
2680+
filters: DiagnosticFilterMap,
2681+
parent: Option<Handle<DiagnosticFilterNode>>,
2682+
) -> Option<Handle<DiagnosticFilterNode>> {
2683+
filters
2684+
.into_iter()
2685+
.fold(parent, |parent, (triggering_rule, (new_severity, span))| {
2686+
Some(arena.append(
2687+
DiagnosticFilterNode {
2688+
inner: DiagnosticFilter {
2689+
new_severity,
2690+
triggering_rule,
2691+
},
2692+
parent,
2693+
},
2694+
span,
2695+
))
2696+
})
2697+
}
26752698
}

naga/src/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ pub use crate::arena::{Arena, Handle, Range, UniqueArena};
269269
pub use crate::span::{SourceLocation, Span, SpanContext, WithSpan};
270270
#[cfg(feature = "arbitrary")]
271271
use arbitrary::Arbitrary;
272+
use diagnostic_filter::DiagnosticFilterNode;
272273
#[cfg(feature = "deserialize")]
273274
use serde::Deserialize;
274275
#[cfg(feature = "serialize")]
@@ -2272,4 +2273,17 @@ pub struct Module {
22722273
pub functions: Arena<Function>,
22732274
/// Entry points.
22742275
pub entry_points: Vec<EntryPoint>,
2276+
/// Arena for all diagnostic filter rules parsed in this module, including those in functions
2277+
/// and statements.
2278+
///
2279+
/// This arena contains elements of a _tree_ of diagnostic filter rules. When nodes are built
2280+
/// by a front-end, they refer to a parent scope
2281+
pub diagnostic_filters: Arena<DiagnosticFilterNode>,
2282+
/// The leaf of all diagnostic filter rules tree parsed from directives in this module.
2283+
///
2284+
/// In WGSL, this corresponds to `diagnostic(…);` directives.
2285+
///
2286+
/// See [`DiagnosticFilterNode`] for details on how the tree is represented and used in
2287+
/// validation.
2288+
pub diagnostic_filter_leaf: Option<Handle<DiagnosticFilterNode>>,
22752289
}

naga/src/valid/analyzer.rs

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//! - expression reference counts
77
88
use super::{ExpressionError, FunctionError, ModuleInfo, ShaderStages, ValidationFlags};
9+
use crate::diagnostic_filter::DiagnosticFilterNode;
910
use crate::span::{AddSpan as _, WithSpan};
1011
use crate::{
1112
arena::{Arena, Handle},
@@ -289,6 +290,13 @@ pub struct FunctionInfo {
289290

290291
/// Indicates that the function is using dual source blending.
291292
pub dual_source_blending: bool,
293+
294+
/// The leaf of all module-wide diagnostic filter rules tree parsed from directives in this
295+
/// module.
296+
///
297+
/// See [`DiagnosticFilterNode`] for details on how the tree is represented and used in
298+
/// validation.
299+
diagnostic_filter_leaf: Option<Handle<DiagnosticFilterNode>>,
292300
}
293301

294302
impl FunctionInfo {
@@ -820,12 +828,14 @@ impl FunctionInfo {
820828
/// Returns a `NonUniformControlFlow` error if any of the expressions in the block
821829
/// require uniformity, but the current flow is non-uniform.
822830
#[allow(clippy::or_fun_call)]
831+
#[allow(clippy::only_used_in_recursion)]
823832
fn process_block(
824833
&mut self,
825834
statements: &crate::Block,
826835
other_functions: &[FunctionInfo],
827836
mut disruptor: Option<UniformityDisruptor>,
828837
expression_arena: &Arena<crate::Expression>,
838+
diagnostic_filter_arena: &Arena<DiagnosticFilterNode>,
829839
) -> Result<FunctionUniformity, WithSpan<FunctionError>> {
830840
use crate::Statement as S;
831841

@@ -901,9 +911,13 @@ impl FunctionInfo {
901911
exit: ExitFlags::empty(),
902912
}
903913
}
904-
S::Block(ref b) => {
905-
self.process_block(b, other_functions, disruptor, expression_arena)?
906-
}
914+
S::Block(ref b) => self.process_block(
915+
b,
916+
other_functions,
917+
disruptor,
918+
expression_arena,
919+
diagnostic_filter_arena,
920+
)?,
907921
S::If {
908922
condition,
909923
ref accept,
@@ -917,12 +931,14 @@ impl FunctionInfo {
917931
other_functions,
918932
branch_disruptor,
919933
expression_arena,
934+
diagnostic_filter_arena,
920935
)?;
921936
let reject_uniformity = self.process_block(
922937
reject,
923938
other_functions,
924939
branch_disruptor,
925940
expression_arena,
941+
diagnostic_filter_arena,
926942
)?;
927943
accept_uniformity | reject_uniformity
928944
}
@@ -941,6 +957,7 @@ impl FunctionInfo {
941957
other_functions,
942958
case_disruptor,
943959
expression_arena,
960+
diagnostic_filter_arena,
944961
)?;
945962
case_disruptor = if case.fall_through {
946963
case_disruptor.or(case_uniformity.exit_disruptor())
@@ -956,14 +973,20 @@ impl FunctionInfo {
956973
ref continuing,
957974
break_if,
958975
} => {
959-
let body_uniformity =
960-
self.process_block(body, other_functions, disruptor, expression_arena)?;
976+
let body_uniformity = self.process_block(
977+
body,
978+
other_functions,
979+
disruptor,
980+
expression_arena,
981+
diagnostic_filter_arena,
982+
)?;
961983
let continuing_disruptor = disruptor.or(body_uniformity.exit_disruptor());
962984
let continuing_uniformity = self.process_block(
963985
continuing,
964986
other_functions,
965987
continuing_disruptor,
966988
expression_arena,
989+
diagnostic_filter_arena,
967990
)?;
968991
if let Some(expr) = break_if {
969992
let _ = self.add_ref(expr);
@@ -1117,6 +1140,7 @@ impl ModuleInfo {
11171140
expressions: vec![ExpressionInfo::new(); fun.expressions.len()].into_boxed_slice(),
11181141
sampling: crate::FastHashSet::default(),
11191142
dual_source_blending: false,
1143+
diagnostic_filter_leaf: module.diagnostic_filter_leaf,
11201144
};
11211145
let resolve_context =
11221146
ResolveContext::with_locals(module, &fun.local_variables, &fun.arguments);
@@ -1140,7 +1164,13 @@ impl ModuleInfo {
11401164
}
11411165
}
11421166

1143-
let uniformity = info.process_block(&fun.body, &self.functions, None, &fun.expressions)?;
1167+
let uniformity = info.process_block(
1168+
&fun.body,
1169+
&self.functions,
1170+
None,
1171+
&fun.expressions,
1172+
&module.diagnostic_filters,
1173+
)?;
11441174
info.uniformity = uniformity.result;
11451175
info.may_kill = uniformity.exit.contains(ExitFlags::MAY_KILL);
11461176

@@ -1230,6 +1260,7 @@ fn uniform_control_flow() {
12301260
expressions: vec![ExpressionInfo::new(); expressions.len()].into_boxed_slice(),
12311261
sampling: crate::FastHashSet::default(),
12321262
dual_source_blending: false,
1263+
diagnostic_filter_leaf: None,
12331264
};
12341265
let resolve_context = ResolveContext {
12351266
constants: &Arena::new(),
@@ -1276,7 +1307,8 @@ fn uniform_control_flow() {
12761307
&vec![stmt_emit1, stmt_if_uniform].into(),
12771308
&[],
12781309
None,
1279-
&expressions
1310+
&expressions,
1311+
&Arena::new(),
12801312
),
12811313
Ok(FunctionUniformity {
12821314
result: Uniformity {
@@ -1308,6 +1340,7 @@ fn uniform_control_flow() {
13081340
&[],
13091341
None,
13101342
&expressions,
1343+
&Arena::new(),
13111344
);
13121345
if DISABLE_UNIFORMITY_REQ_FOR_FRAGMENT_STAGE {
13131346
assert_eq!(info[derivative_expr].ref_count, 2);
@@ -1335,7 +1368,8 @@ fn uniform_control_flow() {
13351368
&vec![stmt_emit3, stmt_return_non_uniform].into(),
13361369
&[],
13371370
Some(UniformityDisruptor::Return),
1338-
&expressions
1371+
&expressions,
1372+
&Arena::new(),
13391373
),
13401374
Ok(FunctionUniformity {
13411375
result: Uniformity {
@@ -1362,7 +1396,8 @@ fn uniform_control_flow() {
13621396
&vec![stmt_emit4, stmt_assign, stmt_kill, stmt_return_pointer].into(),
13631397
&[],
13641398
Some(UniformityDisruptor::Discard),
1365-
&expressions
1399+
&expressions,
1400+
&Arena::new(),
13661401
),
13671402
Ok(FunctionUniformity {
13681403
result: Uniformity {

0 commit comments

Comments
 (0)