/src/wgpu/naga/src/front/wgsl/lower/mod.rs
Line | Count | Source |
1 | | use alloc::{ |
2 | | borrow::ToOwned, |
3 | | boxed::Box, |
4 | | format, |
5 | | string::{String, ToString}, |
6 | | vec::Vec, |
7 | | }; |
8 | | use core::num::NonZeroU32; |
9 | | |
10 | | use crate::front::wgsl::error::{Error, ExpectedToken, InvalidAssignmentType}; |
11 | | use crate::front::wgsl::index::Index; |
12 | | use crate::front::wgsl::parse::number::Number; |
13 | | use crate::front::wgsl::parse::{ast, conv}; |
14 | | use crate::front::wgsl::Result; |
15 | | use crate::front::Typifier; |
16 | | use crate::{ |
17 | | common::wgsl::{TryToWgsl, TypeContext}, |
18 | | compact::KeepUnused, |
19 | | }; |
20 | | use crate::{common::ForDebugWithTypes, proc::LayoutErrorInner}; |
21 | | use crate::{ir, proc}; |
22 | | use crate::{Arena, FastHashMap, FastIndexMap, Handle, Span}; |
23 | | |
24 | | mod construction; |
25 | | mod conversion; |
26 | | |
27 | | /// Resolves the inner type of a given expression. |
28 | | /// |
29 | | /// Expects a &mut [`ExpressionContext`] and a [`Handle<Expression>`]. |
30 | | /// |
31 | | /// Returns a &[`ir::TypeInner`]. |
32 | | /// |
33 | | /// Ideally, we would simply have a function that takes a `&mut ExpressionContext` |
34 | | /// and returns a `&TypeResolution`. Unfortunately, this leads the borrow checker |
35 | | /// to conclude that the mutable borrow lasts for as long as we are using the |
36 | | /// `&TypeResolution`, so we can't use the `ExpressionContext` for anything else - |
37 | | /// like, say, resolving another operand's type. Using a macro that expands to |
38 | | /// two separate calls, only the first of which needs a `&mut`, |
39 | | /// lets the borrow checker see that the mutable borrow is over. |
40 | | macro_rules! resolve_inner { |
41 | | ($ctx:ident, $expr:expr) => {{ |
42 | | $ctx.grow_types($expr)?; |
43 | | $ctx.typifier()[$expr].inner_with(&$ctx.module.types) |
44 | | }}; |
45 | | } |
46 | | pub(super) use resolve_inner; |
47 | | |
48 | | /// Resolves the inner types of two given expressions. |
49 | | /// |
50 | | /// Expects a &mut [`ExpressionContext`] and two [`Handle<Expression>`]s. |
51 | | /// |
52 | | /// Returns a tuple containing two &[`ir::TypeInner`]. |
53 | | /// |
54 | | /// See the documentation of [`resolve_inner!`] for why this macro is necessary. |
55 | | macro_rules! resolve_inner_binary { |
56 | | ($ctx:ident, $left:expr, $right:expr) => {{ |
57 | | $ctx.grow_types($left)?; |
58 | | $ctx.grow_types($right)?; |
59 | | ( |
60 | | $ctx.typifier()[$left].inner_with(&$ctx.module.types), |
61 | | $ctx.typifier()[$right].inner_with(&$ctx.module.types), |
62 | | ) |
63 | | }}; |
64 | | } |
65 | | |
66 | | /// Resolves the type of a given expression. |
67 | | /// |
68 | | /// Expects a &mut [`ExpressionContext`] and a [`Handle<Expression>`]. |
69 | | /// |
70 | | /// Returns a &[`TypeResolution`]. |
71 | | /// |
72 | | /// See the documentation of [`resolve_inner!`] for why this macro is necessary. |
73 | | /// |
74 | | /// [`TypeResolution`]: proc::TypeResolution |
75 | | macro_rules! resolve { |
76 | | ($ctx:ident, $expr:expr) => {{ |
77 | | let expr = $expr; |
78 | | $ctx.grow_types(expr)?; |
79 | | &$ctx.typifier()[expr] |
80 | | }}; |
81 | | } |
82 | | pub(super) use resolve; |
83 | | |
84 | | /// State for constructing a `ir::Module`. |
85 | | pub struct GlobalContext<'source, 'temp, 'out> { |
86 | | /// The `TranslationUnit`'s expressions arena. |
87 | | ast_expressions: &'temp Arena<ast::Expression<'source>>, |
88 | | |
89 | | /// The `TranslationUnit`'s types arena. |
90 | | types: &'temp Arena<ast::Type<'source>>, |
91 | | |
92 | | // Naga IR values. |
93 | | /// The map from the names of module-scope declarations to the Naga IR |
94 | | /// `Handle`s we have built for them, owned by `Lowerer::lower`. |
95 | | globals: &'temp mut FastHashMap<&'source str, LoweredGlobalDecl>, |
96 | | |
97 | | /// The module we're constructing. |
98 | | module: &'out mut ir::Module, |
99 | | |
100 | | const_typifier: &'temp mut Typifier, |
101 | | |
102 | | layouter: &'temp mut proc::Layouter, |
103 | | |
104 | | global_expression_kind_tracker: &'temp mut proc::ExpressionKindTracker, |
105 | | } |
106 | | |
107 | | impl<'source> GlobalContext<'source, '_, '_> { |
108 | 802 | fn as_const(&mut self) -> ExpressionContext<'source, '_, '_> { |
109 | 802 | ExpressionContext { |
110 | 802 | ast_expressions: self.ast_expressions, |
111 | 802 | globals: self.globals, |
112 | 802 | types: self.types, |
113 | 802 | module: self.module, |
114 | 802 | const_typifier: self.const_typifier, |
115 | 802 | layouter: self.layouter, |
116 | 802 | expr_type: ExpressionContextType::Constant(None), |
117 | 802 | global_expression_kind_tracker: self.global_expression_kind_tracker, |
118 | 802 | } |
119 | 802 | } |
120 | | |
121 | 0 | fn as_override(&mut self) -> ExpressionContext<'source, '_, '_> { |
122 | 0 | ExpressionContext { |
123 | 0 | ast_expressions: self.ast_expressions, |
124 | 0 | globals: self.globals, |
125 | 0 | types: self.types, |
126 | 0 | module: self.module, |
127 | 0 | const_typifier: self.const_typifier, |
128 | 0 | layouter: self.layouter, |
129 | 0 | expr_type: ExpressionContextType::Override, |
130 | 0 | global_expression_kind_tracker: self.global_expression_kind_tracker, |
131 | 0 | } |
132 | 0 | } |
133 | | |
134 | 23.4k | fn ensure_type_exists( |
135 | 23.4k | &mut self, |
136 | 23.4k | name: Option<String>, |
137 | 23.4k | inner: ir::TypeInner, |
138 | 23.4k | ) -> Handle<ir::Type> { |
139 | 23.4k | self.module |
140 | 23.4k | .types |
141 | 23.4k | .insert(ir::Type { inner, name }, Span::UNDEFINED) |
142 | 23.4k | } |
143 | | } |
144 | | |
145 | | /// State for lowering a statement within a function. |
146 | | pub struct StatementContext<'source, 'temp, 'out> { |
147 | | // WGSL AST values. |
148 | | /// A reference to [`TranslationUnit::expressions`] for the translation unit |
149 | | /// we're lowering. |
150 | | /// |
151 | | /// [`TranslationUnit::expressions`]: ast::TranslationUnit::expressions |
152 | | ast_expressions: &'temp Arena<ast::Expression<'source>>, |
153 | | |
154 | | /// A reference to [`TranslationUnit::types`] for the translation unit |
155 | | /// we're lowering. |
156 | | /// |
157 | | /// [`TranslationUnit::types`]: ast::TranslationUnit::types |
158 | | types: &'temp Arena<ast::Type<'source>>, |
159 | | |
160 | | // Naga IR values. |
161 | | /// The map from the names of module-scope declarations to the Naga IR |
162 | | /// `Handle`s we have built for them, owned by `Lowerer::lower`. |
163 | | globals: &'temp mut FastHashMap<&'source str, LoweredGlobalDecl>, |
164 | | |
165 | | /// A map from each `ast::Local` handle to the Naga expression |
166 | | /// we've built for it: |
167 | | /// |
168 | | /// - WGSL function arguments become Naga [`FunctionArgument`] expressions. |
169 | | /// |
170 | | /// - WGSL `var` declarations become Naga [`LocalVariable`] expressions. |
171 | | /// |
172 | | /// - WGSL `let` declararations become arbitrary Naga expressions. |
173 | | /// |
174 | | /// This always borrows the `local_table` local variable in |
175 | | /// [`Lowerer::function`]. |
176 | | /// |
177 | | /// [`LocalVariable`]: ir::Expression::LocalVariable |
178 | | /// [`FunctionArgument`]: ir::Expression::FunctionArgument |
179 | | local_table: |
180 | | &'temp mut FastHashMap<Handle<ast::Local>, Declared<Typed<Handle<ir::Expression>>>>, |
181 | | |
182 | | const_typifier: &'temp mut Typifier, |
183 | | typifier: &'temp mut Typifier, |
184 | | layouter: &'temp mut proc::Layouter, |
185 | | function: &'out mut ir::Function, |
186 | | /// Stores the names of expressions that are assigned in `let` statement |
187 | | /// Also stores the spans of the names, for use in errors. |
188 | | named_expressions: &'out mut FastIndexMap<Handle<ir::Expression>, (String, Span)>, |
189 | | module: &'out mut ir::Module, |
190 | | |
191 | | /// Which `Expression`s in `self.naga_expressions` are const expressions, in |
192 | | /// the WGSL sense. |
193 | | /// |
194 | | /// According to the WGSL spec, a const expression must not refer to any |
195 | | /// `let` declarations, even if those declarations' initializers are |
196 | | /// themselves const expressions. So this tracker is not simply concerned |
197 | | /// with the form of the expressions; it is also tracking whether WGSL says |
198 | | /// we should consider them to be const. See the use of `force_non_const` in |
199 | | /// the code for lowering `let` bindings. |
200 | | local_expression_kind_tracker: &'temp mut proc::ExpressionKindTracker, |
201 | | global_expression_kind_tracker: &'temp mut proc::ExpressionKindTracker, |
202 | | } |
203 | | |
204 | | impl<'a, 'temp> StatementContext<'a, 'temp, '_> { |
205 | 1 | fn as_const<'t>( |
206 | 1 | &'t mut self, |
207 | 1 | block: &'t mut ir::Block, |
208 | 1 | emitter: &'t mut proc::Emitter, |
209 | 1 | ) -> ExpressionContext<'a, 't, 't> |
210 | 1 | where |
211 | 1 | 'temp: 't, |
212 | | { |
213 | 1 | ExpressionContext { |
214 | 1 | globals: self.globals, |
215 | 1 | types: self.types, |
216 | 1 | ast_expressions: self.ast_expressions, |
217 | 1 | const_typifier: self.const_typifier, |
218 | 1 | layouter: self.layouter, |
219 | 1 | global_expression_kind_tracker: self.global_expression_kind_tracker, |
220 | 1 | module: self.module, |
221 | 1 | expr_type: ExpressionContextType::Constant(Some(LocalExpressionContext { |
222 | 1 | local_table: self.local_table, |
223 | 1 | function: self.function, |
224 | 1 | block, |
225 | 1 | emitter, |
226 | 1 | typifier: self.typifier, |
227 | 1 | local_expression_kind_tracker: self.local_expression_kind_tracker, |
228 | 1 | })), |
229 | 1 | } |
230 | 1 | } |
231 | | |
232 | 14.1k | fn as_expression<'t>( |
233 | 14.1k | &'t mut self, |
234 | 14.1k | block: &'t mut ir::Block, |
235 | 14.1k | emitter: &'t mut proc::Emitter, |
236 | 14.1k | ) -> ExpressionContext<'a, 't, 't> |
237 | 14.1k | where |
238 | 14.1k | 'temp: 't, |
239 | | { |
240 | 14.1k | ExpressionContext { |
241 | 14.1k | globals: self.globals, |
242 | 14.1k | types: self.types, |
243 | 14.1k | ast_expressions: self.ast_expressions, |
244 | 14.1k | const_typifier: self.const_typifier, |
245 | 14.1k | layouter: self.layouter, |
246 | 14.1k | global_expression_kind_tracker: self.global_expression_kind_tracker, |
247 | 14.1k | module: self.module, |
248 | 14.1k | expr_type: ExpressionContextType::Runtime(LocalExpressionContext { |
249 | 14.1k | local_table: self.local_table, |
250 | 14.1k | function: self.function, |
251 | 14.1k | block, |
252 | 14.1k | emitter, |
253 | 14.1k | typifier: self.typifier, |
254 | 14.1k | local_expression_kind_tracker: self.local_expression_kind_tracker, |
255 | 14.1k | }), |
256 | 14.1k | } |
257 | 14.1k | } |
258 | | |
259 | | #[allow(dead_code)] |
260 | 0 | fn as_global(&mut self) -> GlobalContext<'a, '_, '_> { |
261 | 0 | GlobalContext { |
262 | 0 | ast_expressions: self.ast_expressions, |
263 | 0 | globals: self.globals, |
264 | 0 | types: self.types, |
265 | 0 | module: self.module, |
266 | 0 | const_typifier: self.const_typifier, |
267 | 0 | layouter: self.layouter, |
268 | 0 | global_expression_kind_tracker: self.global_expression_kind_tracker, |
269 | 0 | } |
270 | 0 | } |
271 | | |
272 | 0 | fn invalid_assignment_type(&self, expr: Handle<ir::Expression>) -> InvalidAssignmentType { |
273 | 0 | if let Some(&(_, span)) = self.named_expressions.get(&expr) { |
274 | 0 | InvalidAssignmentType::ImmutableBinding(span) |
275 | | } else { |
276 | 0 | match self.function.expressions[expr] { |
277 | 0 | ir::Expression::Swizzle { .. } => InvalidAssignmentType::Swizzle, |
278 | 0 | ir::Expression::Access { base, .. } => self.invalid_assignment_type(base), |
279 | 0 | ir::Expression::AccessIndex { base, .. } => self.invalid_assignment_type(base), |
280 | 0 | _ => InvalidAssignmentType::Other, |
281 | | } |
282 | | } |
283 | 0 | } |
284 | | } |
285 | | |
286 | | pub struct LocalExpressionContext<'temp, 'out> { |
287 | | /// A map from [`ast::Local`] handles to the Naga expressions we've built for them. |
288 | | /// |
289 | | /// This is always [`StatementContext::local_table`] for the |
290 | | /// enclosing statement; see that documentation for details. |
291 | | local_table: &'temp FastHashMap<Handle<ast::Local>, Declared<Typed<Handle<ir::Expression>>>>, |
292 | | |
293 | | function: &'out mut ir::Function, |
294 | | block: &'temp mut ir::Block, |
295 | | emitter: &'temp mut proc::Emitter, |
296 | | typifier: &'temp mut Typifier, |
297 | | |
298 | | /// Which `Expression`s in `self.naga_expressions` are const expressions, in |
299 | | /// the WGSL sense. |
300 | | /// |
301 | | /// See [`StatementContext::local_expression_kind_tracker`] for details. |
302 | | local_expression_kind_tracker: &'temp mut proc::ExpressionKindTracker, |
303 | | } |
304 | | |
305 | | /// The type of Naga IR expression we are lowering an [`ast::Expression`] to. |
306 | | pub enum ExpressionContextType<'temp, 'out> { |
307 | | /// We are lowering to an arbitrary runtime expression, to be |
308 | | /// included in a function's body. |
309 | | /// |
310 | | /// The given [`LocalExpressionContext`] holds information about local |
311 | | /// variables, arguments, and other definitions available only to runtime |
312 | | /// expressions, not constant or override expressions. |
313 | | Runtime(LocalExpressionContext<'temp, 'out>), |
314 | | |
315 | | /// We are lowering to a constant expression, to be included in the module's |
316 | | /// constant expression arena. |
317 | | /// |
318 | | /// Everything global constant expressions are allowed to refer to is |
319 | | /// available in the [`ExpressionContext`], but local constant expressions can |
320 | | /// also refer to other |
321 | | Constant(Option<LocalExpressionContext<'temp, 'out>>), |
322 | | |
323 | | /// We are lowering to an override expression, to be included in the module's |
324 | | /// constant expression arena. |
325 | | /// |
326 | | /// Everything override expressions are allowed to refer to is |
327 | | /// available in the [`ExpressionContext`], so this variant |
328 | | /// carries no further information. |
329 | | Override, |
330 | | } |
331 | | |
332 | | /// State for lowering an [`ast::Expression`] to Naga IR. |
333 | | /// |
334 | | /// [`ExpressionContext`]s come in two kinds, distinguished by |
335 | | /// the value of the [`expr_type`] field: |
336 | | /// |
337 | | /// - A [`Runtime`] context contributes [`naga::Expression`]s to a [`naga::Function`]'s |
338 | | /// runtime expression arena. |
339 | | /// |
340 | | /// - A [`Constant`] context contributes [`naga::Expression`]s to a [`naga::Module`]'s |
341 | | /// constant expression arena. |
342 | | /// |
343 | | /// [`ExpressionContext`]s are constructed in restricted ways: |
344 | | /// |
345 | | /// - To get a [`Runtime`] [`ExpressionContext`], call |
346 | | /// [`StatementContext::as_expression`]. |
347 | | /// |
348 | | /// - To get a [`Constant`] [`ExpressionContext`], call |
349 | | /// [`GlobalContext::as_const`]. |
350 | | /// |
351 | | /// - You can demote a [`Runtime`] context to a [`Constant`] context |
352 | | /// by calling [`as_const`], but there's no way to go in the other |
353 | | /// direction, producing a runtime context from a constant one. This |
354 | | /// is because runtime expressions can refer to constant |
355 | | /// expressions, via [`Expression::Constant`], but constant |
356 | | /// expressions can't refer to a function's expressions. |
357 | | /// |
358 | | /// Not to be confused with `wgsl::parse::ExpressionContext`, which is |
359 | | /// for parsing the `ast::Expression` in the first place. |
360 | | /// |
361 | | /// [`expr_type`]: ExpressionContext::expr_type |
362 | | /// [`Runtime`]: ExpressionContextType::Runtime |
363 | | /// [`naga::Expression`]: ir::Expression |
364 | | /// [`naga::Function`]: ir::Function |
365 | | /// [`Constant`]: ExpressionContextType::Constant |
366 | | /// [`naga::Module`]: ir::Module |
367 | | /// [`as_const`]: ExpressionContext::as_const |
368 | | /// [`Expression::Constant`]: ir::Expression::Constant |
369 | | pub struct ExpressionContext<'source, 'temp, 'out> { |
370 | | // WGSL AST values. |
371 | | ast_expressions: &'temp Arena<ast::Expression<'source>>, |
372 | | types: &'temp Arena<ast::Type<'source>>, |
373 | | |
374 | | // Naga IR values. |
375 | | /// The map from the names of module-scope declarations to the Naga IR |
376 | | /// `Handle`s we have built for them, owned by `Lowerer::lower`. |
377 | | globals: &'temp mut FastHashMap<&'source str, LoweredGlobalDecl>, |
378 | | |
379 | | /// The IR [`Module`] we're constructing. |
380 | | /// |
381 | | /// [`Module`]: ir::Module |
382 | | module: &'out mut ir::Module, |
383 | | |
384 | | /// Type judgments for [`module::global_expressions`]. |
385 | | /// |
386 | | /// [`module::global_expressions`]: ir::Module::global_expressions |
387 | | const_typifier: &'temp mut Typifier, |
388 | | layouter: &'temp mut proc::Layouter, |
389 | | global_expression_kind_tracker: &'temp mut proc::ExpressionKindTracker, |
390 | | |
391 | | /// Whether we are lowering a constant expression or a general |
392 | | /// runtime expression, and the data needed in each case. |
393 | | expr_type: ExpressionContextType<'temp, 'out>, |
394 | | } |
395 | | |
396 | | impl TypeContext for ExpressionContext<'_, '_, '_> { |
397 | 0 | fn lookup_type(&self, handle: Handle<ir::Type>) -> &ir::Type { |
398 | 0 | &self.module.types[handle] |
399 | 0 | } |
400 | | |
401 | 0 | fn type_name(&self, handle: Handle<ir::Type>) -> &str { |
402 | 0 | self.module.types[handle] |
403 | 0 | .name |
404 | 0 | .as_deref() |
405 | 0 | .unwrap_or("{anonymous type}") |
406 | 0 | } |
407 | | |
408 | 0 | fn write_override<W: core::fmt::Write>( |
409 | 0 | &self, |
410 | 0 | handle: Handle<ir::Override>, |
411 | 0 | out: &mut W, |
412 | 0 | ) -> core::fmt::Result { |
413 | 0 | match self.module.overrides[handle].name { |
414 | 0 | Some(ref name) => out.write_str(name), |
415 | 0 | None => write!(out, "{{anonymous override {handle:?}}}"), |
416 | | } |
417 | 0 | } |
418 | | |
419 | 0 | fn write_unnamed_struct<W: core::fmt::Write>( |
420 | 0 | &self, |
421 | 0 | _: &ir::TypeInner, |
422 | 0 | _: &mut W, |
423 | 0 | ) -> core::fmt::Result { |
424 | 0 | unreachable!("the WGSL front end should always know the type name"); |
425 | | } |
426 | | } |
427 | | |
428 | | impl<'source, 'temp, 'out> ExpressionContext<'source, 'temp, 'out> { |
429 | | #[allow(dead_code)] |
430 | 9.36k | fn as_const(&mut self) -> ExpressionContext<'source, '_, '_> { |
431 | | ExpressionContext { |
432 | 9.36k | globals: self.globals, |
433 | 9.36k | types: self.types, |
434 | 9.36k | ast_expressions: self.ast_expressions, |
435 | 9.36k | const_typifier: self.const_typifier, |
436 | 9.36k | layouter: self.layouter, |
437 | 9.36k | module: self.module, |
438 | 3 | expr_type: ExpressionContextType::Constant(match self.expr_type { |
439 | 9.35k | ExpressionContextType::Runtime(ref mut local_expression_context) |
440 | 3 | | ExpressionContextType::Constant(Some(ref mut local_expression_context)) => { |
441 | 9.36k | Some(LocalExpressionContext { |
442 | 9.36k | local_table: local_expression_context.local_table, |
443 | 9.36k | function: local_expression_context.function, |
444 | 9.36k | block: local_expression_context.block, |
445 | 9.36k | emitter: local_expression_context.emitter, |
446 | 9.36k | typifier: local_expression_context.typifier, |
447 | 9.36k | local_expression_kind_tracker: local_expression_context |
448 | 9.36k | .local_expression_kind_tracker, |
449 | 9.36k | }) |
450 | | } |
451 | 0 | ExpressionContextType::Constant(None) | ExpressionContextType::Override => None, |
452 | | }), |
453 | 9.36k | global_expression_kind_tracker: self.global_expression_kind_tracker, |
454 | | } |
455 | 9.36k | } |
456 | | |
457 | 23.4k | fn as_global(&mut self) -> GlobalContext<'source, '_, '_> { |
458 | 23.4k | GlobalContext { |
459 | 23.4k | ast_expressions: self.ast_expressions, |
460 | 23.4k | globals: self.globals, |
461 | 23.4k | types: self.types, |
462 | 23.4k | module: self.module, |
463 | 23.4k | const_typifier: self.const_typifier, |
464 | 23.4k | layouter: self.layouter, |
465 | 23.4k | global_expression_kind_tracker: self.global_expression_kind_tracker, |
466 | 23.4k | } |
467 | 23.4k | } |
468 | | |
469 | 40.0k | fn as_const_evaluator(&mut self) -> proc::ConstantEvaluator<'_> { |
470 | 2.34k | match self.expr_type { |
471 | 37.7k | ExpressionContextType::Runtime(ref mut rctx) => { |
472 | 37.7k | proc::ConstantEvaluator::for_wgsl_function( |
473 | 37.7k | self.module, |
474 | 37.7k | &mut rctx.function.expressions, |
475 | 37.7k | rctx.local_expression_kind_tracker, |
476 | 37.7k | self.layouter, |
477 | 37.7k | rctx.emitter, |
478 | 37.7k | rctx.block, |
479 | | false, |
480 | | ) |
481 | | } |
482 | 11 | ExpressionContextType::Constant(Some(ref mut rctx)) => { |
483 | 11 | proc::ConstantEvaluator::for_wgsl_function( |
484 | 11 | self.module, |
485 | 11 | &mut rctx.function.expressions, |
486 | 11 | rctx.local_expression_kind_tracker, |
487 | 11 | self.layouter, |
488 | 11 | rctx.emitter, |
489 | 11 | rctx.block, |
490 | | true, |
491 | | ) |
492 | | } |
493 | 2.33k | ExpressionContextType::Constant(None) => proc::ConstantEvaluator::for_wgsl_module( |
494 | 2.33k | self.module, |
495 | 2.33k | self.global_expression_kind_tracker, |
496 | 2.33k | self.layouter, |
497 | | false, |
498 | | ), |
499 | 0 | ExpressionContextType::Override => proc::ConstantEvaluator::for_wgsl_module( |
500 | 0 | self.module, |
501 | 0 | self.global_expression_kind_tracker, |
502 | 0 | self.layouter, |
503 | | true, |
504 | | ), |
505 | | } |
506 | 40.0k | } |
507 | | |
508 | | /// Return a wrapper around `value` suitable for formatting. |
509 | | /// |
510 | | /// Return a wrapper around `value` that implements |
511 | | /// [`core::fmt::Display`] in a form suitable for use in |
512 | | /// diagnostic messages. |
513 | 0 | fn as_diagnostic_display<T>( |
514 | 0 | &self, |
515 | 0 | value: T, |
516 | 0 | ) -> crate::common::DiagnosticDisplay<(T, proc::GlobalCtx<'_>)> { |
517 | 0 | let ctx = self.module.to_ctx(); |
518 | 0 | crate::common::DiagnosticDisplay((value, ctx)) |
519 | 0 | } |
520 | | |
521 | 40.0k | fn append_expression( |
522 | 40.0k | &mut self, |
523 | 40.0k | expr: ir::Expression, |
524 | 40.0k | span: Span, |
525 | 40.0k | ) -> Result<'source, Handle<ir::Expression>> { |
526 | 40.0k | let mut eval = self.as_const_evaluator(); |
527 | 40.0k | eval.try_eval_and_append(expr, span) |
528 | 40.0k | .map_err(|e| Box::new(Error::ConstantEvaluatorError(e.into(), span))) |
529 | 40.0k | } |
530 | | |
531 | 0 | fn const_eval_expr_to_u32( |
532 | 0 | &self, |
533 | 0 | handle: Handle<ir::Expression>, |
534 | 0 | ) -> core::result::Result<u32, proc::U32EvalError> { |
535 | 0 | match self.expr_type { |
536 | 0 | ExpressionContextType::Runtime(ref ctx) => { |
537 | 0 | if !ctx.local_expression_kind_tracker.is_const(handle) { |
538 | 0 | return Err(proc::U32EvalError::NonConst); |
539 | 0 | } |
540 | | |
541 | 0 | self.module |
542 | 0 | .to_ctx() |
543 | 0 | .eval_expr_to_u32_from(handle, &ctx.function.expressions) |
544 | | } |
545 | 0 | ExpressionContextType::Constant(Some(ref ctx)) => { |
546 | 0 | assert!(ctx.local_expression_kind_tracker.is_const(handle)); |
547 | 0 | self.module |
548 | 0 | .to_ctx() |
549 | 0 | .eval_expr_to_u32_from(handle, &ctx.function.expressions) |
550 | | } |
551 | 0 | ExpressionContextType::Constant(None) => self.module.to_ctx().eval_expr_to_u32(handle), |
552 | 0 | ExpressionContextType::Override => Err(proc::U32EvalError::NonConst), |
553 | | } |
554 | 0 | } |
555 | | |
556 | | /// Return `true` if `handle` is a constant expression. |
557 | 2 | fn is_const(&self, handle: Handle<ir::Expression>) -> bool { |
558 | | use ExpressionContextType as Ect; |
559 | 0 | match self.expr_type { |
560 | 2 | Ect::Runtime(ref ctx) | Ect::Constant(Some(ref ctx)) => { |
561 | 2 | ctx.local_expression_kind_tracker.is_const(handle) |
562 | | } |
563 | | Ect::Constant(None) | Ect::Override => { |
564 | 0 | self.global_expression_kind_tracker.is_const(handle) |
565 | | } |
566 | | } |
567 | 2 | } |
568 | | |
569 | 156 | fn get_expression_span(&self, handle: Handle<ir::Expression>) -> Span { |
570 | 8 | match self.expr_type { |
571 | 148 | ExpressionContextType::Runtime(ref ctx) |
572 | 3 | | ExpressionContextType::Constant(Some(ref ctx)) => { |
573 | 151 | ctx.function.expressions.get_span(handle) |
574 | | } |
575 | | ExpressionContextType::Constant(None) | ExpressionContextType::Override => { |
576 | 5 | self.module.global_expressions.get_span(handle) |
577 | | } |
578 | | } |
579 | 156 | } |
580 | | |
581 | 18.2k | fn typifier(&self) -> &Typifier { |
582 | 3.90k | match self.expr_type { |
583 | 14.3k | ExpressionContextType::Runtime(ref ctx) |
584 | 14.3k | | ExpressionContextType::Constant(Some(ref ctx)) => ctx.typifier, |
585 | | ExpressionContextType::Constant(None) | ExpressionContextType::Override => { |
586 | 3.88k | self.const_typifier |
587 | | } |
588 | | } |
589 | 18.2k | } |
590 | | |
591 | 50 | fn local( |
592 | 50 | &mut self, |
593 | 50 | local: &Handle<ast::Local>, |
594 | 50 | span: Span, |
595 | 50 | ) -> Result<'source, Typed<Handle<ir::Expression>>> { |
596 | 0 | match self.expr_type { |
597 | 50 | ExpressionContextType::Runtime(ref ctx) => Ok(ctx.local_table[local].runtime()), |
598 | 0 | ExpressionContextType::Constant(Some(ref ctx)) => ctx.local_table[local] |
599 | 0 | .const_time() |
600 | 0 | .ok_or(Box::new(Error::UnexpectedOperationInConstContext(span))), |
601 | 0 | _ => Err(Box::new(Error::UnexpectedOperationInConstContext(span))), |
602 | | } |
603 | 50 | } |
604 | | |
605 | 0 | fn runtime_expression_ctx( |
606 | 0 | &mut self, |
607 | 0 | span: Span, |
608 | 0 | ) -> Result<'source, &mut LocalExpressionContext<'temp, 'out>> { |
609 | 0 | match self.expr_type { |
610 | 0 | ExpressionContextType::Runtime(ref mut ctx) => Ok(ctx), |
611 | | ExpressionContextType::Constant(_) | ExpressionContextType::Override => { |
612 | 0 | Err(Box::new(Error::UnexpectedOperationInConstContext(span))) |
613 | | } |
614 | | } |
615 | 0 | } |
616 | | |
617 | 0 | fn gather_component( |
618 | 0 | &mut self, |
619 | 0 | expr: Handle<ir::Expression>, |
620 | 0 | component_span: Span, |
621 | 0 | gather_span: Span, |
622 | 0 | ) -> Result<'source, ir::SwizzleComponent> { |
623 | 0 | match self.expr_type { |
624 | 0 | ExpressionContextType::Runtime(ref rctx) => { |
625 | 0 | if !rctx.local_expression_kind_tracker.is_const(expr) { |
626 | 0 | return Err(Box::new(Error::ExpectedConstExprConcreteIntegerScalar( |
627 | 0 | component_span, |
628 | 0 | ))); |
629 | 0 | } |
630 | | |
631 | 0 | let index = self |
632 | 0 | .module |
633 | 0 | .to_ctx() |
634 | 0 | .eval_expr_to_u32_from(expr, &rctx.function.expressions) |
635 | 0 | .map_err(|err| match err { |
636 | | proc::U32EvalError::NonConst => { |
637 | 0 | Error::ExpectedConstExprConcreteIntegerScalar(component_span) |
638 | | } |
639 | 0 | proc::U32EvalError::Negative => Error::ExpectedNonNegative(component_span), |
640 | 0 | })?; |
641 | 0 | ir::SwizzleComponent::XYZW |
642 | 0 | .get(index as usize) |
643 | 0 | .copied() |
644 | 0 | .ok_or(Box::new(Error::InvalidGatherComponent(component_span))) |
645 | | } |
646 | | // This means a `gather` operation appeared in a constant expression. |
647 | | // This error refers to the `gather` itself, not its "component" argument. |
648 | 0 | ExpressionContextType::Constant(_) | ExpressionContextType::Override => Err(Box::new( |
649 | 0 | Error::UnexpectedOperationInConstContext(gather_span), |
650 | 0 | )), |
651 | | } |
652 | 0 | } |
653 | | |
654 | | /// Determine the type of `handle`, and add it to the module's arena. |
655 | | /// |
656 | | /// If you just need a `TypeInner` for `handle`'s type, use the |
657 | | /// [`resolve_inner!`] macro instead. This function |
658 | | /// should only be used when the type of `handle` needs to appear |
659 | | /// in the module's final `Arena<Type>`, for example, if you're |
660 | | /// creating a [`LocalVariable`] whose type is inferred from its |
661 | | /// initializer. |
662 | | /// |
663 | | /// [`LocalVariable`]: ir::LocalVariable |
664 | 3 | fn register_type( |
665 | 3 | &mut self, |
666 | 3 | handle: Handle<ir::Expression>, |
667 | 3 | ) -> Result<'source, Handle<ir::Type>> { |
668 | 3 | self.grow_types(handle)?; |
669 | | // This is equivalent to calling ExpressionContext::typifier(), |
670 | | // except that this lets the borrow checker see that it's okay |
671 | | // to also borrow self.module.types mutably below. |
672 | 3 | let typifier = match self.expr_type { |
673 | 3 | ExpressionContextType::Runtime(ref ctx) |
674 | 3 | | ExpressionContextType::Constant(Some(ref ctx)) => ctx.typifier, |
675 | | ExpressionContextType::Constant(None) | ExpressionContextType::Override => { |
676 | 0 | &*self.const_typifier |
677 | | } |
678 | | }; |
679 | 3 | Ok(typifier.register_type(handle, &mut self.module.types)) |
680 | 3 | } |
681 | | |
682 | | /// Resolve the types of all expressions up through `handle`. |
683 | | /// |
684 | | /// Ensure that [`self.typifier`] has a [`TypeResolution`] for |
685 | | /// every expression in [`self.function.expressions`]. |
686 | | /// |
687 | | /// This does not add types to any arena. The [`Typifier`] |
688 | | /// documentation explains the steps we take to avoid filling |
689 | | /// arenas with intermediate types. |
690 | | /// |
691 | | /// This function takes `&mut self`, so it can't conveniently |
692 | | /// return a shared reference to the resulting `TypeResolution`: |
693 | | /// the shared reference would extend the mutable borrow, and you |
694 | | /// wouldn't be able to use `self` for anything else. Instead, you |
695 | | /// should use [`register_type`] or one of [`resolve!`], |
696 | | /// [`resolve_inner!`] or [`resolve_inner_binary!`]. |
697 | | /// |
698 | | /// [`self.typifier`]: ExpressionContext::typifier |
699 | | /// [`TypeResolution`]: proc::TypeResolution |
700 | | /// [`register_type`]: Self::register_type |
701 | | /// [`Typifier`]: Typifier |
702 | 18.2k | fn grow_types(&mut self, handle: Handle<ir::Expression>) -> Result<'source, &mut Self> { |
703 | 18.2k | let empty_arena = Arena::new(); |
704 | | let resolve_ctx; |
705 | | let typifier; |
706 | | let expressions; |
707 | 3.90k | match self.expr_type { |
708 | 14.3k | ExpressionContextType::Runtime(ref mut ctx) |
709 | 14.3k | | ExpressionContextType::Constant(Some(ref mut ctx)) => { |
710 | 14.3k | resolve_ctx = proc::ResolveContext::with_locals( |
711 | 14.3k | self.module, |
712 | 14.3k | &ctx.function.local_variables, |
713 | 14.3k | &ctx.function.arguments, |
714 | 14.3k | ); |
715 | 14.3k | typifier = &mut *ctx.typifier; |
716 | 14.3k | expressions = &ctx.function.expressions; |
717 | 14.3k | } |
718 | 3.88k | ExpressionContextType::Constant(None) | ExpressionContextType::Override => { |
719 | 3.88k | resolve_ctx = proc::ResolveContext::with_locals(self.module, &empty_arena, &[]); |
720 | 3.88k | typifier = self.const_typifier; |
721 | 3.88k | expressions = &self.module.global_expressions; |
722 | 3.88k | } |
723 | | }; |
724 | 18.2k | typifier |
725 | 18.2k | .grow(handle, expressions, &resolve_ctx) |
726 | 18.2k | .map_err(Error::InvalidResolve)?; |
727 | | |
728 | 18.2k | Ok(self) |
729 | 18.2k | } |
730 | | |
731 | 0 | fn image_data( |
732 | 0 | &mut self, |
733 | 0 | image: Handle<ir::Expression>, |
734 | 0 | span: Span, |
735 | 0 | ) -> Result<'source, (ir::ImageClass, bool)> { |
736 | 0 | match *resolve_inner!(self, image) { |
737 | 0 | ir::TypeInner::Image { class, arrayed, .. } => Ok((class, arrayed)), |
738 | 0 | _ => Err(Box::new(Error::BadTexture(span))), |
739 | | } |
740 | 0 | } |
741 | | |
742 | 3 | fn prepare_args<'b>( |
743 | 3 | &mut self, |
744 | 3 | args: &'b [Handle<ast::Expression<'source>>], |
745 | 3 | min_args: u32, |
746 | 3 | span: Span, |
747 | 3 | ) -> ArgumentContext<'b, 'source> { |
748 | 3 | ArgumentContext { |
749 | 3 | args: args.iter(), |
750 | 3 | min_args, |
751 | 3 | args_used: 0, |
752 | 3 | total_args: args.len() as u32, |
753 | 3 | span, |
754 | 3 | } |
755 | 3 | } |
756 | | |
757 | | /// Insert splats, if needed by the non-'*' operations. |
758 | | /// |
759 | | /// See the "Binary arithmetic expressions with mixed scalar and vector operands" |
760 | | /// table in the WebGPU Shading Language specification for relevant operators. |
761 | | /// |
762 | | /// Multiply is not handled here as backends are expected to handle vec*scalar |
763 | | /// operations, so inserting splats into the IR increases size needlessly. |
764 | 826 | fn binary_op_splat( |
765 | 826 | &mut self, |
766 | 826 | op: ir::BinaryOperator, |
767 | 826 | left: &mut Handle<ir::Expression>, |
768 | 826 | right: &mut Handle<ir::Expression>, |
769 | 826 | ) -> Result<'source, ()> { |
770 | 776 | if matches!( |
771 | 826 | op, |
772 | | ir::BinaryOperator::Add |
773 | | | ir::BinaryOperator::Subtract |
774 | | | ir::BinaryOperator::Divide |
775 | | | ir::BinaryOperator::Modulo |
776 | | ) { |
777 | 50 | match resolve_inner_binary!(self, *left, *right) { |
778 | 46 | (&ir::TypeInner::Vector { size, .. }, &ir::TypeInner::Scalar { .. }) => { |
779 | 46 | *right = self.append_expression( |
780 | 46 | ir::Expression::Splat { |
781 | 46 | size, |
782 | 46 | value: *right, |
783 | 46 | }, |
784 | 46 | self.get_expression_span(*right), |
785 | 0 | )?; |
786 | | } |
787 | 2 | (&ir::TypeInner::Scalar { .. }, &ir::TypeInner::Vector { size, .. }) => { |
788 | 2 | *left = self.append_expression( |
789 | 2 | ir::Expression::Splat { size, value: *left }, |
790 | 2 | self.get_expression_span(*left), |
791 | 0 | )?; |
792 | | } |
793 | 2 | _ => {} |
794 | | } |
795 | 776 | } |
796 | | |
797 | 826 | Ok(()) |
798 | 826 | } |
799 | | |
800 | | /// Add a single expression to the expression table that is not covered by `self.emitter`. |
801 | | /// |
802 | | /// This is useful for `CallResult` and `AtomicResult` expressions, which should not be covered by |
803 | | /// `Emit` statements. |
804 | 1.62k | fn interrupt_emitter( |
805 | 1.62k | &mut self, |
806 | 1.62k | expression: ir::Expression, |
807 | 1.62k | span: Span, |
808 | 1.62k | ) -> Result<'source, Handle<ir::Expression>> { |
809 | 1.55k | match self.expr_type { |
810 | 63 | ExpressionContextType::Runtime(ref mut rctx) |
811 | 66 | | ExpressionContextType::Constant(Some(ref mut rctx)) => { |
812 | 66 | rctx.block |
813 | 66 | .extend(rctx.emitter.finish(&rctx.function.expressions)); |
814 | 66 | } |
815 | 1.55k | ExpressionContextType::Constant(None) | ExpressionContextType::Override => {} |
816 | | } |
817 | 1.62k | let result = self.append_expression(expression, span); |
818 | 1.55k | match self.expr_type { |
819 | 63 | ExpressionContextType::Runtime(ref mut rctx) |
820 | 66 | | ExpressionContextType::Constant(Some(ref mut rctx)) => { |
821 | 66 | rctx.emitter.start(&rctx.function.expressions); |
822 | 66 | } |
823 | 1.55k | ExpressionContextType::Constant(None) | ExpressionContextType::Override => {} |
824 | | } |
825 | 1.62k | result |
826 | 1.62k | } |
827 | | |
828 | | /// Apply the WGSL Load Rule to `expr`. |
829 | | /// |
830 | | /// If `expr` is has type `ref<SC, T, A>`, perform a load to produce a value of type |
831 | | /// `T`. Otherwise, return `expr` unchanged. |
832 | 39.9k | fn apply_load_rule( |
833 | 39.9k | &mut self, |
834 | 39.9k | expr: Typed<Handle<ir::Expression>>, |
835 | 39.9k | ) -> Result<'source, Handle<ir::Expression>> { |
836 | 39.9k | match expr { |
837 | 46 | Typed::Reference(pointer) => { |
838 | 46 | let load = ir::Expression::Load { pointer }; |
839 | 46 | let span = self.get_expression_span(pointer); |
840 | 46 | self.append_expression(load, span) |
841 | | } |
842 | 39.8k | Typed::Plain(handle) => Ok(handle), |
843 | | } |
844 | 39.9k | } |
845 | | |
846 | 14.0k | fn ensure_type_exists(&mut self, inner: ir::TypeInner) -> Handle<ir::Type> { |
847 | 14.0k | self.as_global().ensure_type_exists(None, inner) |
848 | 14.0k | } |
849 | | } |
850 | | |
851 | | struct ArgumentContext<'ctx, 'source> { |
852 | | args: core::slice::Iter<'ctx, Handle<ast::Expression<'source>>>, |
853 | | min_args: u32, |
854 | | args_used: u32, |
855 | | total_args: u32, |
856 | | span: Span, |
857 | | } |
858 | | |
859 | | impl<'source> ArgumentContext<'_, 'source> { |
860 | 3 | pub fn finish(self) -> Result<'source, ()> { |
861 | 3 | if self.args.len() == 0 { |
862 | 3 | Ok(()) |
863 | | } else { |
864 | 0 | Err(Box::new(Error::WrongArgumentCount { |
865 | 0 | found: self.total_args, |
866 | 0 | expected: self.min_args..self.args_used + 1, |
867 | 0 | span: self.span, |
868 | 0 | })) |
869 | | } |
870 | 3 | } |
871 | | |
872 | 7 | pub fn next(&mut self) -> Result<'source, Handle<ast::Expression<'source>>> { |
873 | 7 | match self.args.next().copied() { |
874 | 7 | Some(arg) => { |
875 | 7 | self.args_used += 1; |
876 | 7 | Ok(arg) |
877 | | } |
878 | 0 | None => Err(Box::new(Error::WrongArgumentCount { |
879 | 0 | found: self.total_args, |
880 | 0 | expected: self.min_args..self.args_used + 1, |
881 | 0 | span: self.span, |
882 | 0 | })), |
883 | | } |
884 | 7 | } |
885 | | } |
886 | | |
887 | | #[derive(Debug, Copy, Clone)] |
888 | | enum Declared<T> { |
889 | | /// Value declared as const |
890 | | Const(T), |
891 | | |
892 | | /// Value declared as non-const |
893 | | Runtime(T), |
894 | | } |
895 | | |
896 | | impl<T> Declared<T> { |
897 | 50 | fn runtime(self) -> T { |
898 | 50 | match self { |
899 | 50 | Declared::Const(t) | Declared::Runtime(t) => t, |
900 | | } |
901 | 50 | } |
902 | | |
903 | 0 | fn const_time(self) -> Option<T> { |
904 | 0 | match self { |
905 | 0 | Declared::Const(t) => Some(t), |
906 | 0 | Declared::Runtime(_) => None, |
907 | | } |
908 | 0 | } |
909 | | } |
910 | | |
911 | | /// WGSL type annotations on expressions, types, values, etc. |
912 | | /// |
913 | | /// Naga and WGSL types are very close, but Naga lacks WGSL's `ref` types, which |
914 | | /// we need to know to apply the Load Rule. This enum carries some WGSL or Naga |
915 | | /// datum along with enough information to determine its corresponding WGSL |
916 | | /// type. |
917 | | /// |
918 | | /// The `T` type parameter can be any expression-like thing: |
919 | | /// |
920 | | /// - `Typed<Handle<ir::Type>>` can represent a full WGSL type. For example, |
921 | | /// given some Naga `Pointer` type `ptr`, a WGSL reference type is a |
922 | | /// `Typed::Reference(ptr)` whereas a WGSL pointer type is a |
923 | | /// `Typed::Plain(ptr)`. |
924 | | /// |
925 | | /// - `Typed<ir::Expression>` or `Typed<Handle<ir::Expression>>` can |
926 | | /// represent references similarly. |
927 | | /// |
928 | | /// Use the `map` and `try_map` methods to convert from one expression |
929 | | /// representation to another. |
930 | | /// |
931 | | /// [`Expression`]: ir::Expression |
932 | | #[derive(Debug, Copy, Clone)] |
933 | | enum Typed<T> { |
934 | | /// A WGSL reference. |
935 | | Reference(T), |
936 | | |
937 | | /// A WGSL plain type. |
938 | | Plain(T), |
939 | | } |
940 | | |
941 | | impl<T> Typed<T> { |
942 | 0 | fn map<U>(self, mut f: impl FnMut(T) -> U) -> Typed<U> { |
943 | 0 | match self { |
944 | 0 | Self::Reference(v) => Typed::Reference(f(v)), |
945 | 0 | Self::Plain(v) => Typed::Plain(f(v)), |
946 | | } |
947 | 0 | } Unexecuted instantiation: <naga::front::wgsl::lower::Typed<naga::arena::handle::Handle<naga::ir::Expression>>>::map::<naga::ir::Expression, <naga::front::wgsl::lower::Lowerer>::expression_for_reference::{closure#3}> Unexecuted instantiation: <naga::front::wgsl::lower::Typed<naga::arena::handle::Handle<naga::ir::Expression>>>::map::<naga::ir::Expression, <naga::front::wgsl::lower::Lowerer>::expression_for_reference::{closure#4}> |
948 | | |
949 | 24.1k | fn try_map<U, E>( |
950 | 24.1k | self, |
951 | 24.1k | mut f: impl FnMut(T) -> core::result::Result<U, E>, |
952 | 24.1k | ) -> core::result::Result<Typed<U>, E> { |
953 | 24.1k | Ok(match self { |
954 | 0 | Self::Reference(expr) => Typed::Reference(f(expr)?), |
955 | 24.1k | Self::Plain(expr) => Typed::Plain(f(expr)?), |
956 | | }) |
957 | 24.1k | } Unexecuted instantiation: <naga::front::wgsl::lower::Typed<naga::arena::handle::Handle<naga::ir::Expression>>>::try_map::<naga::ir::Expression, alloc::boxed::Box<naga::front::wgsl::error::Error>, <naga::front::wgsl::lower::Lowerer>::expression_for_reference::{closure#1}> Unexecuted instantiation: <naga::front::wgsl::lower::Typed<naga::ir::Expression>>::try_map::<naga::arena::handle::Handle<naga::ir::Expression>, alloc::boxed::Box<naga::front::wgsl::error::Error>, <naga::front::wgsl::lower::Lowerer>::expression_for_reference::{closure#0}> <naga::front::wgsl::lower::Typed<naga::ir::Expression>>::try_map::<naga::arena::handle::Handle<naga::ir::Expression>, alloc::boxed::Box<naga::front::wgsl::error::Error>, <naga::front::wgsl::lower::Lowerer>::expression_for_reference::{closure#5}> Line | Count | Source | 949 | 24.1k | fn try_map<U, E>( | 950 | 24.1k | self, | 951 | 24.1k | mut f: impl FnMut(T) -> core::result::Result<U, E>, | 952 | 24.1k | ) -> core::result::Result<Typed<U>, E> { | 953 | 24.1k | Ok(match self { | 954 | 0 | Self::Reference(expr) => Typed::Reference(f(expr)?), | 955 | 24.1k | Self::Plain(expr) => Typed::Plain(f(expr)?), | 956 | | }) | 957 | 24.1k | } |
|
958 | | } |
959 | | |
960 | | /// A single vector component or swizzle. |
961 | | /// |
962 | | /// This represents the things that can appear after the `.` in a vector access |
963 | | /// expression: either a single component name, or a series of them, |
964 | | /// representing a swizzle. |
965 | | enum Components { |
966 | | Single(u32), |
967 | | Swizzle { |
968 | | size: ir::VectorSize, |
969 | | pattern: [ir::SwizzleComponent; 4], |
970 | | }, |
971 | | } |
972 | | |
973 | | impl Components { |
974 | 0 | const fn letter_component(letter: char) -> Option<ir::SwizzleComponent> { |
975 | | use ir::SwizzleComponent as Sc; |
976 | 0 | match letter { |
977 | 0 | 'x' | 'r' => Some(Sc::X), |
978 | 0 | 'y' | 'g' => Some(Sc::Y), |
979 | 0 | 'z' | 'b' => Some(Sc::Z), |
980 | 0 | 'w' | 'a' => Some(Sc::W), |
981 | 0 | _ => None, |
982 | | } |
983 | 0 | } |
984 | | |
985 | 0 | fn single_component(name: &str, name_span: Span) -> Result<'_, u32> { |
986 | 0 | let ch = name.chars().next().ok_or(Error::BadAccessor(name_span))?; |
987 | 0 | match Self::letter_component(ch) { |
988 | 0 | Some(sc) => Ok(sc as u32), |
989 | 0 | None => Err(Box::new(Error::BadAccessor(name_span))), |
990 | | } |
991 | 0 | } |
992 | | |
993 | | /// Construct a `Components` value from a 'member' name, like `"wzy"` or `"x"`. |
994 | | /// |
995 | | /// Use `name_span` for reporting errors in parsing the component string. |
996 | 0 | fn new(name: &str, name_span: Span) -> Result<'_, Self> { |
997 | 0 | let size = match name.len() { |
998 | 0 | 1 => return Ok(Components::Single(Self::single_component(name, name_span)?)), |
999 | 0 | 2 => ir::VectorSize::Bi, |
1000 | 0 | 3 => ir::VectorSize::Tri, |
1001 | 0 | 4 => ir::VectorSize::Quad, |
1002 | 0 | _ => return Err(Box::new(Error::BadAccessor(name_span))), |
1003 | | }; |
1004 | | |
1005 | 0 | let mut pattern = [ir::SwizzleComponent::X; 4]; |
1006 | 0 | for (comp, ch) in pattern.iter_mut().zip(name.chars()) { |
1007 | 0 | *comp = Self::letter_component(ch).ok_or(Error::BadAccessor(name_span))?; |
1008 | | } |
1009 | | |
1010 | 0 | if name.chars().all(|c| matches!(c, 'x' | 'y' | 'z' | 'w')) |
1011 | 0 | || name.chars().all(|c| matches!(c, 'r' | 'g' | 'b' | 'a')) |
1012 | | { |
1013 | 0 | Ok(Components::Swizzle { size, pattern }) |
1014 | | } else { |
1015 | 0 | Err(Box::new(Error::BadAccessor(name_span))) |
1016 | | } |
1017 | 0 | } |
1018 | | } |
1019 | | |
1020 | | /// An `ast::GlobalDecl` for which we have built the Naga IR equivalent. |
1021 | | enum LoweredGlobalDecl { |
1022 | | Function { |
1023 | | handle: Handle<ir::Function>, |
1024 | | must_use: bool, |
1025 | | }, |
1026 | | Var(Handle<ir::GlobalVariable>), |
1027 | | Const(Handle<ir::Constant>), |
1028 | | Override(Handle<ir::Override>), |
1029 | | Type(Handle<ir::Type>), |
1030 | | EntryPoint(usize), |
1031 | | } |
1032 | | |
1033 | | enum Texture { |
1034 | | Gather, |
1035 | | GatherCompare, |
1036 | | |
1037 | | Sample, |
1038 | | SampleBias, |
1039 | | SampleCompare, |
1040 | | SampleCompareLevel, |
1041 | | SampleGrad, |
1042 | | SampleLevel, |
1043 | | SampleBaseClampToEdge, |
1044 | | } |
1045 | | |
1046 | | impl Texture { |
1047 | 3 | pub fn map(word: &str) -> Option<Self> { |
1048 | 3 | Some(match word { |
1049 | 3 | "textureGather" => Self::Gather, |
1050 | 3 | "textureGatherCompare" => Self::GatherCompare, |
1051 | | |
1052 | 3 | "textureSample" => Self::Sample, |
1053 | 3 | "textureSampleBias" => Self::SampleBias, |
1054 | 3 | "textureSampleCompare" => Self::SampleCompare, |
1055 | 3 | "textureSampleCompareLevel" => Self::SampleCompareLevel, |
1056 | 3 | "textureSampleGrad" => Self::SampleGrad, |
1057 | 3 | "textureSampleLevel" => Self::SampleLevel, |
1058 | 3 | "textureSampleBaseClampToEdge" => Self::SampleBaseClampToEdge, |
1059 | 3 | _ => return None, |
1060 | | }) |
1061 | 3 | } |
1062 | | |
1063 | 0 | pub const fn min_argument_count(&self) -> u32 { |
1064 | 0 | match *self { |
1065 | 0 | Self::Gather => 3, |
1066 | 0 | Self::GatherCompare => 4, |
1067 | | |
1068 | 0 | Self::Sample => 3, |
1069 | 0 | Self::SampleBias => 5, |
1070 | 0 | Self::SampleCompare => 5, |
1071 | 0 | Self::SampleCompareLevel => 5, |
1072 | 0 | Self::SampleGrad => 6, |
1073 | 0 | Self::SampleLevel => 5, |
1074 | 0 | Self::SampleBaseClampToEdge => 3, |
1075 | | } |
1076 | 0 | } |
1077 | | } |
1078 | | |
1079 | | enum SubgroupGather { |
1080 | | BroadcastFirst, |
1081 | | Broadcast, |
1082 | | Shuffle, |
1083 | | ShuffleDown, |
1084 | | ShuffleUp, |
1085 | | ShuffleXor, |
1086 | | QuadBroadcast, |
1087 | | } |
1088 | | |
1089 | | impl SubgroupGather { |
1090 | 3 | pub fn map(word: &str) -> Option<Self> { |
1091 | 3 | Some(match word { |
1092 | 3 | "subgroupBroadcastFirst" => Self::BroadcastFirst, |
1093 | 3 | "subgroupBroadcast" => Self::Broadcast, |
1094 | 3 | "subgroupShuffle" => Self::Shuffle, |
1095 | 3 | "subgroupShuffleDown" => Self::ShuffleDown, |
1096 | 3 | "subgroupShuffleUp" => Self::ShuffleUp, |
1097 | 3 | "subgroupShuffleXor" => Self::ShuffleXor, |
1098 | 3 | "quadBroadcast" => Self::QuadBroadcast, |
1099 | 3 | _ => return None, |
1100 | | }) |
1101 | 3 | } |
1102 | | } |
1103 | | |
1104 | | /// Whether a declaration accepts abstract types, or concretizes. |
1105 | | enum AbstractRule { |
1106 | | /// This declaration concretizes its initialization expression. |
1107 | | Concretize, |
1108 | | |
1109 | | /// This declaration can accept initializers with abstract types. |
1110 | | Allow, |
1111 | | } |
1112 | | |
1113 | | pub struct Lowerer<'source, 'temp> { |
1114 | | index: &'temp Index<'source>, |
1115 | | } |
1116 | | |
1117 | | impl<'source, 'temp> Lowerer<'source, 'temp> { |
1118 | 17 | pub const fn new(index: &'temp Index<'source>) -> Self { |
1119 | 17 | Self { index } |
1120 | 17 | } |
1121 | | |
1122 | 17 | pub fn lower(&mut self, tu: ast::TranslationUnit<'source>) -> Result<'source, ir::Module> { |
1123 | 17 | let mut module = ir::Module { |
1124 | 17 | diagnostic_filters: tu.diagnostic_filters, |
1125 | 17 | diagnostic_filter_leaf: tu.diagnostic_filter_leaf, |
1126 | 17 | ..Default::default() |
1127 | 17 | }; |
1128 | | |
1129 | 17 | let mut ctx = GlobalContext { |
1130 | 17 | ast_expressions: &tu.expressions, |
1131 | 17 | globals: &mut FastHashMap::default(), |
1132 | 17 | types: &tu.types, |
1133 | 17 | module: &mut module, |
1134 | 17 | const_typifier: &mut Typifier::new(), |
1135 | 17 | layouter: &mut proc::Layouter::default(), |
1136 | 17 | global_expression_kind_tracker: &mut proc::ExpressionKindTracker::new(), |
1137 | 17 | }; |
1138 | 17 | if !tu.doc_comments.is_empty() { |
1139 | 0 | ctx.module.get_or_insert_default_doc_comments().module = |
1140 | 0 | tu.doc_comments.iter().map(|s| s.to_string()).collect(); |
1141 | 17 | } |
1142 | | |
1143 | 813 | for decl_handle in self.index.visit_ordered() { |
1144 | 813 | let span = tu.decls.get_span(decl_handle); |
1145 | 813 | let decl = &tu.decls[decl_handle]; |
1146 | | |
1147 | 813 | match decl.kind { |
1148 | 8 | ast::GlobalDeclKind::Fn(ref f) => { |
1149 | 8 | let lowered_decl = self.function(f, span, &mut ctx)?; |
1150 | 3 | if !f.doc_comments.is_empty() { |
1151 | 0 | match lowered_decl { |
1152 | 0 | LoweredGlobalDecl::Function { handle, .. } => { |
1153 | 0 | ctx.module |
1154 | 0 | .get_or_insert_default_doc_comments() |
1155 | 0 | .functions |
1156 | 0 | .insert( |
1157 | 0 | handle, |
1158 | 0 | f.doc_comments.iter().map(|s| s.to_string()).collect(), |
1159 | | ); |
1160 | | } |
1161 | 0 | LoweredGlobalDecl::EntryPoint(index) => { |
1162 | 0 | ctx.module |
1163 | 0 | .get_or_insert_default_doc_comments() |
1164 | 0 | .entry_points |
1165 | 0 | .insert( |
1166 | 0 | index, |
1167 | 0 | f.doc_comments.iter().map(|s| s.to_string()).collect(), |
1168 | | ); |
1169 | | } |
1170 | 0 | _ => {} |
1171 | | } |
1172 | 3 | } |
1173 | 3 | ctx.globals.insert(f.name.name, lowered_decl); |
1174 | | } |
1175 | 0 | ast::GlobalDeclKind::Var(ref v) => { |
1176 | 0 | let explicit_ty = |
1177 | 0 | v.ty.map(|ast| self.resolve_ast_type(ast, &mut ctx.as_const())) |
1178 | 0 | .transpose()?; |
1179 | | |
1180 | 0 | let (ty, initializer) = self.type_and_init( |
1181 | 0 | v.name, |
1182 | 0 | v.init, |
1183 | 0 | explicit_ty, |
1184 | 0 | AbstractRule::Concretize, |
1185 | 0 | &mut ctx.as_override(), |
1186 | 0 | )?; |
1187 | | |
1188 | 0 | let binding = if let Some(ref binding) = v.binding { |
1189 | | Some(ir::ResourceBinding { |
1190 | 0 | group: self.const_u32(binding.group, &mut ctx.as_const())?.0, |
1191 | 0 | binding: self.const_u32(binding.binding, &mut ctx.as_const())?.0, |
1192 | | }) |
1193 | | } else { |
1194 | 0 | None |
1195 | | }; |
1196 | | |
1197 | 0 | let handle = ctx.module.global_variables.append( |
1198 | 0 | ir::GlobalVariable { |
1199 | 0 | name: Some(v.name.name.to_string()), |
1200 | 0 | space: v.space, |
1201 | 0 | binding, |
1202 | 0 | ty, |
1203 | 0 | init: initializer, |
1204 | 0 | }, |
1205 | 0 | span, |
1206 | | ); |
1207 | | |
1208 | 0 | if !v.doc_comments.is_empty() { |
1209 | 0 | ctx.module |
1210 | 0 | .get_or_insert_default_doc_comments() |
1211 | 0 | .global_variables |
1212 | 0 | .insert( |
1213 | 0 | handle, |
1214 | 0 | v.doc_comments.iter().map(|s| s.to_string()).collect(), |
1215 | | ); |
1216 | 0 | } |
1217 | 0 | ctx.globals |
1218 | 0 | .insert(v.name.name, LoweredGlobalDecl::Var(handle)); |
1219 | | } |
1220 | 1 | ast::GlobalDeclKind::Const(ref c) => { |
1221 | 1 | let mut ectx = ctx.as_const(); |
1222 | | |
1223 | 1 | let explicit_ty = |
1224 | 1 | c.ty.map(|ast| self.resolve_ast_type(ast, &mut ectx)) |
1225 | 1 | .transpose()?; |
1226 | | |
1227 | 1 | let (ty, init) = self.type_and_init( |
1228 | 1 | c.name, |
1229 | 1 | Some(c.init), |
1230 | 1 | explicit_ty, |
1231 | 1 | AbstractRule::Allow, |
1232 | 1 | &mut ectx, |
1233 | 1 | )?; |
1234 | 0 | let init = init.expect("Global const must have init"); |
1235 | | |
1236 | 0 | let handle = ctx.module.constants.append( |
1237 | 0 | ir::Constant { |
1238 | 0 | name: Some(c.name.name.to_string()), |
1239 | 0 | ty, |
1240 | 0 | init, |
1241 | 0 | }, |
1242 | 0 | span, |
1243 | | ); |
1244 | | |
1245 | 0 | ctx.globals |
1246 | 0 | .insert(c.name.name, LoweredGlobalDecl::Const(handle)); |
1247 | 0 | if !c.doc_comments.is_empty() { |
1248 | 0 | ctx.module |
1249 | 0 | .get_or_insert_default_doc_comments() |
1250 | 0 | .constants |
1251 | 0 | .insert( |
1252 | 0 | handle, |
1253 | 0 | c.doc_comments.iter().map(|s| s.to_string()).collect(), |
1254 | | ); |
1255 | 0 | } |
1256 | | } |
1257 | 0 | ast::GlobalDeclKind::Override(ref o) => { |
1258 | 0 | let explicit_ty = |
1259 | 0 | o.ty.map(|ast| self.resolve_ast_type(ast, &mut ctx.as_const())) |
1260 | 0 | .transpose()?; |
1261 | | |
1262 | 0 | let mut ectx = ctx.as_override(); |
1263 | | |
1264 | 0 | let (ty, init) = self.type_and_init( |
1265 | 0 | o.name, |
1266 | 0 | o.init, |
1267 | 0 | explicit_ty, |
1268 | 0 | AbstractRule::Concretize, |
1269 | 0 | &mut ectx, |
1270 | 0 | )?; |
1271 | | |
1272 | 0 | let id = |
1273 | 0 | o.id.map(|id| self.const_u32(id, &mut ctx.as_const())) |
1274 | 0 | .transpose()?; |
1275 | | |
1276 | 0 | let id = if let Some((id, id_span)) = id { |
1277 | | Some( |
1278 | 0 | u16::try_from(id) |
1279 | 0 | .map_err(|_| Error::PipelineConstantIDValue(id_span))?, |
1280 | | ) |
1281 | | } else { |
1282 | 0 | None |
1283 | | }; |
1284 | | |
1285 | 0 | let handle = ctx.module.overrides.append( |
1286 | 0 | ir::Override { |
1287 | 0 | name: Some(o.name.name.to_string()), |
1288 | 0 | id, |
1289 | 0 | ty, |
1290 | 0 | init, |
1291 | 0 | }, |
1292 | 0 | span, |
1293 | | ); |
1294 | | |
1295 | 0 | ctx.globals |
1296 | 0 | .insert(o.name.name, LoweredGlobalDecl::Override(handle)); |
1297 | | } |
1298 | 29 | ast::GlobalDeclKind::Struct(ref s) => { |
1299 | 29 | let handle = self.r#struct(s, span, &mut ctx)?; |
1300 | 24 | ctx.globals |
1301 | 24 | .insert(s.name.name, LoweredGlobalDecl::Type(handle)); |
1302 | 24 | if !s.doc_comments.is_empty() { |
1303 | 0 | ctx.module |
1304 | 0 | .get_or_insert_default_doc_comments() |
1305 | 0 | .types |
1306 | 0 | .insert( |
1307 | 0 | handle, |
1308 | 0 | s.doc_comments.iter().map(|s| s.to_string()).collect(), |
1309 | | ); |
1310 | 24 | } |
1311 | | } |
1312 | 0 | ast::GlobalDeclKind::Type(ref alias) => { |
1313 | 0 | let ty = self.resolve_named_ast_type( |
1314 | 0 | alias.ty, |
1315 | 0 | Some(alias.name.name.to_string()), |
1316 | 0 | &mut ctx.as_const(), |
1317 | 0 | )?; |
1318 | 0 | ctx.globals |
1319 | 0 | .insert(alias.name.name, LoweredGlobalDecl::Type(ty)); |
1320 | | } |
1321 | 775 | ast::GlobalDeclKind::ConstAssert(condition) => { |
1322 | 775 | let condition = self.expression(condition, &mut ctx.as_const())?; |
1323 | | |
1324 | 775 | let span = ctx.module.global_expressions.get_span(condition); |
1325 | 775 | match ctx |
1326 | 775 | .module |
1327 | 775 | .to_ctx() |
1328 | 775 | .eval_expr_to_bool_from(condition, &ctx.module.global_expressions) |
1329 | | { |
1330 | 772 | Some(true) => Ok(()), |
1331 | 3 | Some(false) => Err(Error::ConstAssertFailed(span)), |
1332 | 0 | _ => Err(Error::NotBool(span)), |
1333 | 3 | }?; |
1334 | | } |
1335 | | } |
1336 | | } |
1337 | | |
1338 | | // Constant evaluation may leave abstract-typed literals and |
1339 | | // compositions in expression arenas, so we need to compact the module |
1340 | | // to remove unused expressions and types. |
1341 | 3 | crate::compact::compact(&mut module, KeepUnused::Yes); |
1342 | | |
1343 | 3 | Ok(module) |
1344 | 17 | } |
1345 | | |
1346 | | /// Obtain (inferred) type and initializer after automatic conversion |
1347 | 5 | fn type_and_init( |
1348 | 5 | &mut self, |
1349 | 5 | name: ast::Ident<'source>, |
1350 | 5 | init: Option<Handle<ast::Expression<'source>>>, |
1351 | 5 | explicit_ty: Option<Handle<ir::Type>>, |
1352 | 5 | abstract_rule: AbstractRule, |
1353 | 5 | ectx: &mut ExpressionContext<'source, '_, '_>, |
1354 | 5 | ) -> Result<'source, (Handle<ir::Type>, Option<Handle<ir::Expression>>)> { |
1355 | | let ty; |
1356 | | let initializer; |
1357 | 5 | match (init, explicit_ty) { |
1358 | 0 | (Some(init), Some(explicit_ty)) => { |
1359 | 0 | let init = self.expression_for_abstract(init, ectx)?; |
1360 | 0 | let ty_res = proc::TypeResolution::Handle(explicit_ty); |
1361 | 0 | let init = ectx |
1362 | 0 | .try_automatic_conversions(init, &ty_res, name.span) |
1363 | 0 | .map_err(|error| match *error { |
1364 | 0 | Error::AutoConversion(e) => Box::new(Error::InitializationTypeMismatch { |
1365 | 0 | name: name.span, |
1366 | 0 | expected: e.dest_type, |
1367 | 0 | got: e.source_type, |
1368 | 0 | }), |
1369 | 0 | _ => error, |
1370 | 0 | })?; |
1371 | | |
1372 | 0 | let init_ty = ectx.register_type(init)?; |
1373 | 0 | if !ectx.module.compare_types( |
1374 | 0 | &proc::TypeResolution::Handle(explicit_ty), |
1375 | 0 | &proc::TypeResolution::Handle(init_ty), |
1376 | 0 | ) { |
1377 | 0 | return Err(Box::new(Error::InitializationTypeMismatch { |
1378 | 0 | name: name.span, |
1379 | 0 | expected: ectx.type_to_string(explicit_ty), |
1380 | 0 | got: ectx.type_to_string(init_ty), |
1381 | 0 | })); |
1382 | 0 | } |
1383 | 0 | ty = explicit_ty; |
1384 | 0 | initializer = Some(init); |
1385 | | } |
1386 | 5 | (Some(init), None) => { |
1387 | 5 | let mut init = self.expression_for_abstract(init, ectx)?; |
1388 | 3 | if let AbstractRule::Concretize = abstract_rule { |
1389 | 3 | init = ectx.concretize(init)?; |
1390 | 0 | } |
1391 | 3 | ty = ectx.register_type(init)?; |
1392 | 3 | initializer = Some(init); |
1393 | | } |
1394 | 0 | (None, Some(explicit_ty)) => { |
1395 | 0 | ty = explicit_ty; |
1396 | 0 | initializer = None; |
1397 | 0 | } |
1398 | 0 | (None, None) => return Err(Box::new(Error::DeclMissingTypeAndInit(name.span))), |
1399 | | } |
1400 | 3 | Ok((ty, initializer)) |
1401 | 5 | } |
1402 | | |
1403 | 8 | fn function( |
1404 | 8 | &mut self, |
1405 | 8 | f: &ast::Function<'source>, |
1406 | 8 | span: Span, |
1407 | 8 | ctx: &mut GlobalContext<'source, '_, '_>, |
1408 | 8 | ) -> Result<'source, LoweredGlobalDecl> { |
1409 | 8 | let mut local_table = FastHashMap::default(); |
1410 | 8 | let mut expressions = Arena::new(); |
1411 | 8 | let mut named_expressions = FastIndexMap::default(); |
1412 | 8 | let mut local_expression_kind_tracker = proc::ExpressionKindTracker::new(); |
1413 | | |
1414 | 8 | let arguments = f |
1415 | 8 | .arguments |
1416 | 8 | .iter() |
1417 | 8 | .enumerate() |
1418 | 8 | .map(|(i, arg)| -> Result<'_, _> { |
1419 | 0 | let ty = self.resolve_ast_type(arg.ty, &mut ctx.as_const())?; |
1420 | 0 | let expr = |
1421 | 0 | expressions.append(ir::Expression::FunctionArgument(i as u32), arg.name.span); |
1422 | 0 | local_table.insert(arg.handle, Declared::Runtime(Typed::Plain(expr))); |
1423 | 0 | named_expressions.insert(expr, (arg.name.name.to_string(), arg.name.span)); |
1424 | 0 | local_expression_kind_tracker.insert(expr, proc::ExpressionKind::Runtime); |
1425 | | |
1426 | | Ok(ir::FunctionArgument { |
1427 | 0 | name: Some(arg.name.name.to_string()), |
1428 | 0 | ty, |
1429 | 0 | binding: self.binding(&arg.binding, ty, ctx)?, |
1430 | | }) |
1431 | 0 | }) |
1432 | 8 | .collect::<Result<Vec<_>>>()?; |
1433 | | |
1434 | 8 | let result = f |
1435 | 8 | .result |
1436 | 8 | .as_ref() |
1437 | 8 | .map(|res| -> Result<'_, _> { |
1438 | 0 | let ty = self.resolve_ast_type(res.ty, &mut ctx.as_const())?; |
1439 | | Ok(ir::FunctionResult { |
1440 | 0 | ty, |
1441 | 0 | binding: self.binding(&res.binding, ty, ctx)?, |
1442 | | }) |
1443 | 0 | }) |
1444 | 8 | .transpose()?; |
1445 | | |
1446 | 8 | let mut function = ir::Function { |
1447 | 8 | name: Some(f.name.name.to_string()), |
1448 | 8 | arguments, |
1449 | 8 | result, |
1450 | 8 | local_variables: Arena::new(), |
1451 | 8 | expressions, |
1452 | 8 | named_expressions: crate::NamedExpressions::default(), |
1453 | 8 | body: ir::Block::default(), |
1454 | 8 | diagnostic_filter_leaf: f.diagnostic_filter_leaf, |
1455 | 8 | }; |
1456 | | |
1457 | 8 | let mut typifier = Typifier::default(); |
1458 | 8 | let mut stmt_ctx = StatementContext { |
1459 | 8 | local_table: &mut local_table, |
1460 | 8 | globals: ctx.globals, |
1461 | 8 | ast_expressions: ctx.ast_expressions, |
1462 | 8 | const_typifier: ctx.const_typifier, |
1463 | 8 | typifier: &mut typifier, |
1464 | 8 | layouter: ctx.layouter, |
1465 | 8 | function: &mut function, |
1466 | 8 | named_expressions: &mut named_expressions, |
1467 | 8 | types: ctx.types, |
1468 | 8 | module: ctx.module, |
1469 | 8 | local_expression_kind_tracker: &mut local_expression_kind_tracker, |
1470 | 8 | global_expression_kind_tracker: ctx.global_expression_kind_tracker, |
1471 | 8 | }; |
1472 | 8 | let mut body = self.block(&f.body, false, &mut stmt_ctx)?; |
1473 | 3 | proc::ensure_block_returns(&mut body); |
1474 | | |
1475 | 3 | function.body = body; |
1476 | 3 | function.named_expressions = named_expressions |
1477 | 3 | .into_iter() |
1478 | 14.0k | .map(|(key, (name, _))| (key, name)) |
1479 | 3 | .collect(); |
1480 | | |
1481 | 3 | if let Some(ref entry) = f.entry_point { |
1482 | 0 | let workgroup_size_info = if let Some(workgroup_size) = entry.workgroup_size { |
1483 | | // TODO: replace with try_map once stabilized |
1484 | 0 | let mut workgroup_size_out = [1; 3]; |
1485 | 0 | let mut workgroup_size_overrides_out = [None; 3]; |
1486 | 0 | for (i, size) in workgroup_size.into_iter().enumerate() { |
1487 | 0 | if let Some(size_expr) = size { |
1488 | 0 | match self.const_u32(size_expr, &mut ctx.as_const()) { |
1489 | 0 | Ok(value) => { |
1490 | 0 | workgroup_size_out[i] = value.0; |
1491 | 0 | } |
1492 | 0 | Err(err) => { |
1493 | 0 | if let Error::ConstantEvaluatorError(ref ty, _) = *err { |
1494 | 0 | match **ty { |
1495 | | proc::ConstantEvaluatorError::OverrideExpr => { |
1496 | 0 | workgroup_size_overrides_out[i] = |
1497 | 0 | Some(self.workgroup_size_override( |
1498 | 0 | size_expr, |
1499 | 0 | &mut ctx.as_override(), |
1500 | 0 | )?); |
1501 | | } |
1502 | | _ => { |
1503 | 0 | return Err(err); |
1504 | | } |
1505 | | } |
1506 | | } else { |
1507 | 0 | return Err(err); |
1508 | | } |
1509 | | } |
1510 | | } |
1511 | 0 | } |
1512 | | } |
1513 | 0 | if workgroup_size_overrides_out.iter().all(|x| x.is_none()) { |
1514 | 0 | (workgroup_size_out, None) |
1515 | | } else { |
1516 | 0 | (workgroup_size_out, Some(workgroup_size_overrides_out)) |
1517 | | } |
1518 | | } else { |
1519 | 0 | ([0; 3], None) |
1520 | | }; |
1521 | | |
1522 | 0 | let (workgroup_size, workgroup_size_overrides) = workgroup_size_info; |
1523 | 0 | ctx.module.entry_points.push(ir::EntryPoint { |
1524 | 0 | name: f.name.name.to_string(), |
1525 | 0 | stage: entry.stage, |
1526 | 0 | early_depth_test: entry.early_depth_test, |
1527 | 0 | workgroup_size, |
1528 | 0 | workgroup_size_overrides, |
1529 | 0 | function, |
1530 | 0 | }); |
1531 | 0 | Ok(LoweredGlobalDecl::EntryPoint( |
1532 | 0 | ctx.module.entry_points.len() - 1, |
1533 | 0 | )) |
1534 | | } else { |
1535 | 3 | let handle = ctx.module.functions.append(function, span); |
1536 | | Ok(LoweredGlobalDecl::Function { |
1537 | 3 | handle, |
1538 | 3 | must_use: f.result.as_ref().is_some_and(|res| res.must_use), |
1539 | | }) |
1540 | | } |
1541 | 8 | } |
1542 | | |
1543 | 0 | fn workgroup_size_override( |
1544 | 0 | &mut self, |
1545 | 0 | size_expr: Handle<ast::Expression<'source>>, |
1546 | 0 | ctx: &mut ExpressionContext<'source, '_, '_>, |
1547 | 0 | ) -> Result<'source, Handle<ir::Expression>> { |
1548 | 0 | let span = ctx.ast_expressions.get_span(size_expr); |
1549 | 0 | let expr = self.expression(size_expr, ctx)?; |
1550 | 0 | match resolve_inner!(ctx, expr).scalar_kind().ok_or(0) { |
1551 | 0 | Ok(ir::ScalarKind::Sint) | Ok(ir::ScalarKind::Uint) => Ok(expr), |
1552 | 0 | _ => Err(Box::new(Error::ExpectedConstExprConcreteIntegerScalar( |
1553 | 0 | span, |
1554 | 0 | ))), |
1555 | | } |
1556 | 0 | } |
1557 | | |
1558 | 8 | fn block( |
1559 | 8 | &mut self, |
1560 | 8 | b: &ast::Block<'source>, |
1561 | 8 | is_inside_loop: bool, |
1562 | 8 | ctx: &mut StatementContext<'source, '_, '_>, |
1563 | 8 | ) -> Result<'source, ir::Block> { |
1564 | 8 | let mut block = ir::Block::default(); |
1565 | | |
1566 | 14.1k | for stmt in b.stmts.iter() { |
1567 | 14.1k | self.statement(stmt, &mut block, is_inside_loop, ctx)?; |
1568 | | } |
1569 | | |
1570 | 3 | Ok(block) |
1571 | 8 | } |
1572 | | |
1573 | 14.1k | fn statement( |
1574 | 14.1k | &mut self, |
1575 | 14.1k | stmt: &ast::Statement<'source>, |
1576 | 14.1k | block: &mut ir::Block, |
1577 | 14.1k | is_inside_loop: bool, |
1578 | 14.1k | ctx: &mut StatementContext<'source, '_, '_>, |
1579 | 14.1k | ) -> Result<'source, ()> { |
1580 | 14.1k | let out = match stmt.kind { |
1581 | 0 | ast::StatementKind::Block(ref block) => { |
1582 | 0 | let block = self.block(block, is_inside_loop, ctx)?; |
1583 | 0 | ir::Statement::Block(block) |
1584 | | } |
1585 | 4 | ast::StatementKind::LocalDecl(ref decl) => match *decl { |
1586 | 1 | ast::LocalDecl::Let(ref l) => { |
1587 | 1 | let mut emitter = proc::Emitter::default(); |
1588 | 1 | emitter.start(&ctx.function.expressions); |
1589 | | |
1590 | 1 | let explicit_ty = l |
1591 | 1 | .ty |
1592 | 1 | .map(|ty| self.resolve_ast_type(ty, &mut ctx.as_const(block, &mut emitter))) |
1593 | 1 | .transpose()?; |
1594 | | |
1595 | 1 | let mut ectx = ctx.as_expression(block, &mut emitter); |
1596 | | |
1597 | 1 | let (_ty, initializer) = self.type_and_init( |
1598 | 1 | l.name, |
1599 | 1 | Some(l.init), |
1600 | 1 | explicit_ty, |
1601 | 1 | AbstractRule::Concretize, |
1602 | 1 | &mut ectx, |
1603 | 0 | )?; |
1604 | | |
1605 | | // We passed `Some()` to `type_and_init`, so we |
1606 | | // will get a lowered initializer expression back. |
1607 | 1 | let initializer = |
1608 | 1 | initializer.expect("type_and_init did not return an initializer"); |
1609 | | |
1610 | | // The WGSL spec says that any expression that refers to a |
1611 | | // `let`-bound variable is not a const expression. This |
1612 | | // affects when errors must be reported, so we can't even |
1613 | | // treat suitable `let` bindings as constant as an |
1614 | | // optimization. |
1615 | 1 | ctx.local_expression_kind_tracker |
1616 | 1 | .force_non_const(initializer); |
1617 | | |
1618 | 1 | block.extend(emitter.finish(&ctx.function.expressions)); |
1619 | 1 | ctx.local_table |
1620 | 1 | .insert(l.handle, Declared::Runtime(Typed::Plain(initializer))); |
1621 | 1 | ctx.named_expressions |
1622 | 1 | .insert(initializer, (l.name.name.to_string(), l.name.span)); |
1623 | | |
1624 | 1 | return Ok(()); |
1625 | | } |
1626 | 2 | ast::LocalDecl::Var(ref v) => { |
1627 | 2 | let mut emitter = proc::Emitter::default(); |
1628 | 2 | emitter.start(&ctx.function.expressions); |
1629 | | |
1630 | 2 | let explicit_ty = |
1631 | 2 | v.ty.map(|ast| { |
1632 | 0 | self.resolve_ast_type(ast, &mut ctx.as_const(block, &mut emitter)) |
1633 | 0 | }) |
1634 | 2 | .transpose()?; |
1635 | | |
1636 | 2 | let mut ectx = ctx.as_expression(block, &mut emitter); |
1637 | 2 | let (ty, initializer) = self.type_and_init( |
1638 | 2 | v.name, |
1639 | 2 | v.init, |
1640 | 2 | explicit_ty, |
1641 | 2 | AbstractRule::Concretize, |
1642 | 2 | &mut ectx, |
1643 | 0 | )?; |
1644 | | |
1645 | 2 | let (const_initializer, initializer) = { |
1646 | 2 | match initializer { |
1647 | 2 | Some(init) => { |
1648 | | // It's not correct to hoist the initializer up |
1649 | | // to the top of the function if: |
1650 | | // - the initialization is inside a loop, and should |
1651 | | // take place on every iteration, or |
1652 | | // - the initialization is not a constant |
1653 | | // expression, so its value depends on the |
1654 | | // state at the point of initialization. |
1655 | 2 | if is_inside_loop |
1656 | 2 | || !ctx.local_expression_kind_tracker.is_const_or_override(init) |
1657 | | { |
1658 | 0 | (None, Some(init)) |
1659 | | } else { |
1660 | 2 | (Some(init), None) |
1661 | | } |
1662 | | } |
1663 | 0 | None => (None, None), |
1664 | | } |
1665 | | }; |
1666 | | |
1667 | 2 | let var = ctx.function.local_variables.append( |
1668 | 2 | ir::LocalVariable { |
1669 | 2 | name: Some(v.name.name.to_string()), |
1670 | 2 | ty, |
1671 | 2 | init: const_initializer, |
1672 | 2 | }, |
1673 | 2 | stmt.span, |
1674 | | ); |
1675 | | |
1676 | 2 | let handle = ctx |
1677 | 2 | .as_expression(block, &mut emitter) |
1678 | 2 | .interrupt_emitter(ir::Expression::LocalVariable(var), Span::UNDEFINED)?; |
1679 | 2 | block.extend(emitter.finish(&ctx.function.expressions)); |
1680 | 2 | ctx.local_table |
1681 | 2 | .insert(v.handle, Declared::Runtime(Typed::Reference(handle))); |
1682 | | |
1683 | 2 | match initializer { |
1684 | 0 | Some(initializer) => ir::Statement::Store { |
1685 | 0 | pointer: handle, |
1686 | 0 | value: initializer, |
1687 | 0 | }, |
1688 | 2 | None => return Ok(()), |
1689 | | } |
1690 | | } |
1691 | 1 | ast::LocalDecl::Const(ref c) => { |
1692 | 1 | let mut emitter = proc::Emitter::default(); |
1693 | 1 | emitter.start(&ctx.function.expressions); |
1694 | | |
1695 | 1 | let ectx = &mut ctx.as_const(block, &mut emitter); |
1696 | | |
1697 | 1 | let explicit_ty = |
1698 | 1 | c.ty.map(|ast| self.resolve_ast_type(ast, &mut ectx.as_const())) |
1699 | 1 | .transpose()?; |
1700 | | |
1701 | 1 | let (_ty, init) = self.type_and_init( |
1702 | 1 | c.name, |
1703 | 1 | Some(c.init), |
1704 | 1 | explicit_ty, |
1705 | 1 | AbstractRule::Allow, |
1706 | 1 | &mut ectx.as_const(), |
1707 | 1 | )?; |
1708 | 0 | let init = init.expect("Local const must have init"); |
1709 | | |
1710 | 0 | block.extend(emitter.finish(&ctx.function.expressions)); |
1711 | 0 | ctx.local_table |
1712 | 0 | .insert(c.handle, Declared::Const(Typed::Plain(init))); |
1713 | 0 | return Ok(()); |
1714 | | } |
1715 | | }, |
1716 | | ast::StatementKind::If { |
1717 | 0 | condition, |
1718 | 0 | ref accept, |
1719 | 0 | ref reject, |
1720 | | } => { |
1721 | 0 | let mut emitter = proc::Emitter::default(); |
1722 | 0 | emitter.start(&ctx.function.expressions); |
1723 | | |
1724 | 0 | let condition = |
1725 | 0 | self.expression(condition, &mut ctx.as_expression(block, &mut emitter))?; |
1726 | 0 | block.extend(emitter.finish(&ctx.function.expressions)); |
1727 | | |
1728 | 0 | let accept = self.block(accept, is_inside_loop, ctx)?; |
1729 | 0 | let reject = self.block(reject, is_inside_loop, ctx)?; |
1730 | | |
1731 | 0 | ir::Statement::If { |
1732 | 0 | condition, |
1733 | 0 | accept, |
1734 | 0 | reject, |
1735 | 0 | } |
1736 | | } |
1737 | | ast::StatementKind::Switch { |
1738 | 2 | selector, |
1739 | 2 | ref cases, |
1740 | | } => { |
1741 | 2 | let mut emitter = proc::Emitter::default(); |
1742 | 2 | emitter.start(&ctx.function.expressions); |
1743 | | |
1744 | 2 | let mut ectx = ctx.as_expression(block, &mut emitter); |
1745 | | |
1746 | | // Determine the scalar type of the selector and case expressions, find the |
1747 | | // consensus type for automatic conversion, then convert them. |
1748 | 2 | let (mut exprs, spans) = core::iter::once(selector) |
1749 | 2 | .chain(cases.iter().filter_map(|case| match case.value { |
1750 | 0 | ast::SwitchValue::Expr(expr) => Some(expr), |
1751 | 0 | ast::SwitchValue::Default => None, |
1752 | 0 | })) |
1753 | 2 | .enumerate() |
1754 | 2 | .map(|(i, expr)| { |
1755 | 2 | let span = ectx.ast_expressions.get_span(expr); |
1756 | 2 | let expr = self.expression_for_abstract(expr, &mut ectx)?; |
1757 | 0 | let ty = resolve_inner!(ectx, expr); |
1758 | 0 | match *ty { |
1759 | | ir::TypeInner::Scalar( |
1760 | | ir::Scalar::I32 | ir::Scalar::U32 | ir::Scalar::ABSTRACT_INT, |
1761 | 0 | ) => Ok((expr, span)), |
1762 | 0 | _ => match i { |
1763 | 0 | 0 => Err(Box::new(Error::InvalidSwitchSelector { span })), |
1764 | 0 | _ => Err(Box::new(Error::InvalidSwitchCase { span })), |
1765 | | }, |
1766 | | } |
1767 | 2 | }) |
1768 | 2 | .collect::<Result<(Vec<_>, Vec<_>)>>()?; |
1769 | | |
1770 | 0 | let mut consensus = |
1771 | 0 | ectx.automatic_conversion_consensus(&exprs) |
1772 | 0 | .map_err(|span_idx| Error::SwitchCaseTypeMismatch { |
1773 | 0 | span: spans[span_idx], |
1774 | 0 | })?; |
1775 | | // Concretize to I32 if the selector and all cases were abstract |
1776 | 0 | if consensus == ir::Scalar::ABSTRACT_INT { |
1777 | 0 | consensus = ir::Scalar::I32; |
1778 | 0 | } |
1779 | 0 | for expr in &mut exprs { |
1780 | 0 | ectx.convert_to_leaf_scalar(expr, consensus)?; |
1781 | | } |
1782 | | |
1783 | 0 | block.extend(emitter.finish(&ctx.function.expressions)); |
1784 | | |
1785 | 0 | let mut exprs = exprs.into_iter(); |
1786 | 0 | let selector = exprs |
1787 | 0 | .next() |
1788 | 0 | .expect("First element should be selector expression"); |
1789 | | |
1790 | 0 | let cases = cases |
1791 | 0 | .iter() |
1792 | 0 | .map(|case| { |
1793 | | Ok(ir::SwitchCase { |
1794 | 0 | value: match case.value { |
1795 | 0 | ast::SwitchValue::Expr(expr) => { |
1796 | 0 | let span = ctx.ast_expressions.get_span(expr); |
1797 | 0 | let expr = exprs.next().expect( |
1798 | 0 | "Should yield expression for each SwitchValue::Expr case", |
1799 | | ); |
1800 | 0 | match ctx |
1801 | 0 | .module |
1802 | 0 | .to_ctx() |
1803 | 0 | .eval_expr_to_literal_from(expr, &ctx.function.expressions) |
1804 | | { |
1805 | 0 | Some(ir::Literal::I32(value)) => { |
1806 | 0 | ir::SwitchValue::I32(value) |
1807 | | } |
1808 | 0 | Some(ir::Literal::U32(value)) => { |
1809 | 0 | ir::SwitchValue::U32(value) |
1810 | | } |
1811 | | _ => { |
1812 | 0 | return Err(Box::new(Error::InvalidSwitchCase { |
1813 | 0 | span, |
1814 | 0 | })); |
1815 | | } |
1816 | | } |
1817 | | } |
1818 | 0 | ast::SwitchValue::Default => ir::SwitchValue::Default, |
1819 | | }, |
1820 | 0 | body: self.block(&case.body, is_inside_loop, ctx)?, |
1821 | 0 | fall_through: case.fall_through, |
1822 | | }) |
1823 | 0 | }) |
1824 | 0 | .collect::<Result<_>>()?; |
1825 | | |
1826 | 0 | ir::Statement::Switch { selector, cases } |
1827 | | } |
1828 | | ast::StatementKind::Loop { |
1829 | 0 | ref body, |
1830 | 0 | ref continuing, |
1831 | 0 | break_if, |
1832 | | } => { |
1833 | 0 | let body = self.block(body, true, ctx)?; |
1834 | 0 | let mut continuing = self.block(continuing, true, ctx)?; |
1835 | | |
1836 | 0 | let mut emitter = proc::Emitter::default(); |
1837 | 0 | emitter.start(&ctx.function.expressions); |
1838 | 0 | let break_if = break_if |
1839 | 0 | .map(|expr| { |
1840 | 0 | self.expression(expr, &mut ctx.as_expression(&mut continuing, &mut emitter)) |
1841 | 0 | }) |
1842 | 0 | .transpose()?; |
1843 | 0 | continuing.extend(emitter.finish(&ctx.function.expressions)); |
1844 | | |
1845 | 0 | ir::Statement::Loop { |
1846 | 0 | body, |
1847 | 0 | continuing, |
1848 | 0 | break_if, |
1849 | 0 | } |
1850 | | } |
1851 | 0 | ast::StatementKind::Break => ir::Statement::Break, |
1852 | 0 | ast::StatementKind::Continue => ir::Statement::Continue, |
1853 | 0 | ast::StatementKind::Return { value: ast_value } => { |
1854 | 0 | let mut emitter = proc::Emitter::default(); |
1855 | 0 | emitter.start(&ctx.function.expressions); |
1856 | | |
1857 | | let value; |
1858 | 0 | if let Some(ast_expr) = ast_value { |
1859 | 0 | let result_ty = ctx.function.result.as_ref().map(|r| r.ty); |
1860 | 0 | let mut ectx = ctx.as_expression(block, &mut emitter); |
1861 | 0 | let expr = self.expression_for_abstract(ast_expr, &mut ectx)?; |
1862 | | |
1863 | 0 | if let Some(result_ty) = result_ty { |
1864 | 0 | let mut ectx = ctx.as_expression(block, &mut emitter); |
1865 | 0 | let resolution = proc::TypeResolution::Handle(result_ty); |
1866 | 0 | let converted = |
1867 | 0 | ectx.try_automatic_conversions(expr, &resolution, Span::default())?; |
1868 | 0 | value = Some(converted); |
1869 | 0 | } else { |
1870 | 0 | value = Some(expr); |
1871 | 0 | } |
1872 | 0 | } else { |
1873 | 0 | value = None; |
1874 | 0 | } |
1875 | 0 | block.extend(emitter.finish(&ctx.function.expressions)); |
1876 | | |
1877 | 0 | ir::Statement::Return { value } |
1878 | | } |
1879 | 0 | ast::StatementKind::Kill => ir::Statement::Kill, |
1880 | | ast::StatementKind::Call { |
1881 | 0 | ref function, |
1882 | 0 | ref arguments, |
1883 | | } => { |
1884 | 0 | let mut emitter = proc::Emitter::default(); |
1885 | 0 | emitter.start(&ctx.function.expressions); |
1886 | | |
1887 | 0 | let _ = self.call( |
1888 | 0 | stmt.span, |
1889 | 0 | function, |
1890 | 0 | arguments, |
1891 | 0 | &mut ctx.as_expression(block, &mut emitter), |
1892 | | true, |
1893 | 0 | )?; |
1894 | 0 | block.extend(emitter.finish(&ctx.function.expressions)); |
1895 | 0 | return Ok(()); |
1896 | | } |
1897 | | ast::StatementKind::Assign { |
1898 | 50 | target: ast_target, |
1899 | 50 | op, |
1900 | 50 | value, |
1901 | | } => { |
1902 | 50 | let mut emitter = proc::Emitter::default(); |
1903 | 50 | emitter.start(&ctx.function.expressions); |
1904 | 50 | let target_span = ctx.ast_expressions.get_span(ast_target); |
1905 | | |
1906 | 50 | let mut ectx = ctx.as_expression(block, &mut emitter); |
1907 | 50 | let target = self.expression_for_reference(ast_target, &mut ectx)?; |
1908 | 50 | let target_handle = match target { |
1909 | 50 | Typed::Reference(handle) => handle, |
1910 | 0 | Typed::Plain(handle) => { |
1911 | 0 | let ty = ctx.invalid_assignment_type(handle); |
1912 | 0 | return Err(Box::new(Error::InvalidAssignment { |
1913 | 0 | span: target_span, |
1914 | 0 | ty, |
1915 | 0 | })); |
1916 | | } |
1917 | | }; |
1918 | | |
1919 | | // Usually the value needs to be converted to match the type of |
1920 | | // the memory view you're assigning it to. The bit shift |
1921 | | // operators are exceptions, in that the right operand is always |
1922 | | // a `u32` or `vecN<u32>`. |
1923 | 50 | let target_scalar = match op { |
1924 | | Some(ir::BinaryOperator::ShiftLeft | ir::BinaryOperator::ShiftRight) => { |
1925 | 0 | Some(ir::Scalar::U32) |
1926 | | } |
1927 | 50 | _ => resolve_inner!(ectx, target_handle) |
1928 | 50 | .pointer_automatically_convertible_scalar(&ectx.module.types), |
1929 | | }; |
1930 | | |
1931 | 50 | let value = self.expression_for_abstract(value, &mut ectx)?; |
1932 | 50 | let mut value = match target_scalar { |
1933 | 50 | Some(target_scalar) => ectx.try_automatic_conversion_for_leaf_scalar( |
1934 | 50 | value, |
1935 | 50 | target_scalar, |
1936 | 50 | target_span, |
1937 | 0 | )?, |
1938 | 0 | None => value, |
1939 | | }; |
1940 | | |
1941 | 50 | let value = match op { |
1942 | 46 | Some(op) => { |
1943 | 46 | let mut left = ectx.apply_load_rule(target)?; |
1944 | 46 | ectx.binary_op_splat(op, &mut left, &mut value)?; |
1945 | 46 | ectx.append_expression( |
1946 | 46 | ir::Expression::Binary { |
1947 | 46 | op, |
1948 | 46 | left, |
1949 | 46 | right: value, |
1950 | 46 | }, |
1951 | 46 | stmt.span, |
1952 | 0 | )? |
1953 | | } |
1954 | 4 | None => value, |
1955 | | }; |
1956 | 50 | block.extend(emitter.finish(&ctx.function.expressions)); |
1957 | | |
1958 | 50 | ir::Statement::Store { |
1959 | 50 | pointer: target_handle, |
1960 | 50 | value, |
1961 | 50 | } |
1962 | | } |
1963 | 0 | ast::StatementKind::Increment(value) | ast::StatementKind::Decrement(value) => { |
1964 | 0 | let mut emitter = proc::Emitter::default(); |
1965 | 0 | emitter.start(&ctx.function.expressions); |
1966 | | |
1967 | 0 | let op = match stmt.kind { |
1968 | 0 | ast::StatementKind::Increment(_) => ir::BinaryOperator::Add, |
1969 | 0 | ast::StatementKind::Decrement(_) => ir::BinaryOperator::Subtract, |
1970 | 0 | _ => unreachable!(), |
1971 | | }; |
1972 | | |
1973 | 0 | let value_span = ctx.ast_expressions.get_span(value); |
1974 | 0 | let target = self |
1975 | 0 | .expression_for_reference(value, &mut ctx.as_expression(block, &mut emitter))?; |
1976 | 0 | let target_handle = match target { |
1977 | 0 | Typed::Reference(handle) => handle, |
1978 | | Typed::Plain(_) => { |
1979 | 0 | return Err(Box::new(Error::BadIncrDecrReferenceType(value_span))) |
1980 | | } |
1981 | | }; |
1982 | | |
1983 | 0 | let mut ectx = ctx.as_expression(block, &mut emitter); |
1984 | 0 | let scalar = match *resolve_inner!(ectx, target_handle) { |
1985 | | ir::TypeInner::ValuePointer { |
1986 | 0 | size: None, scalar, .. |
1987 | 0 | } => scalar, |
1988 | 0 | ir::TypeInner::Pointer { base, .. } => match ectx.module.types[base].inner { |
1989 | 0 | ir::TypeInner::Scalar(scalar) => scalar, |
1990 | 0 | _ => return Err(Box::new(Error::BadIncrDecrReferenceType(value_span))), |
1991 | | }, |
1992 | 0 | _ => return Err(Box::new(Error::BadIncrDecrReferenceType(value_span))), |
1993 | | }; |
1994 | 0 | let literal = match scalar.kind { |
1995 | 0 | ir::ScalarKind::Sint | ir::ScalarKind::Uint => ir::Literal::one(scalar) |
1996 | 0 | .ok_or(Error::BadIncrDecrReferenceType(value_span))?, |
1997 | 0 | _ => return Err(Box::new(Error::BadIncrDecrReferenceType(value_span))), |
1998 | | }; |
1999 | | |
2000 | 0 | let right = |
2001 | 0 | ectx.interrupt_emitter(ir::Expression::Literal(literal), Span::UNDEFINED)?; |
2002 | 0 | let rctx = ectx.runtime_expression_ctx(stmt.span)?; |
2003 | 0 | let left = rctx.function.expressions.append( |
2004 | 0 | ir::Expression::Load { |
2005 | 0 | pointer: target_handle, |
2006 | 0 | }, |
2007 | 0 | value_span, |
2008 | | ); |
2009 | 0 | let value = rctx |
2010 | 0 | .function |
2011 | 0 | .expressions |
2012 | 0 | .append(ir::Expression::Binary { op, left, right }, stmt.span); |
2013 | 0 | rctx.local_expression_kind_tracker |
2014 | 0 | .insert(left, proc::ExpressionKind::Runtime); |
2015 | 0 | rctx.local_expression_kind_tracker |
2016 | 0 | .insert(value, proc::ExpressionKind::Runtime); |
2017 | | |
2018 | 0 | block.extend(emitter.finish(&ctx.function.expressions)); |
2019 | 0 | ir::Statement::Store { |
2020 | 0 | pointer: target_handle, |
2021 | 0 | value, |
2022 | 0 | } |
2023 | | } |
2024 | 0 | ast::StatementKind::ConstAssert(condition) => { |
2025 | 0 | let mut emitter = proc::Emitter::default(); |
2026 | 0 | emitter.start(&ctx.function.expressions); |
2027 | | |
2028 | 0 | let condition = |
2029 | 0 | self.expression(condition, &mut ctx.as_const(block, &mut emitter))?; |
2030 | | |
2031 | 0 | let span = ctx.function.expressions.get_span(condition); |
2032 | 0 | match ctx |
2033 | 0 | .module |
2034 | 0 | .to_ctx() |
2035 | 0 | .eval_expr_to_bool_from(condition, &ctx.function.expressions) |
2036 | | { |
2037 | 0 | Some(true) => Ok(()), |
2038 | 0 | Some(false) => Err(Error::ConstAssertFailed(span)), |
2039 | 0 | _ => Err(Error::NotBool(span)), |
2040 | 0 | }?; |
2041 | | |
2042 | 0 | block.extend(emitter.finish(&ctx.function.expressions)); |
2043 | | |
2044 | 0 | return Ok(()); |
2045 | | } |
2046 | 14.0k | ast::StatementKind::Phony(expr) => { |
2047 | | // Remembered the RHS of the phony assignment as a named expression. This |
2048 | | // is important (1) to preserve the RHS for validation, (2) to track any |
2049 | | // referenced globals. |
2050 | 14.0k | let mut emitter = proc::Emitter::default(); |
2051 | 14.0k | emitter.start(&ctx.function.expressions); |
2052 | | |
2053 | 14.0k | let value = self.expression(expr, &mut ctx.as_expression(block, &mut emitter))?; |
2054 | 14.0k | block.extend(emitter.finish(&ctx.function.expressions)); |
2055 | 14.0k | ctx.named_expressions |
2056 | 14.0k | .insert(value, ("phony".to_string(), stmt.span)); |
2057 | 14.0k | return Ok(()); |
2058 | | } |
2059 | | }; |
2060 | | |
2061 | 50 | block.push(out, stmt.span); |
2062 | | |
2063 | 50 | Ok(()) |
2064 | 14.1k | } |
2065 | | |
2066 | | /// Lower `expr` and apply the Load Rule if possible. |
2067 | | /// |
2068 | | /// For the time being, this concretizes abstract values, to support |
2069 | | /// consumers that haven't been adapted to consume them yet. Consumers |
2070 | | /// prepared for abstract values can call [`expression_for_abstract`]. |
2071 | | /// |
2072 | | /// [`expression_for_abstract`]: Lowerer::expression_for_abstract |
2073 | 14.8k | fn expression( |
2074 | 14.8k | &mut self, |
2075 | 14.8k | expr: Handle<ast::Expression<'source>>, |
2076 | 14.8k | ctx: &mut ExpressionContext<'source, '_, '_>, |
2077 | 14.8k | ) -> Result<'source, Handle<ir::Expression>> { |
2078 | 14.8k | let expr = self.expression_for_abstract(expr, ctx)?; |
2079 | 14.8k | ctx.concretize(expr) |
2080 | 14.8k | } |
2081 | | |
2082 | 39.8k | fn expression_for_abstract( |
2083 | 39.8k | &mut self, |
2084 | 39.8k | expr: Handle<ast::Expression<'source>>, |
2085 | 39.8k | ctx: &mut ExpressionContext<'source, '_, '_>, |
2086 | 39.8k | ) -> Result<'source, Handle<ir::Expression>> { |
2087 | 39.8k | let expr = self.expression_for_reference(expr, ctx)?; |
2088 | 39.8k | ctx.apply_load_rule(expr) |
2089 | 39.8k | } |
2090 | | |
2091 | 0 | fn expression_with_leaf_scalar( |
2092 | 0 | &mut self, |
2093 | 0 | expr: Handle<ast::Expression<'source>>, |
2094 | 0 | scalar: ir::Scalar, |
2095 | 0 | ctx: &mut ExpressionContext<'source, '_, '_>, |
2096 | 0 | ) -> Result<'source, Handle<ir::Expression>> { |
2097 | 0 | let unconverted = self.expression_for_abstract(expr, ctx)?; |
2098 | 0 | ctx.try_automatic_conversion_for_leaf_scalar(unconverted, scalar, Span::default()) |
2099 | 0 | } |
2100 | | |
2101 | 39.9k | fn expression_for_reference( |
2102 | 39.9k | &mut self, |
2103 | 39.9k | expr: Handle<ast::Expression<'source>>, |
2104 | 39.9k | ctx: &mut ExpressionContext<'source, '_, '_>, |
2105 | 39.9k | ) -> Result<'source, Typed<Handle<ir::Expression>>> { |
2106 | 39.9k | let span = ctx.ast_expressions.get_span(expr); |
2107 | 39.9k | let expr = &ctx.ast_expressions[expr]; |
2108 | | |
2109 | 24.1k | let expr: Typed<ir::Expression> = match *expr { |
2110 | 1.61k | ast::Expression::Literal(literal) => { |
2111 | 1.61k | let literal = match literal { |
2112 | 0 | ast::Literal::Number(Number::F16(f)) => ir::Literal::F16(f), |
2113 | 1 | ast::Literal::Number(Number::F32(f)) => ir::Literal::F32(f), |
2114 | 0 | ast::Literal::Number(Number::I32(i)) => ir::Literal::I32(i), |
2115 | 0 | ast::Literal::Number(Number::U32(u)) => ir::Literal::U32(u), |
2116 | 0 | ast::Literal::Number(Number::I64(i)) => ir::Literal::I64(i), |
2117 | 0 | ast::Literal::Number(Number::U64(u)) => ir::Literal::U64(u), |
2118 | 0 | ast::Literal::Number(Number::F64(f)) => ir::Literal::F64(f), |
2119 | 1.61k | ast::Literal::Number(Number::AbstractInt(i)) => ir::Literal::AbstractInt(i), |
2120 | 6 | ast::Literal::Number(Number::AbstractFloat(f)) => ir::Literal::AbstractFloat(f), |
2121 | 0 | ast::Literal::Bool(b) => ir::Literal::Bool(b), |
2122 | | }; |
2123 | 1.61k | let handle = ctx.interrupt_emitter(ir::Expression::Literal(literal), span)?; |
2124 | 1.61k | return Ok(Typed::Plain(handle)); |
2125 | | } |
2126 | 50 | ast::Expression::Ident(ast::IdentExpr::Local(local)) => { |
2127 | 50 | return ctx.local(&local, span); |
2128 | | } |
2129 | 1 | ast::Expression::Ident(ast::IdentExpr::Unresolved(name)) => { |
2130 | 1 | let global = ctx |
2131 | 1 | .globals |
2132 | 1 | .get(name) |
2133 | 1 | .ok_or(Error::UnknownIdent(span, name))?; |
2134 | 0 | let expr = match *global { |
2135 | 0 | LoweredGlobalDecl::Var(handle) => { |
2136 | 0 | let expr = ir::Expression::GlobalVariable(handle); |
2137 | 0 | match ctx.module.global_variables[handle].space { |
2138 | 0 | ir::AddressSpace::Handle => Typed::Plain(expr), |
2139 | 0 | _ => Typed::Reference(expr), |
2140 | | } |
2141 | | } |
2142 | 0 | LoweredGlobalDecl::Const(handle) => { |
2143 | 0 | Typed::Plain(ir::Expression::Constant(handle)) |
2144 | | } |
2145 | 0 | LoweredGlobalDecl::Override(handle) => { |
2146 | 0 | Typed::Plain(ir::Expression::Override(handle)) |
2147 | | } |
2148 | | LoweredGlobalDecl::Function { .. } |
2149 | | | LoweredGlobalDecl::Type(_) |
2150 | | | LoweredGlobalDecl::EntryPoint(_) => { |
2151 | 0 | return Err(Box::new(Error::Unexpected(span, ExpectedToken::Variable))); |
2152 | | } |
2153 | | }; |
2154 | | |
2155 | 0 | return expr.try_map(|handle| ctx.interrupt_emitter(handle, span)); |
2156 | | } |
2157 | | ast::Expression::Construct { |
2158 | 14.0k | ref ty, |
2159 | 14.0k | ty_span, |
2160 | 14.0k | ref components, |
2161 | | } => { |
2162 | 14.0k | let handle = self.construct(span, ty, ty_span, components, ctx)?; |
2163 | 14.0k | return Ok(Typed::Plain(handle)); |
2164 | | } |
2165 | 23.4k | ast::Expression::Unary { op, expr } => { |
2166 | 23.4k | let expr = self.expression_for_abstract(expr, ctx)?; |
2167 | 23.4k | Typed::Plain(ir::Expression::Unary { op, expr }) |
2168 | | } |
2169 | 0 | ast::Expression::AddrOf(expr) => { |
2170 | | // The `&` operator simply converts a reference to a pointer. And since a |
2171 | | // reference is required, the Load Rule is not applied. |
2172 | 0 | match self.expression_for_reference(expr, ctx)? { |
2173 | 0 | Typed::Reference(handle) => { |
2174 | 0 | let expr = &ctx.runtime_expression_ctx(span)?.function.expressions[handle]; |
2175 | 0 | if let &ir::Expression::Access { base, .. } |
2176 | 0 | | &ir::Expression::AccessIndex { base, .. } = expr |
2177 | | { |
2178 | 0 | if let Some(ty) = resolve_inner!(ctx, base).pointer_base_type() { |
2179 | 0 | if matches!( |
2180 | 0 | *ty.inner_with(&ctx.module.types), |
2181 | | ir::TypeInner::Vector { .. }, |
2182 | | ) { |
2183 | 0 | return Err(Box::new(Error::InvalidAddrOfOperand( |
2184 | 0 | ctx.get_expression_span(handle), |
2185 | 0 | ))); |
2186 | 0 | } |
2187 | 0 | } |
2188 | 0 | } |
2189 | | // No code is generated. We just declare the reference a pointer now. |
2190 | 0 | return Ok(Typed::Plain(handle)); |
2191 | | } |
2192 | | Typed::Plain(_) => { |
2193 | 0 | return Err(Box::new(Error::NotReference( |
2194 | 0 | "the operand of the `&` operator", |
2195 | 0 | span, |
2196 | 0 | ))); |
2197 | | } |
2198 | | } |
2199 | | } |
2200 | 0 | ast::Expression::Deref(expr) => { |
2201 | | // The pointer we dereference must be loaded. |
2202 | 0 | let pointer = self.expression(expr, ctx)?; |
2203 | | |
2204 | 0 | if resolve_inner!(ctx, pointer).pointer_space().is_none() { |
2205 | 0 | return Err(Box::new(Error::NotPointer(span))); |
2206 | 0 | } |
2207 | | |
2208 | | // No code is generated. We just declare the pointer a reference now. |
2209 | 0 | return Ok(Typed::Reference(pointer)); |
2210 | | } |
2211 | 791 | ast::Expression::Binary { op, left, right } => { |
2212 | 791 | self.binary(op, left, right, span, ctx)? |
2213 | | } |
2214 | | ast::Expression::Call { |
2215 | 4 | ref function, |
2216 | 4 | ref arguments, |
2217 | | } => { |
2218 | 4 | let handle = self |
2219 | 4 | .call(span, function, arguments, ctx, false)? |
2220 | 1 | .ok_or(Error::FunctionReturnsVoid(function.span))?; |
2221 | 1 | return Ok(Typed::Plain(handle)); |
2222 | | } |
2223 | 0 | ast::Expression::Index { base, index } => { |
2224 | 0 | let mut lowered_base = self.expression_for_reference(base, ctx)?; |
2225 | 0 | let index = self.expression(index, ctx)?; |
2226 | | |
2227 | | // <https://www.w3.org/TR/WGSL/#language_extension-pointer_composite_access> |
2228 | | // Declare pointer as reference |
2229 | 0 | if let Typed::Plain(handle) = lowered_base { |
2230 | 0 | if resolve_inner!(ctx, handle).pointer_space().is_some() { |
2231 | 0 | lowered_base = Typed::Reference(handle); |
2232 | 0 | } |
2233 | 0 | } |
2234 | | |
2235 | 0 | lowered_base.try_map(|base| match ctx.const_eval_expr_to_u32(index).ok() { |
2236 | 0 | Some(index) => Ok::<_, Box<Error>>(ir::Expression::AccessIndex { base, index }), |
2237 | | None => { |
2238 | | // When an abstract array value e is indexed by an expression |
2239 | | // that is not a const-expression, then the array is concretized |
2240 | | // before the index is applied. |
2241 | | // https://www.w3.org/TR/WGSL/#array-access-expr |
2242 | | // Also applies to vectors and matrices. |
2243 | 0 | let base = ctx.concretize(base)?; |
2244 | 0 | Ok(ir::Expression::Access { base, index }) |
2245 | | } |
2246 | 0 | })? |
2247 | | } |
2248 | 0 | ast::Expression::Member { base, ref field } => { |
2249 | 0 | let mut lowered_base = self.expression_for_reference(base, ctx)?; |
2250 | | |
2251 | | // <https://www.w3.org/TR/WGSL/#language_extension-pointer_composite_access> |
2252 | | // Declare pointer as reference |
2253 | 0 | if let Typed::Plain(handle) = lowered_base { |
2254 | 0 | if resolve_inner!(ctx, handle).pointer_space().is_some() { |
2255 | 0 | lowered_base = Typed::Reference(handle); |
2256 | 0 | } |
2257 | 0 | } |
2258 | | |
2259 | | let temp_ty; |
2260 | 0 | let composite_type: &ir::TypeInner = match lowered_base { |
2261 | 0 | Typed::Reference(handle) => { |
2262 | 0 | temp_ty = resolve_inner!(ctx, handle) |
2263 | 0 | .pointer_base_type() |
2264 | 0 | .expect("In Typed::Reference(handle), handle must be a Naga pointer"); |
2265 | 0 | temp_ty.inner_with(&ctx.module.types) |
2266 | | } |
2267 | | |
2268 | 0 | Typed::Plain(handle) => { |
2269 | 0 | resolve_inner!(ctx, handle) |
2270 | | } |
2271 | | }; |
2272 | | |
2273 | 0 | let access = match *composite_type { |
2274 | 0 | ir::TypeInner::Struct { ref members, .. } => { |
2275 | 0 | let index = members |
2276 | 0 | .iter() |
2277 | 0 | .position(|m| m.name.as_deref() == Some(field.name)) |
2278 | 0 | .ok_or(Error::BadAccessor(field.span))? |
2279 | | as u32; |
2280 | | |
2281 | 0 | lowered_base.map(|base| ir::Expression::AccessIndex { base, index }) |
2282 | | } |
2283 | | ir::TypeInner::Vector { .. } => { |
2284 | 0 | match Components::new(field.name, field.span)? { |
2285 | 0 | Components::Swizzle { size, pattern } => { |
2286 | | Typed::Plain(ir::Expression::Swizzle { |
2287 | 0 | size, |
2288 | 0 | vector: ctx.apply_load_rule(lowered_base)?, |
2289 | 0 | pattern, |
2290 | | }) |
2291 | | } |
2292 | 0 | Components::Single(index) => { |
2293 | 0 | lowered_base.map(|base| ir::Expression::AccessIndex { base, index }) |
2294 | | } |
2295 | | } |
2296 | | } |
2297 | 0 | _ => return Err(Box::new(Error::BadAccessor(field.span))), |
2298 | | }; |
2299 | | |
2300 | 0 | access |
2301 | | } |
2302 | 0 | ast::Expression::Bitcast { expr, to, ty_span } => { |
2303 | 0 | let expr = self.expression(expr, ctx)?; |
2304 | 0 | let to_resolved = self.resolve_ast_type(to, &mut ctx.as_const())?; |
2305 | | |
2306 | 0 | let element_scalar = match ctx.module.types[to_resolved].inner { |
2307 | 0 | ir::TypeInner::Scalar(scalar) => scalar, |
2308 | 0 | ir::TypeInner::Vector { scalar, .. } => scalar, |
2309 | | _ => { |
2310 | 0 | let ty = resolve!(ctx, expr); |
2311 | 0 | return Err(Box::new(Error::BadTypeCast { |
2312 | 0 | from_type: ctx.type_resolution_to_string(ty), |
2313 | 0 | span: ty_span, |
2314 | 0 | to_type: ctx.type_to_string(to_resolved), |
2315 | 0 | })); |
2316 | | } |
2317 | | }; |
2318 | | |
2319 | 0 | Typed::Plain(ir::Expression::As { |
2320 | 0 | expr, |
2321 | 0 | kind: element_scalar.kind, |
2322 | 0 | convert: None, |
2323 | 0 | }) |
2324 | | } |
2325 | | }; |
2326 | | |
2327 | 24.1k | expr.try_map(|handle| ctx.append_expression(handle, span)) |
2328 | 39.9k | } |
2329 | | |
2330 | 791 | fn binary( |
2331 | 791 | &mut self, |
2332 | 791 | op: ir::BinaryOperator, |
2333 | 791 | left: Handle<ast::Expression<'source>>, |
2334 | 791 | right: Handle<ast::Expression<'source>>, |
2335 | 791 | span: Span, |
2336 | 791 | ctx: &mut ExpressionContext<'source, '_, '_>, |
2337 | 791 | ) -> Result<'source, Typed<ir::Expression>> { |
2338 | | // Load both operands. |
2339 | 791 | let mut left = self.expression_for_abstract(left, ctx)?; |
2340 | 783 | let mut right = self.expression_for_abstract(right, ctx)?; |
2341 | | |
2342 | | // Convert `scalar op vector` to `vector op vector` by introducing |
2343 | | // `Splat` expressions. |
2344 | 780 | ctx.binary_op_splat(op, &mut left, &mut right)?; |
2345 | | |
2346 | | // Apply automatic conversions. |
2347 | 780 | match op { |
2348 | | ir::BinaryOperator::ShiftLeft | ir::BinaryOperator::ShiftRight => { |
2349 | | // Shift operators require the right operand to be `u32` or |
2350 | | // `vecN<u32>`. We can let the validator sort out vector length |
2351 | | // issues, but the right operand must be, or convert to, a u32 leaf |
2352 | | // scalar. |
2353 | | right = |
2354 | 0 | ctx.try_automatic_conversion_for_leaf_scalar(right, ir::Scalar::U32, span)?; |
2355 | | |
2356 | | // Additionally, we must concretize the left operand if the right operand |
2357 | | // is not a const-expression. |
2358 | | // See https://www.w3.org/TR/WGSL/#overload-resolution-section. |
2359 | | // |
2360 | | // 2. Eliminate any candidate where one of its subexpressions resolves to |
2361 | | // an abstract type after feasible automatic conversions, but another of |
2362 | | // the candidate’s subexpressions is not a const-expression. |
2363 | | // |
2364 | | // We only have to explicitly do so for shifts as their operands may be |
2365 | | // of different types - for other binary ops this is achieved by finding |
2366 | | // the conversion consensus for both operands. |
2367 | 0 | if !ctx.is_const(right) { |
2368 | 0 | left = ctx.concretize(left)?; |
2369 | 0 | } |
2370 | | } |
2371 | | |
2372 | | // All other operators follow the same pattern: reconcile the |
2373 | | // scalar leaf types. If there's no reconciliation possible, |
2374 | | // leave the expressions as they are: validation will report the |
2375 | | // problem. |
2376 | | _ => { |
2377 | 780 | ctx.grow_types(left)?; |
2378 | 780 | ctx.grow_types(right)?; |
2379 | 780 | if let Ok(consensus_scalar) = |
2380 | 780 | ctx.automatic_conversion_consensus([left, right].iter()) |
2381 | | { |
2382 | 780 | ctx.convert_to_leaf_scalar(&mut left, consensus_scalar)?; |
2383 | 780 | ctx.convert_to_leaf_scalar(&mut right, consensus_scalar)?; |
2384 | 0 | } |
2385 | | } |
2386 | | } |
2387 | | |
2388 | 780 | Ok(Typed::Plain(ir::Expression::Binary { op, left, right })) |
2389 | 791 | } |
2390 | | |
2391 | | /// Generate Naga IR for call expressions and statements, and type |
2392 | | /// constructor expressions. |
2393 | | /// |
2394 | | /// The "function" being called is simply an `Ident` that we know refers to |
2395 | | /// some module-scope definition. |
2396 | | /// |
2397 | | /// - If it is the name of a type, then the expression is a type constructor |
2398 | | /// expression: either constructing a value from components, a conversion |
2399 | | /// expression, or a zero value expression. |
2400 | | /// |
2401 | | /// - If it is the name of a function, then we're generating a [`Call`] |
2402 | | /// statement. We may be in the midst of generating code for an |
2403 | | /// expression, in which case we must generate an `Emit` statement to |
2404 | | /// force evaluation of the IR expressions we've generated so far, add the |
2405 | | /// `Call` statement to the current block, and then resume generating |
2406 | | /// expressions. |
2407 | | /// |
2408 | | /// [`Call`]: ir::Statement::Call |
2409 | 4 | fn call( |
2410 | 4 | &mut self, |
2411 | 4 | span: Span, |
2412 | 4 | function: &ast::Ident<'source>, |
2413 | 4 | arguments: &[Handle<ast::Expression<'source>>], |
2414 | 4 | ctx: &mut ExpressionContext<'source, '_, '_>, |
2415 | 4 | is_statement: bool, |
2416 | 4 | ) -> Result<'source, Option<Handle<ir::Expression>>> { |
2417 | 4 | let function_span = function.span; |
2418 | 4 | match ctx.globals.get(function.name) { |
2419 | 0 | Some(&LoweredGlobalDecl::Type(ty)) => { |
2420 | 0 | let handle = self.construct( |
2421 | 0 | span, |
2422 | 0 | &ast::ConstructorType::Type(ty), |
2423 | 0 | function_span, |
2424 | 0 | arguments, |
2425 | 0 | ctx, |
2426 | 0 | )?; |
2427 | 0 | Ok(Some(handle)) |
2428 | | } |
2429 | | Some( |
2430 | | &LoweredGlobalDecl::Const(_) |
2431 | | | &LoweredGlobalDecl::Override(_) |
2432 | | | &LoweredGlobalDecl::Var(_), |
2433 | 0 | ) => Err(Box::new(Error::Unexpected( |
2434 | 0 | function_span, |
2435 | 0 | ExpectedToken::Function, |
2436 | 0 | ))), |
2437 | | Some(&LoweredGlobalDecl::EntryPoint(_)) => { |
2438 | 0 | Err(Box::new(Error::CalledEntryPoint(function_span))) |
2439 | | } |
2440 | | Some(&LoweredGlobalDecl::Function { |
2441 | 0 | handle: function, |
2442 | 0 | must_use, |
2443 | | }) => { |
2444 | 0 | let arguments = arguments |
2445 | 0 | .iter() |
2446 | 0 | .enumerate() |
2447 | 0 | .map(|(i, &arg)| { |
2448 | | // Try to convert abstract values to the known argument types |
2449 | | let Some(&ir::FunctionArgument { |
2450 | 0 | ty: parameter_ty, .. |
2451 | 0 | }) = ctx.module.functions[function].arguments.get(i) |
2452 | | else { |
2453 | | // Wrong number of arguments... just concretize the type here |
2454 | | // and let the validator report the error. |
2455 | 0 | return self.expression(arg, ctx); |
2456 | | }; |
2457 | | |
2458 | 0 | let expr = self.expression_for_abstract(arg, ctx)?; |
2459 | 0 | ctx.try_automatic_conversions( |
2460 | 0 | expr, |
2461 | 0 | &proc::TypeResolution::Handle(parameter_ty), |
2462 | 0 | ctx.ast_expressions.get_span(arg), |
2463 | | ) |
2464 | 0 | }) |
2465 | 0 | .collect::<Result<Vec<_>>>()?; |
2466 | | |
2467 | 0 | let has_result = ctx.module.functions[function].result.is_some(); |
2468 | | |
2469 | 0 | if must_use && is_statement { |
2470 | 0 | return Err(Box::new(Error::FunctionMustUseUnused(function_span))); |
2471 | 0 | } |
2472 | | |
2473 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
2474 | | // we need to always do this before a fn call since all arguments need to be emitted before the fn call |
2475 | 0 | rctx.block |
2476 | 0 | .extend(rctx.emitter.finish(&rctx.function.expressions)); |
2477 | 0 | let result = has_result.then(|| { |
2478 | 0 | let result = rctx |
2479 | 0 | .function |
2480 | 0 | .expressions |
2481 | 0 | .append(ir::Expression::CallResult(function), span); |
2482 | 0 | rctx.local_expression_kind_tracker |
2483 | 0 | .insert(result, proc::ExpressionKind::Runtime); |
2484 | 0 | result |
2485 | 0 | }); |
2486 | 0 | rctx.emitter.start(&rctx.function.expressions); |
2487 | 0 | rctx.block.push( |
2488 | 0 | ir::Statement::Call { |
2489 | 0 | function, |
2490 | 0 | arguments, |
2491 | 0 | result, |
2492 | 0 | }, |
2493 | 0 | span, |
2494 | | ); |
2495 | | |
2496 | 0 | Ok(result) |
2497 | | } |
2498 | | None => { |
2499 | 4 | let span = function_span; |
2500 | 4 | let expr = if let Some(fun) = conv::map_relational_fun(function.name) { |
2501 | 0 | let mut args = ctx.prepare_args(arguments, 1, span); |
2502 | 0 | let argument = self.expression(args.next()?, ctx)?; |
2503 | 0 | args.finish()?; |
2504 | | |
2505 | | // Check for no-op all(bool) and any(bool): |
2506 | 0 | let argument_unmodified = matches!( |
2507 | 0 | fun, |
2508 | | ir::RelationalFunction::All | ir::RelationalFunction::Any |
2509 | | ) && { |
2510 | 0 | matches!( |
2511 | 0 | resolve_inner!(ctx, argument), |
2512 | | &ir::TypeInner::Scalar(ir::Scalar { |
2513 | | kind: ir::ScalarKind::Bool, |
2514 | | .. |
2515 | | }) |
2516 | | ) |
2517 | | }; |
2518 | | |
2519 | 0 | if argument_unmodified { |
2520 | 0 | return Ok(Some(argument)); |
2521 | | } else { |
2522 | 0 | ir::Expression::Relational { fun, argument } |
2523 | | } |
2524 | 4 | } else if let Some((axis, ctrl)) = conv::map_derivative(function.name) { |
2525 | 0 | let mut args = ctx.prepare_args(arguments, 1, span); |
2526 | 0 | let expr = self.expression(args.next()?, ctx)?; |
2527 | 0 | args.finish()?; |
2528 | | |
2529 | 0 | ir::Expression::Derivative { axis, ctrl, expr } |
2530 | 4 | } else if let Some(fun) = conv::map_standard_fun(function.name) { |
2531 | 1 | self.math_function_helper(span, fun, arguments, ctx)? |
2532 | 3 | } else if let Some(fun) = Texture::map(function.name) { |
2533 | 0 | self.texture_sample_helper(fun, arguments, span, ctx)? |
2534 | 3 | } else if let Some((op, cop)) = conv::map_subgroup_operation(function.name) { |
2535 | | return Ok(Some( |
2536 | 0 | self.subgroup_operation_helper(span, op, cop, arguments, ctx)?, |
2537 | | )); |
2538 | 3 | } else if let Some(mode) = SubgroupGather::map(function.name) { |
2539 | | return Ok(Some( |
2540 | 0 | self.subgroup_gather_helper(span, mode, arguments, ctx)?, |
2541 | | )); |
2542 | 3 | } else if let Some(fun) = ir::AtomicFunction::map(function.name) { |
2543 | 0 | return self.atomic_helper(span, fun, arguments, is_statement, ctx); |
2544 | | } else { |
2545 | 3 | match function.name { |
2546 | 3 | "select" => { |
2547 | 2 | let mut args = ctx.prepare_args(arguments, 3, span); |
2548 | | |
2549 | 2 | let reject_orig = args.next()?; |
2550 | 2 | let accept_orig = args.next()?; |
2551 | 2 | let mut values = [ |
2552 | 2 | self.expression_for_abstract(reject_orig, ctx)?, |
2553 | 2 | self.expression_for_abstract(accept_orig, ctx)?, |
2554 | | ]; |
2555 | 2 | let condition = self.expression(args.next()?, ctx)?; |
2556 | | |
2557 | 2 | args.finish()?; |
2558 | | |
2559 | 2 | let diagnostic_details = |
2560 | | |ctx: &ExpressionContext<'_, '_, '_>, |
2561 | | ty_res: &proc::TypeResolution, |
2562 | 0 | orig_expr| { |
2563 | 0 | ( |
2564 | 0 | ctx.ast_expressions.get_span(orig_expr), |
2565 | 0 | format!("`{}`", ctx.as_diagnostic_display(ty_res)), |
2566 | 0 | ) |
2567 | 0 | }; |
2568 | 4 | for (&value, orig_value) in |
2569 | 2 | values.iter().zip([reject_orig, accept_orig]) |
2570 | | { |
2571 | 4 | let value_ty_res = resolve!(ctx, value); |
2572 | 4 | if value_ty_res |
2573 | 4 | .inner_with(&ctx.module.types) |
2574 | 4 | .vector_size_and_scalar() |
2575 | 4 | .is_none() |
2576 | | { |
2577 | 0 | let (arg_span, arg_type) = |
2578 | 0 | diagnostic_details(ctx, value_ty_res, orig_value); |
2579 | 0 | return Err(Box::new(Error::SelectUnexpectedArgumentType { |
2580 | 0 | arg_span, |
2581 | 0 | arg_type, |
2582 | 0 | })); |
2583 | 4 | } |
2584 | | } |
2585 | 2 | let mut consensus_scalar = ctx |
2586 | 2 | .automatic_conversion_consensus(&values) |
2587 | 2 | .map_err(|_idx| { |
2588 | 0 | let [reject, accept] = values; |
2589 | 0 | let [(reject_span, reject_type), (accept_span, accept_type)] = |
2590 | 0 | [(reject_orig, reject), (accept_orig, accept)].map( |
2591 | 0 | |(orig_expr, expr)| { |
2592 | 0 | let ty_res = &ctx.typifier()[expr]; |
2593 | 0 | diagnostic_details(ctx, ty_res, orig_expr) |
2594 | 0 | }, |
2595 | | ); |
2596 | 0 | Error::SelectRejectAndAcceptHaveNoCommonType { |
2597 | 0 | reject_span, |
2598 | 0 | reject_type, |
2599 | 0 | accept_span, |
2600 | 0 | accept_type, |
2601 | 0 | } |
2602 | 0 | })?; |
2603 | 2 | if !ctx.is_const(condition) { |
2604 | 0 | consensus_scalar = consensus_scalar.concretize(); |
2605 | 2 | } |
2606 | | |
2607 | 2 | ctx.convert_slice_to_common_leaf_scalar(&mut values, consensus_scalar)?; |
2608 | | |
2609 | 2 | let [reject, accept] = values; |
2610 | | |
2611 | 2 | ir::Expression::Select { |
2612 | 2 | reject, |
2613 | 2 | accept, |
2614 | 2 | condition, |
2615 | 2 | } |
2616 | | } |
2617 | 1 | "arrayLength" => { |
2618 | 0 | let mut args = ctx.prepare_args(arguments, 1, span); |
2619 | 0 | let expr = self.expression(args.next()?, ctx)?; |
2620 | 0 | args.finish()?; |
2621 | | |
2622 | 0 | ir::Expression::ArrayLength(expr) |
2623 | | } |
2624 | 1 | "atomicLoad" => { |
2625 | 0 | let mut args = ctx.prepare_args(arguments, 1, span); |
2626 | 0 | let (pointer, _scalar) = self.atomic_pointer(args.next()?, ctx)?; |
2627 | 0 | args.finish()?; |
2628 | | |
2629 | 0 | ir::Expression::Load { pointer } |
2630 | | } |
2631 | 1 | "atomicStore" => { |
2632 | 0 | let mut args = ctx.prepare_args(arguments, 2, span); |
2633 | 0 | let (pointer, scalar) = self.atomic_pointer(args.next()?, ctx)?; |
2634 | 0 | let value = |
2635 | 0 | self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?; |
2636 | 0 | args.finish()?; |
2637 | | |
2638 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
2639 | 0 | rctx.block |
2640 | 0 | .extend(rctx.emitter.finish(&rctx.function.expressions)); |
2641 | 0 | rctx.emitter.start(&rctx.function.expressions); |
2642 | 0 | rctx.block |
2643 | 0 | .push(ir::Statement::Store { pointer, value }, span); |
2644 | 0 | return Ok(None); |
2645 | | } |
2646 | 1 | "atomicCompareExchangeWeak" => { |
2647 | 0 | let mut args = ctx.prepare_args(arguments, 3, span); |
2648 | | |
2649 | 0 | let (pointer, scalar) = self.atomic_pointer(args.next()?, ctx)?; |
2650 | | |
2651 | 0 | let compare = |
2652 | 0 | self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?; |
2653 | | |
2654 | 0 | let value = args.next()?; |
2655 | 0 | let value_span = ctx.ast_expressions.get_span(value); |
2656 | 0 | let value = self.expression_with_leaf_scalar(value, scalar, ctx)?; |
2657 | | |
2658 | 0 | args.finish()?; |
2659 | | |
2660 | 0 | let expression = match *resolve_inner!(ctx, value) { |
2661 | 0 | ir::TypeInner::Scalar(scalar) => ir::Expression::AtomicResult { |
2662 | 0 | ty: ctx.module.generate_predeclared_type( |
2663 | 0 | ir::PredeclaredType::AtomicCompareExchangeWeakResult( |
2664 | 0 | scalar, |
2665 | 0 | ), |
2666 | 0 | ), |
2667 | 0 | comparison: true, |
2668 | 0 | }, |
2669 | | _ => { |
2670 | 0 | return Err(Box::new(Error::InvalidAtomicOperandType( |
2671 | 0 | value_span, |
2672 | 0 | ))) |
2673 | | } |
2674 | | }; |
2675 | | |
2676 | 0 | let result = ctx.interrupt_emitter(expression, span)?; |
2677 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
2678 | 0 | rctx.block.push( |
2679 | 0 | ir::Statement::Atomic { |
2680 | 0 | pointer, |
2681 | 0 | fun: ir::AtomicFunction::Exchange { |
2682 | 0 | compare: Some(compare), |
2683 | 0 | }, |
2684 | 0 | value, |
2685 | 0 | result: Some(result), |
2686 | 0 | }, |
2687 | 0 | span, |
2688 | | ); |
2689 | 0 | return Ok(Some(result)); |
2690 | | } |
2691 | 1 | "textureAtomicMin" | "textureAtomicMax" | "textureAtomicAdd" |
2692 | 1 | | "textureAtomicAnd" | "textureAtomicOr" | "textureAtomicXor" => { |
2693 | 0 | let mut args = ctx.prepare_args(arguments, 3, span); |
2694 | | |
2695 | 0 | let image = args.next()?; |
2696 | 0 | let image_span = ctx.ast_expressions.get_span(image); |
2697 | 0 | let image = self.expression(image, ctx)?; |
2698 | | |
2699 | 0 | let coordinate = self.expression(args.next()?, ctx)?; |
2700 | | |
2701 | 0 | let (_, arrayed) = ctx.image_data(image, image_span)?; |
2702 | 0 | let array_index = arrayed |
2703 | 0 | .then(|| { |
2704 | 0 | args.min_args += 1; |
2705 | 0 | self.expression(args.next()?, ctx) |
2706 | 0 | }) |
2707 | 0 | .transpose()?; |
2708 | | |
2709 | 0 | let value = self.expression(args.next()?, ctx)?; |
2710 | | |
2711 | 0 | args.finish()?; |
2712 | | |
2713 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
2714 | 0 | rctx.block |
2715 | 0 | .extend(rctx.emitter.finish(&rctx.function.expressions)); |
2716 | 0 | rctx.emitter.start(&rctx.function.expressions); |
2717 | 0 | let stmt = ir::Statement::ImageAtomic { |
2718 | 0 | image, |
2719 | 0 | coordinate, |
2720 | 0 | array_index, |
2721 | 0 | fun: match function.name { |
2722 | 0 | "textureAtomicMin" => ir::AtomicFunction::Min, |
2723 | 0 | "textureAtomicMax" => ir::AtomicFunction::Max, |
2724 | 0 | "textureAtomicAdd" => ir::AtomicFunction::Add, |
2725 | 0 | "textureAtomicAnd" => ir::AtomicFunction::And, |
2726 | 0 | "textureAtomicOr" => ir::AtomicFunction::InclusiveOr, |
2727 | 0 | "textureAtomicXor" => ir::AtomicFunction::ExclusiveOr, |
2728 | 0 | _ => unreachable!(), |
2729 | | }, |
2730 | 0 | value, |
2731 | | }; |
2732 | 0 | rctx.block.push(stmt, span); |
2733 | 0 | return Ok(None); |
2734 | | } |
2735 | 1 | "storageBarrier" => { |
2736 | 0 | ctx.prepare_args(arguments, 0, span).finish()?; |
2737 | | |
2738 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
2739 | 0 | rctx.block |
2740 | 0 | .push(ir::Statement::ControlBarrier(ir::Barrier::STORAGE), span); |
2741 | 0 | return Ok(None); |
2742 | | } |
2743 | 1 | "workgroupBarrier" => { |
2744 | 0 | ctx.prepare_args(arguments, 0, span).finish()?; |
2745 | | |
2746 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
2747 | 0 | rctx.block |
2748 | 0 | .push(ir::Statement::ControlBarrier(ir::Barrier::WORK_GROUP), span); |
2749 | 0 | return Ok(None); |
2750 | | } |
2751 | 1 | "subgroupBarrier" => { |
2752 | 0 | ctx.prepare_args(arguments, 0, span).finish()?; |
2753 | | |
2754 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
2755 | 0 | rctx.block |
2756 | 0 | .push(ir::Statement::ControlBarrier(ir::Barrier::SUB_GROUP), span); |
2757 | 0 | return Ok(None); |
2758 | | } |
2759 | 1 | "textureBarrier" => { |
2760 | 0 | ctx.prepare_args(arguments, 0, span).finish()?; |
2761 | | |
2762 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
2763 | 0 | rctx.block |
2764 | 0 | .push(ir::Statement::ControlBarrier(ir::Barrier::TEXTURE), span); |
2765 | 0 | return Ok(None); |
2766 | | } |
2767 | 1 | "workgroupUniformLoad" => { |
2768 | 1 | let mut args = ctx.prepare_args(arguments, 1, span); |
2769 | 1 | let expr = args.next()?; |
2770 | 1 | args.finish()?; |
2771 | | |
2772 | 1 | let pointer = self.expression(expr, ctx)?; |
2773 | 1 | let result_ty = match *resolve_inner!(ctx, pointer) { |
2774 | | ir::TypeInner::Pointer { |
2775 | 0 | base, |
2776 | | space: ir::AddressSpace::WorkGroup, |
2777 | 0 | } => base, |
2778 | 1 | ref other => { |
2779 | 1 | log::error!("Type {other:?} passed to workgroupUniformLoad"); |
2780 | 1 | let span = ctx.ast_expressions.get_span(expr); |
2781 | 1 | return Err(Box::new(Error::InvalidWorkGroupUniformLoad(span))); |
2782 | | } |
2783 | | }; |
2784 | 0 | let result = ctx.interrupt_emitter( |
2785 | 0 | ir::Expression::WorkGroupUniformLoadResult { ty: result_ty }, |
2786 | 0 | span, |
2787 | 0 | )?; |
2788 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
2789 | 0 | rctx.block.push( |
2790 | 0 | ir::Statement::WorkGroupUniformLoad { pointer, result }, |
2791 | 0 | span, |
2792 | | ); |
2793 | | |
2794 | 0 | return Ok(Some(result)); |
2795 | | } |
2796 | 0 | "textureStore" => { |
2797 | 0 | let mut args = ctx.prepare_args(arguments, 3, span); |
2798 | | |
2799 | 0 | let image = args.next()?; |
2800 | 0 | let image_span = ctx.ast_expressions.get_span(image); |
2801 | 0 | let image = self.expression(image, ctx)?; |
2802 | | |
2803 | 0 | let coordinate = self.expression(args.next()?, ctx)?; |
2804 | | |
2805 | 0 | let (class, arrayed) = ctx.image_data(image, image_span)?; |
2806 | 0 | let array_index = arrayed |
2807 | 0 | .then(|| { |
2808 | 0 | args.min_args += 1; |
2809 | 0 | self.expression(args.next()?, ctx) |
2810 | 0 | }) |
2811 | 0 | .transpose()?; |
2812 | 0 | let scalar = if let ir::ImageClass::Storage { format, .. } = class { |
2813 | 0 | format.into() |
2814 | | } else { |
2815 | 0 | return Err(Box::new(Error::NotStorageTexture(image_span))); |
2816 | | }; |
2817 | | |
2818 | 0 | let value = |
2819 | 0 | self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?; |
2820 | | |
2821 | 0 | args.finish()?; |
2822 | | |
2823 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
2824 | 0 | rctx.block |
2825 | 0 | .extend(rctx.emitter.finish(&rctx.function.expressions)); |
2826 | 0 | rctx.emitter.start(&rctx.function.expressions); |
2827 | 0 | let stmt = ir::Statement::ImageStore { |
2828 | 0 | image, |
2829 | 0 | coordinate, |
2830 | 0 | array_index, |
2831 | 0 | value, |
2832 | 0 | }; |
2833 | 0 | rctx.block.push(stmt, span); |
2834 | 0 | return Ok(None); |
2835 | | } |
2836 | 0 | "textureLoad" => { |
2837 | 0 | let mut args = ctx.prepare_args(arguments, 2, span); |
2838 | | |
2839 | 0 | let image = args.next()?; |
2840 | 0 | let image_span = ctx.ast_expressions.get_span(image); |
2841 | 0 | let image = self.expression(image, ctx)?; |
2842 | | |
2843 | 0 | let coordinate = self.expression(args.next()?, ctx)?; |
2844 | | |
2845 | 0 | let (class, arrayed) = ctx.image_data(image, image_span)?; |
2846 | 0 | let array_index = arrayed |
2847 | 0 | .then(|| { |
2848 | 0 | args.min_args += 1; |
2849 | 0 | self.expression(args.next()?, ctx) |
2850 | 0 | }) |
2851 | 0 | .transpose()?; |
2852 | | |
2853 | 0 | let level = class |
2854 | 0 | .is_mipmapped() |
2855 | 0 | .then(|| { |
2856 | 0 | args.min_args += 1; |
2857 | 0 | self.expression(args.next()?, ctx) |
2858 | 0 | }) |
2859 | 0 | .transpose()?; |
2860 | | |
2861 | 0 | let sample = class |
2862 | 0 | .is_multisampled() |
2863 | 0 | .then(|| self.expression(args.next()?, ctx)) |
2864 | 0 | .transpose()?; |
2865 | | |
2866 | 0 | args.finish()?; |
2867 | | |
2868 | 0 | ir::Expression::ImageLoad { |
2869 | 0 | image, |
2870 | 0 | coordinate, |
2871 | 0 | array_index, |
2872 | 0 | level, |
2873 | 0 | sample, |
2874 | 0 | } |
2875 | | } |
2876 | 0 | "textureDimensions" => { |
2877 | 0 | let mut args = ctx.prepare_args(arguments, 1, span); |
2878 | 0 | let image = self.expression(args.next()?, ctx)?; |
2879 | 0 | let level = args |
2880 | 0 | .next() |
2881 | 0 | .map(|arg| self.expression(arg, ctx)) |
2882 | 0 | .ok() |
2883 | 0 | .transpose()?; |
2884 | 0 | args.finish()?; |
2885 | | |
2886 | 0 | ir::Expression::ImageQuery { |
2887 | 0 | image, |
2888 | 0 | query: ir::ImageQuery::Size { level }, |
2889 | 0 | } |
2890 | | } |
2891 | 0 | "textureNumLevels" => { |
2892 | 0 | let mut args = ctx.prepare_args(arguments, 1, span); |
2893 | 0 | let image = self.expression(args.next()?, ctx)?; |
2894 | 0 | args.finish()?; |
2895 | | |
2896 | 0 | ir::Expression::ImageQuery { |
2897 | 0 | image, |
2898 | 0 | query: ir::ImageQuery::NumLevels, |
2899 | 0 | } |
2900 | | } |
2901 | 0 | "textureNumLayers" => { |
2902 | 0 | let mut args = ctx.prepare_args(arguments, 1, span); |
2903 | 0 | let image = self.expression(args.next()?, ctx)?; |
2904 | 0 | args.finish()?; |
2905 | | |
2906 | 0 | ir::Expression::ImageQuery { |
2907 | 0 | image, |
2908 | 0 | query: ir::ImageQuery::NumLayers, |
2909 | 0 | } |
2910 | | } |
2911 | 0 | "textureNumSamples" => { |
2912 | 0 | let mut args = ctx.prepare_args(arguments, 1, span); |
2913 | 0 | let image = self.expression(args.next()?, ctx)?; |
2914 | 0 | args.finish()?; |
2915 | | |
2916 | 0 | ir::Expression::ImageQuery { |
2917 | 0 | image, |
2918 | 0 | query: ir::ImageQuery::NumSamples, |
2919 | 0 | } |
2920 | | } |
2921 | 0 | "rayQueryInitialize" => { |
2922 | 0 | let mut args = ctx.prepare_args(arguments, 3, span); |
2923 | 0 | let query = self.ray_query_pointer(args.next()?, ctx)?; |
2924 | 0 | let acceleration_structure = self.expression(args.next()?, ctx)?; |
2925 | 0 | let descriptor = self.expression(args.next()?, ctx)?; |
2926 | 0 | args.finish()?; |
2927 | | |
2928 | 0 | let _ = ctx.module.generate_ray_desc_type(); |
2929 | 0 | let fun = ir::RayQueryFunction::Initialize { |
2930 | 0 | acceleration_structure, |
2931 | 0 | descriptor, |
2932 | 0 | }; |
2933 | | |
2934 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
2935 | 0 | rctx.block |
2936 | 0 | .extend(rctx.emitter.finish(&rctx.function.expressions)); |
2937 | 0 | rctx.emitter.start(&rctx.function.expressions); |
2938 | 0 | rctx.block |
2939 | 0 | .push(ir::Statement::RayQuery { query, fun }, span); |
2940 | 0 | return Ok(None); |
2941 | | } |
2942 | 0 | "getCommittedHitVertexPositions" => { |
2943 | 0 | let mut args = ctx.prepare_args(arguments, 1, span); |
2944 | 0 | let query = self.ray_query_pointer(args.next()?, ctx)?; |
2945 | 0 | args.finish()?; |
2946 | | |
2947 | 0 | let _ = ctx.module.generate_vertex_return_type(); |
2948 | | |
2949 | 0 | ir::Expression::RayQueryVertexPositions { |
2950 | 0 | query, |
2951 | 0 | committed: true, |
2952 | 0 | } |
2953 | | } |
2954 | 0 | "getCandidateHitVertexPositions" => { |
2955 | 0 | let mut args = ctx.prepare_args(arguments, 1, span); |
2956 | 0 | let query = self.ray_query_pointer(args.next()?, ctx)?; |
2957 | 0 | args.finish()?; |
2958 | | |
2959 | 0 | let _ = ctx.module.generate_vertex_return_type(); |
2960 | | |
2961 | 0 | ir::Expression::RayQueryVertexPositions { |
2962 | 0 | query, |
2963 | 0 | committed: false, |
2964 | 0 | } |
2965 | | } |
2966 | 0 | "rayQueryProceed" => { |
2967 | 0 | let mut args = ctx.prepare_args(arguments, 1, span); |
2968 | 0 | let query = self.ray_query_pointer(args.next()?, ctx)?; |
2969 | 0 | args.finish()?; |
2970 | | |
2971 | 0 | let result = |
2972 | 0 | ctx.interrupt_emitter(ir::Expression::RayQueryProceedResult, span)?; |
2973 | 0 | let fun = ir::RayQueryFunction::Proceed { result }; |
2974 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
2975 | 0 | rctx.block |
2976 | 0 | .push(ir::Statement::RayQuery { query, fun }, span); |
2977 | 0 | return Ok(Some(result)); |
2978 | | } |
2979 | 0 | "rayQueryGenerateIntersection" => { |
2980 | 0 | let mut args = ctx.prepare_args(arguments, 2, span); |
2981 | 0 | let query = self.ray_query_pointer(args.next()?, ctx)?; |
2982 | 0 | let hit_t = self.expression(args.next()?, ctx)?; |
2983 | 0 | args.finish()?; |
2984 | | |
2985 | 0 | let fun = ir::RayQueryFunction::GenerateIntersection { hit_t }; |
2986 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
2987 | 0 | rctx.block |
2988 | 0 | .push(ir::Statement::RayQuery { query, fun }, span); |
2989 | 0 | return Ok(None); |
2990 | | } |
2991 | 0 | "rayQueryConfirmIntersection" => { |
2992 | 0 | let mut args = ctx.prepare_args(arguments, 1, span); |
2993 | 0 | let query = self.ray_query_pointer(args.next()?, ctx)?; |
2994 | 0 | args.finish()?; |
2995 | | |
2996 | 0 | let fun = ir::RayQueryFunction::ConfirmIntersection; |
2997 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
2998 | 0 | rctx.block |
2999 | 0 | .push(ir::Statement::RayQuery { query, fun }, span); |
3000 | 0 | return Ok(None); |
3001 | | } |
3002 | 0 | "rayQueryTerminate" => { |
3003 | 0 | let mut args = ctx.prepare_args(arguments, 1, span); |
3004 | 0 | let query = self.ray_query_pointer(args.next()?, ctx)?; |
3005 | 0 | args.finish()?; |
3006 | | |
3007 | 0 | let fun = ir::RayQueryFunction::Terminate; |
3008 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
3009 | 0 | rctx.block |
3010 | 0 | .push(ir::Statement::RayQuery { query, fun }, span); |
3011 | 0 | return Ok(None); |
3012 | | } |
3013 | 0 | "rayQueryGetCommittedIntersection" => { |
3014 | 0 | let mut args = ctx.prepare_args(arguments, 1, span); |
3015 | 0 | let query = self.ray_query_pointer(args.next()?, ctx)?; |
3016 | 0 | args.finish()?; |
3017 | | |
3018 | 0 | let _ = ctx.module.generate_ray_intersection_type(); |
3019 | 0 | ir::Expression::RayQueryGetIntersection { |
3020 | 0 | query, |
3021 | 0 | committed: true, |
3022 | 0 | } |
3023 | | } |
3024 | 0 | "rayQueryGetCandidateIntersection" => { |
3025 | 0 | let mut args = ctx.prepare_args(arguments, 1, span); |
3026 | 0 | let query = self.ray_query_pointer(args.next()?, ctx)?; |
3027 | 0 | args.finish()?; |
3028 | | |
3029 | 0 | let _ = ctx.module.generate_ray_intersection_type(); |
3030 | 0 | ir::Expression::RayQueryGetIntersection { |
3031 | 0 | query, |
3032 | 0 | committed: false, |
3033 | 0 | } |
3034 | | } |
3035 | 0 | "RayDesc" => { |
3036 | 0 | let ty = ctx.module.generate_ray_desc_type(); |
3037 | 0 | let handle = self.construct( |
3038 | 0 | span, |
3039 | 0 | &ast::ConstructorType::Type(ty), |
3040 | 0 | function.span, |
3041 | 0 | arguments, |
3042 | 0 | ctx, |
3043 | 0 | )?; |
3044 | 0 | return Ok(Some(handle)); |
3045 | | } |
3046 | 0 | "subgroupBallot" => { |
3047 | 0 | let mut args = ctx.prepare_args(arguments, 0, span); |
3048 | 0 | let predicate = if arguments.len() == 1 { |
3049 | 0 | Some(self.expression(args.next()?, ctx)?) |
3050 | | } else { |
3051 | 0 | None |
3052 | | }; |
3053 | 0 | args.finish()?; |
3054 | | |
3055 | 0 | let result = |
3056 | 0 | ctx.interrupt_emitter(ir::Expression::SubgroupBallotResult, span)?; |
3057 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
3058 | 0 | rctx.block |
3059 | 0 | .push(ir::Statement::SubgroupBallot { result, predicate }, span); |
3060 | 0 | return Ok(Some(result)); |
3061 | | } |
3062 | 0 | "quadSwapX" => { |
3063 | 0 | let mut args = ctx.prepare_args(arguments, 1, span); |
3064 | | |
3065 | 0 | let argument = self.expression(args.next()?, ctx)?; |
3066 | 0 | args.finish()?; |
3067 | | |
3068 | 0 | let ty = ctx.register_type(argument)?; |
3069 | | |
3070 | 0 | let result = ctx.interrupt_emitter( |
3071 | 0 | crate::Expression::SubgroupOperationResult { ty }, |
3072 | 0 | span, |
3073 | 0 | )?; |
3074 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
3075 | 0 | rctx.block.push( |
3076 | 0 | crate::Statement::SubgroupGather { |
3077 | 0 | mode: crate::GatherMode::QuadSwap(crate::Direction::X), |
3078 | 0 | argument, |
3079 | 0 | result, |
3080 | 0 | }, |
3081 | 0 | span, |
3082 | | ); |
3083 | 0 | return Ok(Some(result)); |
3084 | | } |
3085 | | |
3086 | 0 | "quadSwapY" => { |
3087 | 0 | let mut args = ctx.prepare_args(arguments, 1, span); |
3088 | | |
3089 | 0 | let argument = self.expression(args.next()?, ctx)?; |
3090 | 0 | args.finish()?; |
3091 | | |
3092 | 0 | let ty = ctx.register_type(argument)?; |
3093 | | |
3094 | 0 | let result = ctx.interrupt_emitter( |
3095 | 0 | crate::Expression::SubgroupOperationResult { ty }, |
3096 | 0 | span, |
3097 | 0 | )?; |
3098 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
3099 | 0 | rctx.block.push( |
3100 | 0 | crate::Statement::SubgroupGather { |
3101 | 0 | mode: crate::GatherMode::QuadSwap(crate::Direction::Y), |
3102 | 0 | argument, |
3103 | 0 | result, |
3104 | 0 | }, |
3105 | 0 | span, |
3106 | | ); |
3107 | 0 | return Ok(Some(result)); |
3108 | | } |
3109 | | |
3110 | 0 | "quadSwapDiagonal" => { |
3111 | 0 | let mut args = ctx.prepare_args(arguments, 1, span); |
3112 | | |
3113 | 0 | let argument = self.expression(args.next()?, ctx)?; |
3114 | 0 | args.finish()?; |
3115 | | |
3116 | 0 | let ty = ctx.register_type(argument)?; |
3117 | | |
3118 | 0 | let result = ctx.interrupt_emitter( |
3119 | 0 | crate::Expression::SubgroupOperationResult { ty }, |
3120 | 0 | span, |
3121 | 0 | )?; |
3122 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
3123 | 0 | rctx.block.push( |
3124 | 0 | crate::Statement::SubgroupGather { |
3125 | 0 | mode: crate::GatherMode::QuadSwap(crate::Direction::Diagonal), |
3126 | 0 | argument, |
3127 | 0 | result, |
3128 | 0 | }, |
3129 | 0 | span, |
3130 | | ); |
3131 | 0 | return Ok(Some(result)); |
3132 | | } |
3133 | | _ => { |
3134 | 0 | return Err(Box::new(Error::UnknownIdent(function.span, function.name))) |
3135 | | } |
3136 | | } |
3137 | | }; |
3138 | | |
3139 | 3 | let expr = ctx.append_expression(expr, span)?; |
3140 | 1 | Ok(Some(expr)) |
3141 | | } |
3142 | | } |
3143 | 4 | } |
3144 | | |
3145 | | /// Generate a Naga IR [`Math`] expression. |
3146 | | /// |
3147 | | /// Generate Naga IR for a call to the [`MathFunction`] `fun`, whose |
3148 | | /// unlowered arguments are `ast_arguments`. |
3149 | | /// |
3150 | | /// The `span` argument should give the span of the function name in the |
3151 | | /// call expression. |
3152 | | /// |
3153 | | /// [`Math`]: ir::Expression::Math |
3154 | | /// [`MathFunction`]: ir::MathFunction |
3155 | 1 | fn math_function_helper( |
3156 | 1 | &mut self, |
3157 | 1 | span: Span, |
3158 | 1 | fun: ir::MathFunction, |
3159 | 1 | ast_arguments: &[Handle<ast::Expression<'source>>], |
3160 | 1 | ctx: &mut ExpressionContext<'source, '_, '_>, |
3161 | 1 | ) -> Result<'source, ir::Expression> { |
3162 | 1 | let mut lowered_arguments = Vec::with_capacity(ast_arguments.len()); |
3163 | 2 | for &arg in ast_arguments { |
3164 | 1 | let lowered = self.expression_for_abstract(arg, ctx)?; |
3165 | 1 | ctx.grow_types(lowered)?; |
3166 | 1 | lowered_arguments.push(lowered); |
3167 | | } |
3168 | | |
3169 | 1 | let fun_overloads = fun.overloads(); |
3170 | 1 | let rule = self.resolve_overloads(span, fun, fun_overloads, &lowered_arguments, ctx)?; |
3171 | 1 | self.apply_automatic_conversions_for_call(&rule, &mut lowered_arguments, ctx)?; |
3172 | | |
3173 | | // If this function returns a predeclared type, register it |
3174 | | // in `Module::special_types`. The typifier will expect to |
3175 | | // be able to find it there. |
3176 | 1 | if let proc::Conclusion::Predeclared(predeclared) = rule.conclusion { |
3177 | 0 | ctx.module.generate_predeclared_type(predeclared); |
3178 | 1 | } |
3179 | | |
3180 | 1 | Ok(ir::Expression::Math { |
3181 | 1 | fun, |
3182 | 1 | arg: lowered_arguments[0], |
3183 | 1 | arg1: lowered_arguments.get(1).cloned(), |
3184 | 1 | arg2: lowered_arguments.get(2).cloned(), |
3185 | 1 | arg3: lowered_arguments.get(3).cloned(), |
3186 | 1 | }) |
3187 | 1 | } |
3188 | | |
3189 | | /// Choose the right overload for a function call. |
3190 | | /// |
3191 | | /// Return a [`Rule`] representing the most preferred overload in |
3192 | | /// `overloads` to apply to `arguments`, or return an error explaining why |
3193 | | /// the call is not valid. |
3194 | | /// |
3195 | | /// Use `fun` to identify the function being called in error messages; |
3196 | | /// `span` should be the span of the function name in the call expression. |
3197 | | /// |
3198 | | /// [`Rule`]: proc::Rule |
3199 | 1 | fn resolve_overloads<O, F>( |
3200 | 1 | &self, |
3201 | 1 | span: Span, |
3202 | 1 | fun: F, |
3203 | 1 | overloads: O, |
3204 | 1 | arguments: &[Handle<ir::Expression>], |
3205 | 1 | ctx: &ExpressionContext<'source, '_, '_>, |
3206 | 1 | ) -> Result<'source, proc::Rule> |
3207 | 1 | where |
3208 | 1 | O: proc::OverloadSet, |
3209 | 1 | F: TryToWgsl + core::fmt::Debug + Copy, |
3210 | | { |
3211 | 1 | let mut remaining_overloads = overloads.clone(); |
3212 | 1 | let min_arguments = remaining_overloads.min_arguments(); |
3213 | 1 | let max_arguments = remaining_overloads.max_arguments(); |
3214 | 1 | if arguments.len() < min_arguments { |
3215 | 0 | return Err(Box::new(Error::WrongArgumentCount { |
3216 | 0 | span, |
3217 | 0 | expected: min_arguments as u32..max_arguments as u32, |
3218 | 0 | found: arguments.len() as u32, |
3219 | 0 | })); |
3220 | 1 | } |
3221 | 1 | if arguments.len() > max_arguments { |
3222 | 0 | return Err(Box::new(Error::TooManyArguments { |
3223 | 0 | function: fun.to_wgsl_for_diagnostics(), |
3224 | 0 | call_span: span, |
3225 | 0 | arg_span: ctx.get_expression_span(arguments[max_arguments]), |
3226 | 0 | max_arguments: max_arguments as _, |
3227 | 0 | })); |
3228 | 1 | } |
3229 | | |
3230 | 1 | log::debug!( |
3231 | 0 | "Initial overloads: {:#?}", |
3232 | 0 | remaining_overloads.for_debug(&ctx.module.types) |
3233 | | ); |
3234 | | |
3235 | 1 | for (arg_index, &arg) in arguments.iter().enumerate() { |
3236 | 1 | let arg_type_resolution = &ctx.typifier()[arg]; |
3237 | 1 | let arg_inner = arg_type_resolution.inner_with(&ctx.module.types); |
3238 | 1 | log::debug!( |
3239 | 0 | "Supplying argument {arg_index} of type {:?}", |
3240 | 0 | arg_type_resolution.for_debug(&ctx.module.types) |
3241 | | ); |
3242 | 1 | let next_remaining_overloads = |
3243 | 1 | remaining_overloads.arg(arg_index, arg_inner, &ctx.module.types); |
3244 | | |
3245 | | // If any argument is not a constant expression, then no overloads |
3246 | | // that accept abstract values should be considered. |
3247 | | // (`OverloadSet::concrete_only` is supposed to help impose this |
3248 | | // restriction.) However, no `MathFunction` accepts a mix of |
3249 | | // abstract and concrete arguments, so we don't need to worry |
3250 | | // about that here. |
3251 | | |
3252 | 1 | log::debug!( |
3253 | 0 | "Remaining overloads: {:#?}", |
3254 | 0 | next_remaining_overloads.for_debug(&ctx.module.types) |
3255 | | ); |
3256 | | |
3257 | | // If the set of remaining overloads is empty, then this argument's type |
3258 | | // was unacceptable. Diagnose the problem and produce an error message. |
3259 | 1 | if next_remaining_overloads.is_empty() { |
3260 | 0 | let function = fun.to_wgsl_for_diagnostics(); |
3261 | 0 | let call_span = span; |
3262 | 0 | let arg_span = ctx.get_expression_span(arg); |
3263 | 0 | let arg_ty = ctx.as_diagnostic_display(arg_type_resolution).to_string(); |
3264 | | |
3265 | | // Is this type *ever* permitted for the arg_index'th argument? |
3266 | | // For example, `bool` is never permitted for `max`. |
3267 | 0 | let only_this_argument = overloads.arg(arg_index, arg_inner, &ctx.module.types); |
3268 | 0 | if only_this_argument.is_empty() { |
3269 | | // No overload of `fun` accepts this type as the |
3270 | | // arg_index'th argument. Determine the set of types that |
3271 | | // would ever be allowed there. |
3272 | 0 | let allowed: Vec<String> = overloads |
3273 | 0 | .allowed_args(arg_index, &ctx.module.to_ctx()) |
3274 | 0 | .iter() |
3275 | 0 | .map(|ty| ctx.type_resolution_to_string(ty)) |
3276 | 0 | .collect(); |
3277 | | |
3278 | 0 | if allowed.is_empty() { |
3279 | | // No overload of `fun` accepts any argument at this |
3280 | | // index, so it's a simple case of excess arguments. |
3281 | | // However, since each `MathFunction`'s overloads all |
3282 | | // have the same arity, we should have detected this |
3283 | | // earlier. |
3284 | 0 | unreachable!("expected all overloads to have the same arity"); |
3285 | 0 | } |
3286 | | |
3287 | | // Some overloads of `fun` do accept this many arguments, |
3288 | | // but none accept one of this type. |
3289 | 0 | return Err(Box::new(Error::WrongArgumentType { |
3290 | 0 | function, |
3291 | 0 | call_span, |
3292 | 0 | arg_span, |
3293 | 0 | arg_index: arg_index as u32, |
3294 | 0 | arg_ty, |
3295 | 0 | allowed, |
3296 | 0 | })); |
3297 | 0 | } |
3298 | | |
3299 | | // This argument's type is accepted by some overloads---just |
3300 | | // not those overloads that remain, given the prior arguments. |
3301 | | // For example, `max` accepts `f32` as its second argument - |
3302 | | // but not if the first was `i32`. |
3303 | | |
3304 | | // Build a list of the types that would have been accepted here, |
3305 | | // given the prior arguments. |
3306 | 0 | let allowed: Vec<String> = remaining_overloads |
3307 | 0 | .allowed_args(arg_index, &ctx.module.to_ctx()) |
3308 | 0 | .iter() |
3309 | 0 | .map(|ty| ctx.type_resolution_to_string(ty)) |
3310 | 0 | .collect(); |
3311 | | |
3312 | | // Re-run the argument list to determine which prior argument |
3313 | | // made this one unacceptable. |
3314 | 0 | let mut remaining_overloads = overloads; |
3315 | 0 | for (prior_index, &prior_expr) in arguments.iter().enumerate() { |
3316 | 0 | let prior_type_resolution = &ctx.typifier()[prior_expr]; |
3317 | 0 | let prior_ty = prior_type_resolution.inner_with(&ctx.module.types); |
3318 | 0 | remaining_overloads = |
3319 | 0 | remaining_overloads.arg(prior_index, prior_ty, &ctx.module.types); |
3320 | 0 | if remaining_overloads |
3321 | 0 | .arg(arg_index, arg_inner, &ctx.module.types) |
3322 | 0 | .is_empty() |
3323 | | { |
3324 | | // This is the argument that killed our dreams. |
3325 | 0 | let inconsistent_span = ctx.get_expression_span(arguments[prior_index]); |
3326 | 0 | let inconsistent_ty = |
3327 | 0 | ctx.as_diagnostic_display(prior_type_resolution).to_string(); |
3328 | | |
3329 | 0 | if allowed.is_empty() { |
3330 | | // Some overloads did accept `ty` at `arg_index`, but |
3331 | | // given the arguments up through `prior_expr`, we see |
3332 | | // no types acceptable at `arg_index`. This means that some |
3333 | | // overloads expect fewer arguments than others. However, |
3334 | | // each `MathFunction`'s overloads have the same arity, so this |
3335 | | // should be impossible. |
3336 | 0 | unreachable!("expected all overloads to have the same arity"); |
3337 | 0 | } |
3338 | | |
3339 | | // Report `arg`'s type as inconsistent with `prior_expr`'s |
3340 | 0 | return Err(Box::new(Error::InconsistentArgumentType { |
3341 | 0 | function, |
3342 | 0 | call_span, |
3343 | 0 | arg_span, |
3344 | 0 | arg_index: arg_index as u32, |
3345 | 0 | arg_ty, |
3346 | 0 | inconsistent_span, |
3347 | 0 | inconsistent_index: prior_index as u32, |
3348 | 0 | inconsistent_ty, |
3349 | 0 | allowed, |
3350 | 0 | })); |
3351 | 0 | } |
3352 | | } |
3353 | 0 | unreachable!("Failed to eliminate argument type when re-tried"); |
3354 | 1 | } |
3355 | 1 | remaining_overloads = next_remaining_overloads; |
3356 | | } |
3357 | | |
3358 | | // Select the most preferred type rule for this call, |
3359 | | // given the argument types supplied above. |
3360 | 1 | Ok(remaining_overloads.most_preferred()) |
3361 | 1 | } |
3362 | | |
3363 | | /// Apply automatic type conversions for a function call. |
3364 | | /// |
3365 | | /// Apply whatever automatic conversions are needed to pass `arguments` to |
3366 | | /// the function overload described by `rule`. Update `arguments` to refer |
3367 | | /// to the converted arguments. |
3368 | 1 | fn apply_automatic_conversions_for_call( |
3369 | 1 | &self, |
3370 | 1 | rule: &proc::Rule, |
3371 | 1 | arguments: &mut [Handle<ir::Expression>], |
3372 | 1 | ctx: &mut ExpressionContext<'source, '_, '_>, |
3373 | 1 | ) -> Result<'source, ()> { |
3374 | 1 | for (i, argument) in arguments.iter_mut().enumerate() { |
3375 | 1 | let goal_inner = rule.arguments[i].inner_with(&ctx.module.types); |
3376 | 1 | let converted = match goal_inner.scalar_for_conversions(&ctx.module.types) { |
3377 | 1 | Some(goal_scalar) => { |
3378 | 1 | let arg_span = ctx.get_expression_span(*argument); |
3379 | 1 | ctx.try_automatic_conversion_for_leaf_scalar(*argument, goal_scalar, arg_span)? |
3380 | | } |
3381 | | // No conversion is necessary. |
3382 | 0 | None => *argument, |
3383 | | }; |
3384 | | |
3385 | 1 | *argument = converted; |
3386 | | } |
3387 | | |
3388 | 1 | Ok(()) |
3389 | 1 | } |
3390 | | |
3391 | 0 | fn atomic_pointer( |
3392 | 0 | &mut self, |
3393 | 0 | expr: Handle<ast::Expression<'source>>, |
3394 | 0 | ctx: &mut ExpressionContext<'source, '_, '_>, |
3395 | 0 | ) -> Result<'source, (Handle<ir::Expression>, ir::Scalar)> { |
3396 | 0 | let span = ctx.ast_expressions.get_span(expr); |
3397 | 0 | let pointer = self.expression(expr, ctx)?; |
3398 | | |
3399 | 0 | match *resolve_inner!(ctx, pointer) { |
3400 | 0 | ir::TypeInner::Pointer { base, .. } => match ctx.module.types[base].inner { |
3401 | 0 | ir::TypeInner::Atomic(scalar) => Ok((pointer, scalar)), |
3402 | 0 | ref other => { |
3403 | 0 | log::error!("Pointer type to {other:?} passed to atomic op"); |
3404 | 0 | Err(Box::new(Error::InvalidAtomicPointer(span))) |
3405 | | } |
3406 | | }, |
3407 | 0 | ref other => { |
3408 | 0 | log::error!("Type {other:?} passed to atomic op"); |
3409 | 0 | Err(Box::new(Error::InvalidAtomicPointer(span))) |
3410 | | } |
3411 | | } |
3412 | 0 | } |
3413 | | |
3414 | 0 | fn atomic_helper( |
3415 | 0 | &mut self, |
3416 | 0 | span: Span, |
3417 | 0 | fun: ir::AtomicFunction, |
3418 | 0 | args: &[Handle<ast::Expression<'source>>], |
3419 | 0 | is_statement: bool, |
3420 | 0 | ctx: &mut ExpressionContext<'source, '_, '_>, |
3421 | 0 | ) -> Result<'source, Option<Handle<ir::Expression>>> { |
3422 | 0 | let mut args = ctx.prepare_args(args, 2, span); |
3423 | | |
3424 | 0 | let (pointer, scalar) = self.atomic_pointer(args.next()?, ctx)?; |
3425 | 0 | let value = self.expression_with_leaf_scalar(args.next()?, scalar, ctx)?; |
3426 | 0 | let value_inner = resolve_inner!(ctx, value); |
3427 | 0 | args.finish()?; |
3428 | | |
3429 | | // If we don't use the return value of a 64-bit `min` or `max` |
3430 | | // operation, generate a no-result form of the `Atomic` statement, so |
3431 | | // that we can pass validation with only `SHADER_INT64_ATOMIC_MIN_MAX` |
3432 | | // whenever possible. |
3433 | 0 | let is_64_bit_min_max = matches!(fun, ir::AtomicFunction::Min | ir::AtomicFunction::Max) |
3434 | 0 | && matches!( |
3435 | 0 | *value_inner, |
3436 | | ir::TypeInner::Scalar(ir::Scalar { width: 8, .. }) |
3437 | | ); |
3438 | 0 | let result = if is_64_bit_min_max && is_statement { |
3439 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
3440 | 0 | rctx.block |
3441 | 0 | .extend(rctx.emitter.finish(&rctx.function.expressions)); |
3442 | 0 | rctx.emitter.start(&rctx.function.expressions); |
3443 | 0 | None |
3444 | | } else { |
3445 | 0 | let ty = ctx.register_type(value)?; |
3446 | 0 | Some(ctx.interrupt_emitter( |
3447 | 0 | ir::Expression::AtomicResult { |
3448 | 0 | ty, |
3449 | 0 | comparison: false, |
3450 | 0 | }, |
3451 | 0 | span, |
3452 | 0 | )?) |
3453 | | }; |
3454 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
3455 | 0 | rctx.block.push( |
3456 | 0 | ir::Statement::Atomic { |
3457 | 0 | pointer, |
3458 | 0 | fun, |
3459 | 0 | value, |
3460 | 0 | result, |
3461 | 0 | }, |
3462 | 0 | span, |
3463 | | ); |
3464 | 0 | Ok(result) |
3465 | 0 | } |
3466 | | |
3467 | 0 | fn texture_sample_helper( |
3468 | 0 | &mut self, |
3469 | 0 | fun: Texture, |
3470 | 0 | args: &[Handle<ast::Expression<'source>>], |
3471 | 0 | span: Span, |
3472 | 0 | ctx: &mut ExpressionContext<'source, '_, '_>, |
3473 | 0 | ) -> Result<'source, ir::Expression> { |
3474 | 0 | let mut args = ctx.prepare_args(args, fun.min_argument_count(), span); |
3475 | | |
3476 | 0 | fn get_image_and_span<'source>( |
3477 | 0 | lowerer: &mut Lowerer<'source, '_>, |
3478 | 0 | args: &mut ArgumentContext<'_, 'source>, |
3479 | 0 | ctx: &mut ExpressionContext<'source, '_, '_>, |
3480 | 0 | ) -> Result<'source, (Handle<ir::Expression>, Span)> { |
3481 | 0 | let image = args.next()?; |
3482 | 0 | let image_span = ctx.ast_expressions.get_span(image); |
3483 | 0 | let image = lowerer.expression_for_abstract(image, ctx)?; |
3484 | 0 | Ok((image, image_span)) |
3485 | 0 | } |
3486 | | |
3487 | | let image; |
3488 | | let image_span; |
3489 | | let gather; |
3490 | 0 | match fun { |
3491 | | Texture::Gather => { |
3492 | 0 | let image_or_component = args.next()?; |
3493 | 0 | let image_or_component_span = ctx.ast_expressions.get_span(image_or_component); |
3494 | | // Gathers from depth textures don't take an initial `component` argument. |
3495 | 0 | let lowered_image_or_component = self.expression(image_or_component, ctx)?; |
3496 | | |
3497 | 0 | match *resolve_inner!(ctx, lowered_image_or_component) { |
3498 | | ir::TypeInner::Image { |
3499 | | class: ir::ImageClass::Depth { .. }, |
3500 | | .. |
3501 | 0 | } => { |
3502 | 0 | image = lowered_image_or_component; |
3503 | 0 | image_span = image_or_component_span; |
3504 | 0 | gather = Some(ir::SwizzleComponent::X); |
3505 | 0 | } |
3506 | | _ => { |
3507 | 0 | (image, image_span) = get_image_and_span(self, &mut args, ctx)?; |
3508 | 0 | gather = Some(ctx.gather_component( |
3509 | 0 | lowered_image_or_component, |
3510 | 0 | image_or_component_span, |
3511 | 0 | span, |
3512 | 0 | )?); |
3513 | | } |
3514 | | } |
3515 | | } |
3516 | | Texture::GatherCompare => { |
3517 | 0 | (image, image_span) = get_image_and_span(self, &mut args, ctx)?; |
3518 | 0 | gather = Some(ir::SwizzleComponent::X); |
3519 | | } |
3520 | | |
3521 | | _ => { |
3522 | 0 | (image, image_span) = get_image_and_span(self, &mut args, ctx)?; |
3523 | 0 | gather = None; |
3524 | | } |
3525 | | }; |
3526 | | |
3527 | 0 | let sampler = self.expression_for_abstract(args.next()?, ctx)?; |
3528 | | |
3529 | 0 | let coordinate = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?; |
3530 | 0 | let clamp_to_edge = matches!(fun, Texture::SampleBaseClampToEdge); |
3531 | | |
3532 | 0 | let (class, arrayed) = ctx.image_data(image, image_span)?; |
3533 | 0 | let array_index = arrayed |
3534 | 0 | .then(|| self.expression(args.next()?, ctx)) |
3535 | 0 | .transpose()?; |
3536 | | |
3537 | | let level; |
3538 | | let depth_ref; |
3539 | 0 | match fun { |
3540 | 0 | Texture::Gather => { |
3541 | 0 | level = ir::SampleLevel::Zero; |
3542 | 0 | depth_ref = None; |
3543 | 0 | } |
3544 | | Texture::GatherCompare => { |
3545 | 0 | let reference = |
3546 | 0 | self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?; |
3547 | 0 | level = ir::SampleLevel::Zero; |
3548 | 0 | depth_ref = Some(reference); |
3549 | | } |
3550 | | |
3551 | 0 | Texture::Sample => { |
3552 | 0 | level = ir::SampleLevel::Auto; |
3553 | 0 | depth_ref = None; |
3554 | 0 | } |
3555 | | Texture::SampleBias => { |
3556 | 0 | let bias = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?; |
3557 | 0 | level = ir::SampleLevel::Bias(bias); |
3558 | 0 | depth_ref = None; |
3559 | | } |
3560 | | Texture::SampleCompare => { |
3561 | 0 | let reference = |
3562 | 0 | self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?; |
3563 | 0 | level = ir::SampleLevel::Auto; |
3564 | 0 | depth_ref = Some(reference); |
3565 | | } |
3566 | | Texture::SampleCompareLevel => { |
3567 | 0 | let reference = |
3568 | 0 | self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?; |
3569 | 0 | level = ir::SampleLevel::Zero; |
3570 | 0 | depth_ref = Some(reference); |
3571 | | } |
3572 | | Texture::SampleGrad => { |
3573 | 0 | let x = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?; |
3574 | 0 | let y = self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)?; |
3575 | 0 | level = ir::SampleLevel::Gradient { x, y }; |
3576 | 0 | depth_ref = None; |
3577 | | } |
3578 | | Texture::SampleLevel => { |
3579 | 0 | let exact = match class { |
3580 | | // When applied to depth textures, `textureSampleLevel`'s |
3581 | | // `level` argument is an `i32` or `u32`. |
3582 | 0 | ir::ImageClass::Depth { .. } => self.expression(args.next()?, ctx)?, |
3583 | | |
3584 | | // When applied to other sampled types, its `level` argument |
3585 | | // is an `f32`. |
3586 | | ir::ImageClass::Sampled { .. } => { |
3587 | 0 | self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)? |
3588 | | } |
3589 | | |
3590 | | // Sampling `External` textures with a specified level isn't |
3591 | | // allowed, and sampling `Storage` textures isn't allowed at |
3592 | | // all. Let the validator report the error. |
3593 | | ir::ImageClass::Storage { .. } | ir::ImageClass::External => { |
3594 | 0 | self.expression(args.next()?, ctx)? |
3595 | | } |
3596 | | }; |
3597 | 0 | level = ir::SampleLevel::Exact(exact); |
3598 | 0 | depth_ref = None; |
3599 | | } |
3600 | 0 | Texture::SampleBaseClampToEdge => { |
3601 | 0 | level = crate::SampleLevel::Zero; |
3602 | 0 | depth_ref = None; |
3603 | 0 | } |
3604 | | }; |
3605 | | |
3606 | 0 | let offset = args |
3607 | 0 | .next() |
3608 | 0 | .map(|arg| self.expression_with_leaf_scalar(arg, ir::Scalar::I32, &mut ctx.as_const())) |
3609 | 0 | .ok() |
3610 | 0 | .transpose()?; |
3611 | | |
3612 | 0 | args.finish()?; |
3613 | | |
3614 | 0 | Ok(ir::Expression::ImageSample { |
3615 | 0 | image, |
3616 | 0 | sampler, |
3617 | 0 | gather, |
3618 | 0 | coordinate, |
3619 | 0 | array_index, |
3620 | 0 | offset, |
3621 | 0 | level, |
3622 | 0 | depth_ref, |
3623 | 0 | clamp_to_edge, |
3624 | 0 | }) |
3625 | 0 | } |
3626 | | |
3627 | 0 | fn subgroup_operation_helper( |
3628 | 0 | &mut self, |
3629 | 0 | span: Span, |
3630 | 0 | op: ir::SubgroupOperation, |
3631 | 0 | collective_op: ir::CollectiveOperation, |
3632 | 0 | arguments: &[Handle<ast::Expression<'source>>], |
3633 | 0 | ctx: &mut ExpressionContext<'source, '_, '_>, |
3634 | 0 | ) -> Result<'source, Handle<ir::Expression>> { |
3635 | 0 | let mut args = ctx.prepare_args(arguments, 1, span); |
3636 | | |
3637 | 0 | let argument = self.expression(args.next()?, ctx)?; |
3638 | 0 | args.finish()?; |
3639 | | |
3640 | 0 | let ty = ctx.register_type(argument)?; |
3641 | | |
3642 | 0 | let result = ctx.interrupt_emitter(ir::Expression::SubgroupOperationResult { ty }, span)?; |
3643 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
3644 | 0 | rctx.block.push( |
3645 | 0 | ir::Statement::SubgroupCollectiveOperation { |
3646 | 0 | op, |
3647 | 0 | collective_op, |
3648 | 0 | argument, |
3649 | 0 | result, |
3650 | 0 | }, |
3651 | 0 | span, |
3652 | | ); |
3653 | 0 | Ok(result) |
3654 | 0 | } |
3655 | | |
3656 | 0 | fn subgroup_gather_helper( |
3657 | 0 | &mut self, |
3658 | 0 | span: Span, |
3659 | 0 | mode: SubgroupGather, |
3660 | 0 | arguments: &[Handle<ast::Expression<'source>>], |
3661 | 0 | ctx: &mut ExpressionContext<'source, '_, '_>, |
3662 | 0 | ) -> Result<'source, Handle<ir::Expression>> { |
3663 | 0 | let mut args = ctx.prepare_args(arguments, 2, span); |
3664 | | |
3665 | 0 | let argument = self.expression(args.next()?, ctx)?; |
3666 | | |
3667 | | use SubgroupGather as Sg; |
3668 | 0 | let mode = if let Sg::BroadcastFirst = mode { |
3669 | 0 | ir::GatherMode::BroadcastFirst |
3670 | | } else { |
3671 | 0 | let index = self.expression(args.next()?, ctx)?; |
3672 | 0 | match mode { |
3673 | 0 | Sg::BroadcastFirst => unreachable!(), |
3674 | 0 | Sg::Broadcast => ir::GatherMode::Broadcast(index), |
3675 | 0 | Sg::Shuffle => ir::GatherMode::Shuffle(index), |
3676 | 0 | Sg::ShuffleDown => ir::GatherMode::ShuffleDown(index), |
3677 | 0 | Sg::ShuffleUp => ir::GatherMode::ShuffleUp(index), |
3678 | 0 | Sg::ShuffleXor => ir::GatherMode::ShuffleXor(index), |
3679 | 0 | Sg::QuadBroadcast => ir::GatherMode::QuadBroadcast(index), |
3680 | | } |
3681 | | }; |
3682 | | |
3683 | 0 | args.finish()?; |
3684 | | |
3685 | 0 | let ty = ctx.register_type(argument)?; |
3686 | | |
3687 | 0 | let result = ctx.interrupt_emitter(ir::Expression::SubgroupOperationResult { ty }, span)?; |
3688 | 0 | let rctx = ctx.runtime_expression_ctx(span)?; |
3689 | 0 | rctx.block.push( |
3690 | 0 | ir::Statement::SubgroupGather { |
3691 | 0 | mode, |
3692 | 0 | argument, |
3693 | 0 | result, |
3694 | 0 | }, |
3695 | 0 | span, |
3696 | | ); |
3697 | 0 | Ok(result) |
3698 | 0 | } |
3699 | | |
3700 | 29 | fn r#struct( |
3701 | 29 | &mut self, |
3702 | 29 | s: &ast::Struct<'source>, |
3703 | 29 | span: Span, |
3704 | 29 | ctx: &mut GlobalContext<'source, '_, '_>, |
3705 | 29 | ) -> Result<'source, Handle<ir::Type>> { |
3706 | 29 | let mut offset = 0; |
3707 | 29 | let mut struct_alignment = proc::Alignment::ONE; |
3708 | 29 | let mut members = Vec::with_capacity(s.members.len()); |
3709 | | |
3710 | 29 | let mut doc_comments: Vec<Option<Vec<String>>> = Vec::new(); |
3711 | | |
3712 | 29 | for member in s.members.iter() { |
3713 | 21 | let ty = self.resolve_ast_type(member.ty, &mut ctx.as_const())?; |
3714 | | |
3715 | 21 | ctx.layouter.update(ctx.module.to_ctx()).map_err(|err| { |
3716 | 0 | let LayoutErrorInner::TooLarge = err.inner else { |
3717 | 0 | unreachable!("unexpected layout error: {err:?}"); |
3718 | | }; |
3719 | | // Since anonymous types of struct members don't get a span, |
3720 | | // associate the error with the member. The layouter could have |
3721 | | // failed on any type that was pending layout, but if it wasn't |
3722 | | // the current struct member, it wasn't a struct member at all, |
3723 | | // because we resolve struct members one-by-one. |
3724 | 0 | if ty == err.ty { |
3725 | 0 | Box::new(Error::StructMemberTooLarge { |
3726 | 0 | member_name_span: member.name.span, |
3727 | 0 | }) |
3728 | | } else { |
3729 | | // Lots of type definitions don't get spans, so this error |
3730 | | // message may not be very useful. |
3731 | 0 | Box::new(Error::TypeTooLarge { |
3732 | 0 | span: ctx.module.types.get_span(err.ty), |
3733 | 0 | }) |
3734 | | } |
3735 | 0 | })?; |
3736 | | |
3737 | 21 | let member_min_size = ctx.layouter[ty].size; |
3738 | 21 | let member_min_alignment = ctx.layouter[ty].alignment; |
3739 | | |
3740 | 21 | let member_size = if let Some(size_expr) = member.size { |
3741 | 0 | let (size, span) = self.const_u32(size_expr, &mut ctx.as_const())?; |
3742 | 0 | if size < member_min_size { |
3743 | 0 | return Err(Box::new(Error::SizeAttributeTooLow(span, member_min_size))); |
3744 | | } else { |
3745 | 0 | size |
3746 | | } |
3747 | | } else { |
3748 | 21 | member_min_size |
3749 | | }; |
3750 | | |
3751 | 21 | let member_alignment = if let Some(align_expr) = member.align { |
3752 | 5 | let (align, span) = self.const_u32(align_expr, &mut ctx.as_const())?; |
3753 | 5 | if let Some(alignment) = proc::Alignment::new(align) { |
3754 | 5 | if alignment < member_min_alignment { |
3755 | 5 | return Err(Box::new(Error::AlignAttributeTooLow( |
3756 | 5 | span, |
3757 | 5 | member_min_alignment, |
3758 | 5 | ))); |
3759 | | } else { |
3760 | 0 | alignment |
3761 | | } |
3762 | | } else { |
3763 | 0 | return Err(Box::new(Error::NonPowerOfTwoAlignAttribute(span))); |
3764 | | } |
3765 | | } else { |
3766 | 16 | member_min_alignment |
3767 | | }; |
3768 | | |
3769 | 16 | let binding = self.binding(&member.binding, ty, ctx)?; |
3770 | | |
3771 | 16 | offset = member_alignment.round_up(offset); |
3772 | 16 | struct_alignment = struct_alignment.max(member_alignment); |
3773 | | |
3774 | 16 | if !member.doc_comments.is_empty() { |
3775 | 0 | doc_comments.push(Some( |
3776 | 0 | member.doc_comments.iter().map(|s| s.to_string()).collect(), |
3777 | | )); |
3778 | 16 | } |
3779 | 16 | members.push(ir::StructMember { |
3780 | 16 | name: Some(member.name.name.to_owned()), |
3781 | 16 | ty, |
3782 | 16 | binding, |
3783 | 16 | offset, |
3784 | 16 | }); |
3785 | | |
3786 | 16 | offset += member_size; |
3787 | 16 | if offset > crate::valid::MAX_TYPE_SIZE { |
3788 | 0 | return Err(Box::new(Error::TypeTooLarge { span })); |
3789 | 16 | } |
3790 | | } |
3791 | | |
3792 | 24 | let size = struct_alignment.round_up(offset); |
3793 | 24 | let inner = ir::TypeInner::Struct { |
3794 | 24 | members, |
3795 | 24 | span: size, |
3796 | 24 | }; |
3797 | | |
3798 | 24 | let handle = ctx.module.types.insert( |
3799 | 24 | ir::Type { |
3800 | 24 | name: Some(s.name.name.to_string()), |
3801 | 24 | inner, |
3802 | 24 | }, |
3803 | 24 | span, |
3804 | | ); |
3805 | 24 | for (i, c) in doc_comments.drain(..).enumerate() { |
3806 | 0 | if let Some(comment) = c { |
3807 | 0 | ctx.module |
3808 | 0 | .get_or_insert_default_doc_comments() |
3809 | 0 | .struct_members |
3810 | 0 | .insert((handle, i), comment); |
3811 | 0 | } |
3812 | | } |
3813 | 24 | Ok(handle) |
3814 | 29 | } |
3815 | | |
3816 | 5 | fn const_u32( |
3817 | 5 | &mut self, |
3818 | 5 | expr: Handle<ast::Expression<'source>>, |
3819 | 5 | ctx: &mut ExpressionContext<'source, '_, '_>, |
3820 | 5 | ) -> Result<'source, (u32, Span)> { |
3821 | 5 | let span = ctx.ast_expressions.get_span(expr); |
3822 | 5 | let expr = self.expression(expr, ctx)?; |
3823 | 5 | let value = ctx |
3824 | 5 | .module |
3825 | 5 | .to_ctx() |
3826 | 5 | .eval_expr_to_u32(expr) |
3827 | 5 | .map_err(|err| match err { |
3828 | 0 | proc::U32EvalError::NonConst => Error::ExpectedConstExprConcreteIntegerScalar(span), |
3829 | 0 | proc::U32EvalError::Negative => Error::ExpectedNonNegative(span), |
3830 | 0 | })?; |
3831 | 5 | Ok((value, span)) |
3832 | 5 | } |
3833 | | |
3834 | 0 | fn array_size( |
3835 | 0 | &mut self, |
3836 | 0 | size: ast::ArraySize<'source>, |
3837 | 0 | ctx: &mut ExpressionContext<'source, '_, '_>, |
3838 | 0 | ) -> Result<'source, ir::ArraySize> { |
3839 | 0 | Ok(match size { |
3840 | 0 | ast::ArraySize::Constant(expr) => { |
3841 | 0 | let span = ctx.ast_expressions.get_span(expr); |
3842 | 0 | let const_expr = self.expression(expr, &mut ctx.as_const()); |
3843 | 0 | match const_expr { |
3844 | 0 | Ok(value) => { |
3845 | 0 | let len = ctx.const_eval_expr_to_u32(value).map_err(|err| { |
3846 | 0 | Box::new(match err { |
3847 | | proc::U32EvalError::NonConst => { |
3848 | 0 | Error::ExpectedConstExprConcreteIntegerScalar(span) |
3849 | | } |
3850 | | proc::U32EvalError::Negative => { |
3851 | 0 | Error::ExpectedPositiveArrayLength(span) |
3852 | | } |
3853 | | }) |
3854 | 0 | })?; |
3855 | 0 | let size = |
3856 | 0 | NonZeroU32::new(len).ok_or(Error::ExpectedPositiveArrayLength(span))?; |
3857 | 0 | ir::ArraySize::Constant(size) |
3858 | | } |
3859 | 0 | Err(err) => { |
3860 | 0 | if let Error::ConstantEvaluatorError(ref ty, _) = *err { |
3861 | 0 | match **ty { |
3862 | | proc::ConstantEvaluatorError::OverrideExpr => { |
3863 | 0 | ir::ArraySize::Pending(self.array_size_override( |
3864 | 0 | expr, |
3865 | 0 | &mut ctx.as_global().as_override(), |
3866 | 0 | span, |
3867 | 0 | )?) |
3868 | | } |
3869 | | _ => { |
3870 | 0 | return Err(err); |
3871 | | } |
3872 | | } |
3873 | | } else { |
3874 | 0 | return Err(err); |
3875 | | } |
3876 | | } |
3877 | | } |
3878 | | } |
3879 | 0 | ast::ArraySize::Dynamic => ir::ArraySize::Dynamic, |
3880 | | }) |
3881 | 0 | } |
3882 | | |
3883 | 0 | fn array_size_override( |
3884 | 0 | &mut self, |
3885 | 0 | size_expr: Handle<ast::Expression<'source>>, |
3886 | 0 | ctx: &mut ExpressionContext<'source, '_, '_>, |
3887 | 0 | span: Span, |
3888 | 0 | ) -> Result<'source, Handle<ir::Override>> { |
3889 | 0 | let expr = self.expression(size_expr, ctx)?; |
3890 | 0 | match resolve_inner!(ctx, expr).scalar_kind().ok_or(0) { |
3891 | | Ok(ir::ScalarKind::Sint) | Ok(ir::ScalarKind::Uint) => Ok({ |
3892 | 0 | if let ir::Expression::Override(handle) = ctx.module.global_expressions[expr] { |
3893 | 0 | handle |
3894 | | } else { |
3895 | 0 | let ty = ctx.register_type(expr)?; |
3896 | 0 | ctx.module.overrides.append( |
3897 | 0 | ir::Override { |
3898 | 0 | name: None, |
3899 | 0 | id: None, |
3900 | 0 | ty, |
3901 | 0 | init: Some(expr), |
3902 | 0 | }, |
3903 | 0 | span, |
3904 | | ) |
3905 | | } |
3906 | | }), |
3907 | 0 | _ => Err(Box::new(Error::ExpectedConstExprConcreteIntegerScalar( |
3908 | 0 | span, |
3909 | 0 | ))), |
3910 | | } |
3911 | 0 | } |
3912 | | |
3913 | | /// Build the Naga equivalent of a named AST type. |
3914 | | /// |
3915 | | /// Return a Naga `Handle<Type>` representing the front-end type |
3916 | | /// `handle`, which should be named `name`, if given. |
3917 | | /// |
3918 | | /// If `handle` refers to a type cached in [`SpecialTypes`], |
3919 | | /// `name` may be ignored. |
3920 | | /// |
3921 | | /// [`SpecialTypes`]: ir::SpecialTypes |
3922 | 9.39k | fn resolve_named_ast_type( |
3923 | 9.39k | &mut self, |
3924 | 9.39k | handle: Handle<ast::Type<'source>>, |
3925 | 9.39k | name: Option<String>, |
3926 | 9.39k | ctx: &mut ExpressionContext<'source, '_, '_>, |
3927 | 9.39k | ) -> Result<'source, Handle<ir::Type>> { |
3928 | 9.39k | let inner = match ctx.types[handle] { |
3929 | 9.38k | ast::Type::Scalar(scalar) => scalar.to_inner_scalar(), |
3930 | 16 | ast::Type::Vector { size, ty, ty_span } => { |
3931 | 16 | let ty = self.resolve_ast_type(ty, ctx)?; |
3932 | 16 | let scalar = match ctx.module.types[ty].inner { |
3933 | 16 | ir::TypeInner::Scalar(sc) => sc, |
3934 | 0 | _ => return Err(Box::new(Error::UnknownScalarType(ty_span))), |
3935 | | }; |
3936 | 16 | ir::TypeInner::Vector { size, scalar } |
3937 | | } |
3938 | | ast::Type::Matrix { |
3939 | 0 | rows, |
3940 | 0 | columns, |
3941 | 0 | ty, |
3942 | 0 | ty_span, |
3943 | | } => { |
3944 | 0 | let ty = self.resolve_ast_type(ty, ctx)?; |
3945 | 0 | let scalar = match ctx.module.types[ty].inner { |
3946 | 0 | ir::TypeInner::Scalar(sc) => sc, |
3947 | 0 | _ => return Err(Box::new(Error::UnknownScalarType(ty_span))), |
3948 | | }; |
3949 | 0 | match scalar.kind { |
3950 | 0 | ir::ScalarKind::Float => ir::TypeInner::Matrix { |
3951 | 0 | columns, |
3952 | 0 | rows, |
3953 | 0 | scalar, |
3954 | 0 | }, |
3955 | 0 | _ => return Err(Box::new(Error::BadMatrixScalarKind(ty_span, scalar))), |
3956 | | } |
3957 | | } |
3958 | 0 | ast::Type::Atomic(scalar) => scalar.to_inner_atomic(), |
3959 | 0 | ast::Type::Pointer { base, space } => { |
3960 | 0 | let base = self.resolve_ast_type(base, ctx)?; |
3961 | 0 | ir::TypeInner::Pointer { base, space } |
3962 | | } |
3963 | 0 | ast::Type::Array { base, size } => { |
3964 | 0 | let base = self.resolve_ast_type(base, &mut ctx.as_const())?; |
3965 | 0 | let size = self.array_size(size, ctx)?; |
3966 | | |
3967 | | // Determine the size of the base type, if needed. |
3968 | 0 | ctx.layouter.update(ctx.module.to_ctx()).map_err(|err| { |
3969 | 0 | let LayoutErrorInner::TooLarge = err.inner else { |
3970 | 0 | unreachable!("unexpected layout error: {err:?}"); |
3971 | | }; |
3972 | | // Lots of type definitions don't get spans, so this error |
3973 | | // message may not be very useful. |
3974 | 0 | Box::new(Error::TypeTooLarge { |
3975 | 0 | span: ctx.module.types.get_span(err.ty), |
3976 | 0 | }) |
3977 | 0 | })?; |
3978 | 0 | let stride = ctx.layouter[base].to_stride(); |
3979 | | |
3980 | 0 | ir::TypeInner::Array { base, size, stride } |
3981 | | } |
3982 | | ast::Type::Image { |
3983 | 0 | dim, |
3984 | 0 | arrayed, |
3985 | 0 | class, |
3986 | | } => { |
3987 | 0 | if class == crate::ImageClass::External { |
3988 | 0 | // Other than the WGSL backend, every backend that supports |
3989 | 0 | // external textures does so by lowering them to a set of |
3990 | 0 | // ordinary textures and some parameters saying how to |
3991 | 0 | // sample from them. We don't know which backend will |
3992 | 0 | // consume the `Module` we're building, but in case it's not |
3993 | 0 | // WGSL, populate `SpecialTypes::external_texture_params` |
3994 | 0 | // and `SpecialTypes::external_texture_transfer_function` |
3995 | 0 | // with the types the backend will use for the parameter |
3996 | 0 | // buffer. |
3997 | 0 | // |
3998 | 0 | // Neither of these are the type we are lowering here: |
3999 | 0 | // that's an ordinary `TypeInner::Image`. But the fact we |
4000 | 0 | // are lowering a `texture_external` implies the backends |
4001 | 0 | // may need these additional types too. |
4002 | 0 | ctx.module.generate_external_texture_types(); |
4003 | 0 | } |
4004 | 0 | ir::TypeInner::Image { |
4005 | 0 | dim, |
4006 | 0 | arrayed, |
4007 | 0 | class, |
4008 | 0 | } |
4009 | | } |
4010 | 0 | ast::Type::Sampler { comparison } => ir::TypeInner::Sampler { comparison }, |
4011 | 0 | ast::Type::AccelerationStructure { vertex_return } => { |
4012 | 0 | ir::TypeInner::AccelerationStructure { vertex_return } |
4013 | | } |
4014 | 0 | ast::Type::RayQuery { vertex_return } => ir::TypeInner::RayQuery { vertex_return }, |
4015 | 0 | ast::Type::BindingArray { base, size } => { |
4016 | 0 | let base = self.resolve_ast_type(base, ctx)?; |
4017 | 0 | let size = self.array_size(size, ctx)?; |
4018 | 0 | ir::TypeInner::BindingArray { base, size } |
4019 | | } |
4020 | | ast::Type::RayDesc => { |
4021 | 0 | return Ok(ctx.module.generate_ray_desc_type()); |
4022 | | } |
4023 | | ast::Type::RayIntersection => { |
4024 | 0 | return Ok(ctx.module.generate_ray_intersection_type()); |
4025 | | } |
4026 | 0 | ast::Type::User(ref ident) => { |
4027 | 0 | return match ctx.globals.get(ident.name) { |
4028 | 0 | Some(&LoweredGlobalDecl::Type(handle)) => Ok(handle), |
4029 | 0 | Some(_) => Err(Box::new(Error::Unexpected(ident.span, ExpectedToken::Type))), |
4030 | 0 | None => Err(Box::new(Error::UnknownType(ident.span))), |
4031 | | } |
4032 | | } |
4033 | | }; |
4034 | | |
4035 | 9.39k | Ok(ctx.as_global().ensure_type_exists(name, inner)) |
4036 | 9.39k | } |
4037 | | |
4038 | | /// Return a Naga `Handle<Type>` representing the front-end type `handle`. |
4039 | 9.39k | fn resolve_ast_type( |
4040 | 9.39k | &mut self, |
4041 | 9.39k | handle: Handle<ast::Type<'source>>, |
4042 | 9.39k | ctx: &mut ExpressionContext<'source, '_, '_>, |
4043 | 9.39k | ) -> Result<'source, Handle<ir::Type>> { |
4044 | 9.39k | self.resolve_named_ast_type(handle, None, ctx) |
4045 | 9.39k | } |
4046 | | |
4047 | 16 | fn binding( |
4048 | 16 | &mut self, |
4049 | 16 | binding: &Option<ast::Binding<'source>>, |
4050 | 16 | ty: Handle<ir::Type>, |
4051 | 16 | ctx: &mut GlobalContext<'source, '_, '_>, |
4052 | 16 | ) -> Result<'source, Option<ir::Binding>> { |
4053 | 0 | Ok(match *binding { |
4054 | 0 | Some(ast::Binding::BuiltIn(b)) => Some(ir::Binding::BuiltIn(b)), |
4055 | | Some(ast::Binding::Location { |
4056 | 0 | location, |
4057 | 0 | interpolation, |
4058 | 0 | sampling, |
4059 | 0 | blend_src, |
4060 | | }) => { |
4061 | 0 | let blend_src = if let Some(blend_src) = blend_src { |
4062 | 0 | Some(self.const_u32(blend_src, &mut ctx.as_const())?.0) |
4063 | | } else { |
4064 | 0 | None |
4065 | | }; |
4066 | | |
4067 | 0 | let mut binding = ir::Binding::Location { |
4068 | 0 | location: self.const_u32(location, &mut ctx.as_const())?.0, |
4069 | 0 | interpolation, |
4070 | 0 | sampling, |
4071 | 0 | blend_src, |
4072 | | }; |
4073 | 0 | binding.apply_default_interpolation(&ctx.module.types[ty].inner); |
4074 | 0 | Some(binding) |
4075 | | } |
4076 | 16 | None => None, |
4077 | | }) |
4078 | 16 | } |
4079 | | |
4080 | 0 | fn ray_query_pointer( |
4081 | 0 | &mut self, |
4082 | 0 | expr: Handle<ast::Expression<'source>>, |
4083 | 0 | ctx: &mut ExpressionContext<'source, '_, '_>, |
4084 | 0 | ) -> Result<'source, Handle<ir::Expression>> { |
4085 | 0 | let span = ctx.ast_expressions.get_span(expr); |
4086 | 0 | let pointer = self.expression(expr, ctx)?; |
4087 | | |
4088 | 0 | match *resolve_inner!(ctx, pointer) { |
4089 | 0 | ir::TypeInner::Pointer { base, .. } => match ctx.module.types[base].inner { |
4090 | 0 | ir::TypeInner::RayQuery { .. } => Ok(pointer), |
4091 | 0 | ref other => { |
4092 | 0 | log::error!("Pointer type to {other:?} passed to ray query op"); |
4093 | 0 | Err(Box::new(Error::InvalidRayQueryPointer(span))) |
4094 | | } |
4095 | | }, |
4096 | 0 | ref other => { |
4097 | 0 | log::error!("Type {other:?} passed to ray query op"); |
4098 | 0 | Err(Box::new(Error::InvalidRayQueryPointer(span))) |
4099 | | } |
4100 | | } |
4101 | 0 | } |
4102 | | } |
4103 | | |
4104 | | impl ir::AtomicFunction { |
4105 | 3 | pub fn map(word: &str) -> Option<Self> { |
4106 | 3 | Some(match word { |
4107 | 3 | "atomicAdd" => ir::AtomicFunction::Add, |
4108 | 3 | "atomicSub" => ir::AtomicFunction::Subtract, |
4109 | 3 | "atomicAnd" => ir::AtomicFunction::And, |
4110 | 3 | "atomicOr" => ir::AtomicFunction::InclusiveOr, |
4111 | 3 | "atomicXor" => ir::AtomicFunction::ExclusiveOr, |
4112 | 3 | "atomicMin" => ir::AtomicFunction::Min, |
4113 | 3 | "atomicMax" => ir::AtomicFunction::Max, |
4114 | 3 | "atomicExchange" => ir::AtomicFunction::Exchange { compare: None }, |
4115 | 3 | _ => return None, |
4116 | | }) |
4117 | 3 | } |
4118 | | } |