1use alloc::{format, vec, vec::Vec};
2
3use super::{Error, Instruction, LookupExpression, LookupHelper as _};
4use crate::proc::Emitter;
5use crate::{
6 arena::{Arena, Handle},
7 front::spv::{BlockContext, BodyIndex},
8};
9
10pub type BlockId = u32;
11
12impl<I: Iterator<Item = u32>> super::Frontend<I> {
13 pub(super) fn add_call(
16 &mut self,
17 from: spirv::Word,
18 to: spirv::Word,
19 ) -> Handle<crate::Function> {
20 let dummy_handle = self
21 .dummy_functions
22 .append(crate::Function::default(), Default::default());
23 self.deferred_function_calls.push(to);
24 self.function_call_graph.add_edge(from, to, ());
25 dummy_handle
26 }
27
28 pub(super) fn parse_function(&mut self, module: &mut crate::Module) -> Result<(), Error> {
29 let start = self.data_offset;
30 self.lookup_expression.clear();
31 self.lookup_load_override.clear();
32 self.lookup_sampled_image.clear();
33
34 let result_type_id = self.next()?;
35 let fun_id = self.next()?;
36 let _fun_control = self.next()?;
37 let fun_type_id = self.next()?;
38
39 let mut fun = {
40 let ft = self.lookup_function_type.lookup(fun_type_id)?;
41 if ft.return_type_id != result_type_id {
42 return Err(Error::WrongFunctionResultType(result_type_id));
43 }
44 crate::Function {
45 name: self.future_decor.remove(&fun_id).and_then(|dec| dec.name),
46 arguments: Vec::with_capacity(ft.parameter_type_ids.len()),
47 result: if self.lookup_void_type == Some(result_type_id) {
48 None
49 } else {
50 let lookup_result_ty = self.lookup_type.lookup(result_type_id)?;
51 Some(crate::FunctionResult {
52 ty: lookup_result_ty.handle,
53 binding: None,
54 })
55 },
56 local_variables: Arena::new(),
57 expressions: self.make_expression_storage(
58 &module.global_variables,
59 &module.constants,
60 &module.overrides,
61 ),
62 named_expressions: crate::NamedExpressions::default(),
63 body: crate::Block::new(),
64 diagnostic_filter_leaf: None,
65 }
66 };
67
68 for i in 0..fun.arguments.capacity() {
70 let start = self.data_offset;
71 match self.next_inst()? {
72 Instruction {
73 op: spirv::Op::FunctionParameter,
74 wc: 3,
75 } => {
76 let type_id = self.next()?;
77 let id = self.next()?;
78 let handle = fun.expressions.append(
79 crate::Expression::FunctionArgument(i as u32),
80 self.span_from(start),
81 );
82 self.lookup_expression.insert(
83 id,
84 LookupExpression {
85 handle,
86 type_id,
87 block_id: 0,
91 },
92 );
93 if type_id
96 != self
97 .lookup_function_type
98 .lookup(fun_type_id)?
99 .parameter_type_ids[i]
100 {
101 return Err(Error::WrongFunctionArgumentType(type_id));
102 }
103 let ty = self.lookup_type.lookup(type_id)?.handle;
104 let decor = self.future_decor.remove(&id).unwrap_or_default();
105 fun.arguments.push(crate::FunctionArgument {
106 name: decor.name,
107 ty,
108 binding: None,
109 });
110 }
111 Instruction { op, .. } => return Err(Error::InvalidParameter(op)),
112 }
113 }
114
115 let function_index = module.functions.len();
117
118 self.function_call_graph.add_node(fun_id);
120 let mut parameters_sampling =
121 vec![super::image::SamplingFlags::empty(); fun.arguments.len()];
122
123 let mut block_ctx = BlockContext {
124 phis: Default::default(),
125 blocks: Default::default(),
126 body_for_label: Default::default(),
127 mergers: Default::default(),
128 bodies: Default::default(),
129 module,
130 function_id: fun_id,
131 expressions: &mut fun.expressions,
132 local_arena: &mut fun.local_variables,
133 arguments: &fun.arguments,
134 parameter_sampling: &mut parameters_sampling,
135 };
136 block_ctx.bodies.push(super::Body::with_parent(0));
138
139 loop {
141 let fun_inst = self.next_inst()?;
142 log::debug!("{:?}", fun_inst.op);
143 match fun_inst.op {
144 spirv::Op::Line => {
145 fun_inst.expect(4)?;
146 let _file_id = self.next()?;
147 let _row_id = self.next()?;
148 let _col_id = self.next()?;
149 }
150 spirv::Op::Label => {
151 fun_inst.expect(2)?;
153 let block_id = self.next()?;
154
155 self.next_block(block_id, &mut block_ctx)?;
156 }
157 spirv::Op::FunctionEnd => {
158 fun_inst.expect(1)?;
159 break;
160 }
161 spirv::Op::ExtInst => {
162 let _ = self.next()?;
163 let _ = self.next()?;
164 let set_id = self.next()?;
165 if Some(set_id) == self.ext_non_semantic_id {
166 for _ in 0..fun_inst.wc - 4 {
167 self.next()?;
168 }
169 } else {
170 return Err(Error::UnsupportedInstruction(self.state, fun_inst.op));
171 }
172 }
173 _ => {
174 return Err(Error::UnsupportedInstruction(self.state, fun_inst.op));
175 }
176 }
177 }
178
179 if let Some(ref prefix) = self.options.block_ctx_dump_prefix {
180 let dump_suffix = match self.lookup_entry_point.get(&fun_id) {
181 Some(ep) => format!("block_ctx.{:?}-{}.txt", ep.stage, ep.name),
182 None => format!("block_ctx.Fun-{function_index}.txt"),
183 };
184
185 cfg_if::cfg_if! {
186 if #[cfg(feature = "fs")] {
187 let prefix: &std::path::Path = prefix.as_ref();
188 let dest = prefix.join(dump_suffix);
189 let dump = format!("{block_ctx:#?}");
190 if let Err(e) = std::fs::write(&dest, dump) {
191 log::error!("Unable to dump the block context into {dest:?}: {e}");
192 }
193 } else {
194 log::error!("Unable to dump the block context into {prefix:?}/{dump_suffix}: file system integration was not enabled with the `fs` feature");
195 }
196 }
197 }
198
199 for phi in block_ctx.phis.iter() {
205 let phi_pointer: Handle<crate::Expression> = block_ctx.expressions.append(
207 crate::Expression::LocalVariable(phi.local),
208 crate::Span::default(),
209 );
210
211 for &(source, predecessor) in phi.expressions.iter() {
214 let source_lexp = &self.lookup_expression[&source];
215 let predecessor_body_idx = block_ctx.body_for_label[&predecessor];
216 let source_body_idx = block_ctx
219 .body_for_label
220 .get(&source_lexp.block_id)
221 .copied()
222 .unwrap_or(0);
223
224 let value = if super::is_parent(predecessor_body_idx, source_body_idx, &block_ctx) {
233 source_lexp.handle
234 } else {
235 let ty = self.lookup_type[&source_lexp.type_id].handle;
245 let local = block_ctx.local_arena.append(
246 crate::LocalVariable {
247 name: None,
248 ty,
249 init: None,
250 },
251 crate::Span::default(),
252 );
253
254 let pointer = block_ctx.expressions.append(
255 crate::Expression::LocalVariable(local),
256 crate::Span::default(),
257 );
258
259 let start = block_ctx.expressions.len();
261 let expr = block_ctx
262 .expressions
263 .append(crate::Expression::Load { pointer }, crate::Span::default());
264 let range = block_ctx.expressions.range_from(start);
265
266 block_ctx
267 .blocks
268 .get_mut(&predecessor)
269 .unwrap()
270 .push(crate::Statement::Emit(range), crate::Span::default());
271
272 block_ctx
275 .blocks
276 .get_mut(&source_lexp.block_id)
277 .unwrap()
278 .push(
279 crate::Statement::Store {
280 pointer,
281 value: source_lexp.handle,
282 },
283 crate::Span::default(),
284 );
285
286 expr
287 };
288
289 block_ctx.blocks.get_mut(&predecessor).unwrap().push(
292 crate::Statement::Store {
293 pointer: phi_pointer,
294 value,
295 },
296 crate::Span::default(),
297 )
298 }
299 }
300
301 fun.body = block_ctx.lower();
302
303 let fun_handle = module.functions.append(fun, self.span_from_with_op(start));
305 self.lookup_function.insert(
306 fun_id,
307 super::LookupFunction {
308 handle: fun_handle,
309 parameters_sampling,
310 },
311 );
312
313 if let Some(ep) = self.lookup_entry_point.remove(&fun_id) {
314 self.deferred_entry_points.push((ep, fun_id));
315 }
316
317 Ok(())
318 }
319
320 pub(super) fn process_entry_point(
321 &mut self,
322 module: &mut crate::Module,
323 ep: super::EntryPoint,
324 fun_id: u32,
325 ) -> Result<(), Error> {
326 let mut function = crate::Function {
328 name: Some(format!("{}_wrap", ep.name)),
329 arguments: Vec::new(),
330 result: None,
331 local_variables: Arena::new(),
332 expressions: Arena::new(),
333 named_expressions: crate::NamedExpressions::default(),
334 body: crate::Block::new(),
335 diagnostic_filter_leaf: None,
336 };
337
338 for &v_id in ep.variable_ids.iter() {
340 let lvar = self.lookup_variable.lookup(v_id)?;
341 if let super::Variable::Input(ref arg) = lvar.inner {
342 let span = module.global_variables.get_span(lvar.handle);
343 let arg_expr = function.expressions.append(
344 crate::Expression::FunctionArgument(function.arguments.len() as u32),
345 span,
346 );
347 let load_expr = if arg.ty == module.global_variables[lvar.handle].ty {
348 arg_expr
349 } else {
350 let mut emitter = Emitter::default();
353 emitter.start(&function.expressions);
354 let handle = function.expressions.append(
355 crate::Expression::As {
356 expr: arg_expr,
357 target: crate::Scalar::I32,
358 },
359 span,
360 );
361 function.body.extend(emitter.finish(&function.expressions));
362 handle
363 };
364 function.body.push(
365 crate::Statement::Store {
366 pointer: function
367 .expressions
368 .append(crate::Expression::GlobalVariable(lvar.handle), span),
369 value: load_expr,
370 },
371 span,
372 );
373
374 let mut arg = arg.clone();
375 if ep.stage == crate::ShaderStage::Fragment {
376 if let Some(ref mut binding) = arg.binding {
377 binding.apply_default_interpolation(&module.types[arg.ty].inner);
378 }
379 }
380 function.arguments.push(arg);
381 }
382 }
383 let fake_id = !(module.entry_points.len() as u32); let dummy_handle = self.add_call(fake_id, fun_id);
386 function.body.push(
387 crate::Statement::Call {
388 function: dummy_handle,
389 arguments: Vec::new(),
390 result: None,
391 },
392 crate::Span::default(),
393 );
394
395 let mut members = Vec::new();
402 self.layouter.update(module.to_ctx()).unwrap();
403 let mut next_member_offset = 0;
404 let mut struct_alignment = crate::proc::Alignment::ONE;
405 let mut components = Vec::new();
406 for &v_id in ep.variable_ids.iter() {
407 let lvar = self.lookup_variable.lookup(v_id)?;
408 if let super::Variable::Output(ref result) = lvar.inner {
409 let span = module.global_variables.get_span(lvar.handle);
410 let expr_handle = function
411 .expressions
412 .append(crate::Expression::GlobalVariable(lvar.handle), span);
413
414 {
417 let ty = &module.types[result.ty];
418 if let crate::TypeInner::Struct {
419 members: ref original_members,
420 span,
421 } = ty.inner
422 {
423 let mut new_members = None;
424 for (idx, member) in original_members.iter().enumerate() {
425 if let Some(crate::Binding::BuiltIn(built_in)) = member.binding {
426 if !self.gl_per_vertex_builtin_access.contains(&built_in) {
427 new_members.get_or_insert_with(|| original_members.clone())
428 [idx]
429 .binding = None;
430 }
431 }
432 }
433 if let Some(new_members) = new_members {
434 module.types.replace(
435 result.ty,
436 crate::Type {
437 name: ty.name.clone(),
438 inner: crate::TypeInner::Struct {
439 members: new_members,
440 span,
441 },
442 },
443 );
444 }
445 }
446 }
447
448 match module.types[result.ty].inner {
449 crate::TypeInner::Struct {
450 members: ref sub_members,
451 ..
452 } => {
453 for (index, sm) in sub_members.iter().enumerate() {
454 if sm.binding.is_none() {
455 continue;
456 }
457 let mut sm = sm.clone();
458
459 if let Some(ref mut binding) = sm.binding {
460 if ep.stage == crate::ShaderStage::Vertex {
461 binding.apply_default_interpolation(&module.types[sm.ty].inner);
462 }
463 }
464
465 let member_alignment = self.layouter[sm.ty].alignment;
466 next_member_offset = member_alignment.round_up(next_member_offset);
467 sm.offset = next_member_offset;
468 struct_alignment = struct_alignment.max(member_alignment);
469 next_member_offset += self.layouter[sm.ty].size;
470 members.push(sm);
471
472 components.push(function.expressions.append(
473 crate::Expression::AccessIndex {
474 base: expr_handle,
475 index: index as u32,
476 },
477 span,
478 ));
479 }
480 }
481 ref inner => {
482 let mut binding = result.binding.clone();
483 if let Some(ref mut binding) = binding {
484 if ep.stage == crate::ShaderStage::Vertex {
485 binding.apply_default_interpolation(inner);
486 }
487 }
488
489 let member_alignment = self.layouter[result.ty].alignment;
490 next_member_offset = member_alignment.round_up(next_member_offset);
491 members.push(crate::StructMember {
492 name: None,
493 ty: result.ty,
494 binding,
495 offset: next_member_offset,
496 });
497 struct_alignment = struct_alignment.max(member_alignment);
498 next_member_offset += self.layouter[result.ty].size;
499 components.push(expr_handle);
502 }
503 }
504 }
505 }
506
507 for (member_index, member) in members.iter().enumerate() {
508 match member.binding {
509 Some(crate::Binding::BuiltIn(crate::BuiltIn::Position { .. }))
510 if self.options.adjust_coordinate_space =>
511 {
512 let mut emitter = Emitter::default();
513 emitter.start(&function.expressions);
514 let global_expr = components[member_index];
515 let span = function.expressions.get_span(global_expr);
516 let access_expr = function.expressions.append(
517 crate::Expression::AccessIndex {
518 base: global_expr,
519 index: 1,
520 },
521 span,
522 );
523 let load_expr = function.expressions.append(
524 crate::Expression::Load {
525 pointer: access_expr,
526 },
527 span,
528 );
529 let neg_expr = function.expressions.append(
530 crate::Expression::Unary {
531 op: crate::UnaryOperator::Negate,
532 expr: load_expr,
533 },
534 span,
535 );
536 function.body.extend(emitter.finish(&function.expressions));
537 function.body.push(
538 crate::Statement::Store {
539 pointer: access_expr,
540 value: neg_expr,
541 },
542 span,
543 );
544 }
545 _ => {}
546 }
547 }
548
549 let mut emitter = Emitter::default();
550 emitter.start(&function.expressions);
551 for component in components.iter_mut() {
552 let load_expr = crate::Expression::Load {
553 pointer: *component,
554 };
555 let span = function.expressions.get_span(*component);
556 *component = function.expressions.append(load_expr, span);
557 }
558
559 match members[..] {
560 [] => {}
561 [ref member] => {
562 function.body.extend(emitter.finish(&function.expressions));
563 let span = function.expressions.get_span(components[0]);
564 function.body.push(
565 crate::Statement::Return {
566 value: components.first().cloned(),
567 },
568 span,
569 );
570 function.result = Some(crate::FunctionResult {
571 ty: member.ty,
572 binding: member.binding.clone(),
573 });
574 }
575 _ => {
576 let span = crate::Span::total_span(
577 components.iter().map(|h| function.expressions.get_span(*h)),
578 );
579 let ty = module.types.insert(
580 crate::Type {
581 name: None,
582 inner: crate::TypeInner::Struct {
583 members,
584 span: struct_alignment.round_up(next_member_offset),
585 },
586 },
587 span,
588 );
589 let result_expr = function
590 .expressions
591 .append(crate::Expression::Compose { ty, components }, span);
592 function.body.extend(emitter.finish(&function.expressions));
593 function.body.push(
594 crate::Statement::Return {
595 value: Some(result_expr),
596 },
597 span,
598 );
599 function.result = Some(crate::FunctionResult { ty, binding: None });
600 }
601 }
602
603 module.entry_points.push(crate::EntryPoint {
604 name: ep.name,
605 stage: ep.stage,
606 early_depth_test: ep.early_depth_test,
607 workgroup_size: ep.workgroup_size,
608 workgroup_size_overrides: None,
609 function,
610 mesh_info: None,
611 task_payload: None,
612 incoming_ray_payload: None,
613 });
614
615 Ok(())
616 }
617}
618
619impl BlockContext<'_> {
620 pub(super) const fn gctx(&self) -> crate::proc::GlobalCtx<'_> {
621 crate::proc::GlobalCtx {
622 types: &self.module.types,
623 constants: &self.module.constants,
624 overrides: &self.module.overrides,
625 global_expressions: &self.module.global_expressions,
626 }
627 }
628
629 fn lower(mut self) -> crate::Block {
631 fn lower_impl(
632 blocks: &mut crate::FastHashMap<spirv::Word, crate::Block>,
633 bodies: &[super::Body],
634 body_idx: BodyIndex,
635 ) -> crate::Block {
636 let mut block = crate::Block::new();
637
638 for item in bodies[body_idx].data.iter() {
639 match *item {
640 super::BodyFragment::BlockId(id) => block.append(blocks.get_mut(&id).unwrap()),
641 super::BodyFragment::If {
642 condition,
643 accept,
644 reject,
645 } => {
646 let accept = lower_impl(blocks, bodies, accept);
647 let reject = lower_impl(blocks, bodies, reject);
648
649 block.push(
650 crate::Statement::If {
651 condition,
652 accept,
653 reject,
654 },
655 crate::Span::default(),
656 )
657 }
658 super::BodyFragment::Loop {
659 body,
660 continuing,
661 break_if,
662 } => {
663 let body = lower_impl(blocks, bodies, body);
664 let continuing = lower_impl(blocks, bodies, continuing);
665
666 block.push(
667 crate::Statement::Loop {
668 body,
669 continuing,
670 break_if,
671 },
672 crate::Span::default(),
673 )
674 }
675 super::BodyFragment::Switch {
676 selector,
677 ref cases,
678 default,
679 } => {
680 let mut ir_cases: Vec<_> = cases
681 .iter()
682 .map(|&(value, body_idx)| {
683 let body = lower_impl(blocks, bodies, body_idx);
684
685 let fall_through = body.last().is_none_or(|s| !s.is_terminator());
687
688 crate::SwitchCase {
689 value: crate::SwitchValue::I32(value),
690 body,
691 fall_through,
692 }
693 })
694 .collect();
695 ir_cases.push(crate::SwitchCase {
696 value: crate::SwitchValue::Default,
697 body: lower_impl(blocks, bodies, default),
698 fall_through: false,
699 });
700
701 block.push(
702 crate::Statement::Switch {
703 selector,
704 cases: ir_cases,
705 },
706 crate::Span::default(),
707 )
708 }
709 super::BodyFragment::Break => {
710 block.push(crate::Statement::Break, crate::Span::default())
711 }
712 super::BodyFragment::Continue => {
713 block.push(crate::Statement::Continue, crate::Span::default())
714 }
715 }
716 }
717
718 block
719 }
720
721 lower_impl(&mut self.blocks, &self.bodies, 0)
722 }
723}