1#![allow(rustdoc::private_intra_doc_links)]
15#![allow(unstable_name_collisions)]
20use itertools::Itertools as _;
21
22use crate::func_at::FuncAt;
23use crate::print::multiversion::Versions;
24use crate::qptr::{self, QPtrAttr, QPtrMemUsage, QPtrMemUsageKind, QPtrOp, QPtrUsage};
25use crate::visit::{InnerVisit, Visit, Visitor};
26use crate::{
27 AddrSpace, Attr, AttrSet, AttrSetDef, Const, ConstDef, ConstKind, Context, ControlNode,
28 ControlNodeDef, ControlNodeKind, ControlNodeOutputDecl, ControlRegion, ControlRegionDef,
29 ControlRegionInputDecl, DataInst, DataInstDef, DataInstForm, DataInstFormDef, DataInstKind,
30 DeclDef, Diag, DiagLevel, DiagMsgPart, EntityListIter, ExportKey, Exportee, Func, FuncDecl,
31 FuncParam, FxIndexMap, FxIndexSet, GlobalVar, GlobalVarDecl, GlobalVarDefBody, Import, Module,
32 ModuleDebugInfo, ModuleDialect, OrdAssertEq, SelectionKind, Type, TypeDef, TypeKind,
33 TypeOrConst, Value, cfg, spv,
34};
35use arrayvec::ArrayVec;
36use itertools::Either;
37use rustc_hash::FxHashMap;
38use smallvec::SmallVec;
39use std::borrow::Cow;
40use std::collections::hash_map::Entry;
41use std::fmt::{self, Write as _};
42use std::hash::Hash;
43use std::mem;
44
45mod multiversion;
46mod pretty;
47
48pub struct Plan<'a> {
62 cx: &'a Context,
63
64 current_module: Option<&'a Module>,
68
69 versions: Vec<PlanVersion<'a>>,
77
78 use_counts: FxIndexMap<Use, usize>,
84
85 attrs_to_unique_spv_name: FxHashMap<AttrSet, Result<&'a spv::Inst, AmbiguousName>>,
101
102 claimed_spv_names: FxHashMap<&'a spv::Inst, Result<AttrSet, AmbiguousName>>,
107}
108
109struct PlanVersion<'a> {
112 name: String,
115
116 node_defs: FxHashMap<Node, NodeDef<'a>>,
119
120 root: &'a dyn Print<Output = pretty::Fragment>,
124}
125
126#[derive(Copy, Clone)]
128struct AmbiguousName;
129
130#[derive(Copy, Clone, PartialEq, Eq, Hash)]
132enum Node {
133 AllCxInterned,
135
136 ModuleDialect,
139 ModuleDebugInfo,
140
141 GlobalVar(GlobalVar),
142 Func(Func),
143}
144
145impl Node {
146 fn keyword_and_name_prefix(self) -> Result<(&'static str, &'static str), &'static str> {
147 match self {
148 Self::AllCxInterned => Err("Node::AllCxInterned"),
149
150 Self::ModuleDialect => Ok(("module.dialect", "")),
153 Self::ModuleDebugInfo => Ok(("module.debug_info", "")),
154
155 Self::GlobalVar(_) => Ok(("global_var", "GV")),
156 Self::Func(_) => Ok(("func", "F")),
157 }
158 }
159}
160
161#[derive(Copy, Clone, derive_more::From)]
166enum NodeDef<'a> {
167 ModuleDialect(&'a ModuleDialect),
168 ModuleDebugInfo(&'a ModuleDebugInfo),
169 GlobalVar(&'a GlobalVarDecl),
170 Func(&'a FuncDecl),
171}
172
173#[derive(Copy, Clone, PartialEq, Eq, Hash)]
176enum CxInterned {
177 Type(Type),
178 Const(Const),
179}
180
181impl CxInterned {
182 fn keyword_and_name_prefix(self) -> (&'static str, &'static str) {
183 match self {
184 Self::Type(_) => ("type", "T"),
185 Self::Const(_) => ("const", "C"),
186 }
187 }
188}
189
190#[derive(Copy, Clone, PartialEq, Eq, Hash)]
191enum Use {
192 Node(Node),
193
194 CxInterned(CxInterned),
195
196 ControlRegionLabel(ControlRegion),
197
198 ControlRegionInput { region: ControlRegion, input_idx: u32 },
201 ControlNodeOutput { control_node: ControlNode, output_idx: u32 },
202 DataInstOutput(DataInst),
203
204 AlignmentAnchorForControlRegion(ControlRegion),
207 AlignmentAnchorForControlNode(ControlNode),
208 AlignmentAnchorForDataInst(DataInst),
209}
210
211impl From<Value> for Use {
212 fn from(value: Value) -> Self {
213 match value {
214 Value::Const(ct) => Use::CxInterned(CxInterned::Const(ct)),
215 Value::ControlRegionInput { region, input_idx } => {
216 Use::ControlRegionInput { region, input_idx }
217 }
218 Value::ControlNodeOutput { control_node, output_idx } => {
219 Use::ControlNodeOutput { control_node, output_idx }
220 }
221 Value::DataInstOutput(inst) => Use::DataInstOutput(inst),
222 }
223 }
224}
225
226impl Use {
227 const ANCHOR_ALIGNMENT_NAME_PREFIX: &'static str = "AA.";
231
232 fn keyword_and_name_prefix(self) -> (&'static str, &'static str) {
233 match self {
234 Self::Node(node) => node.keyword_and_name_prefix().unwrap(),
235 Self::CxInterned(interned) => interned.keyword_and_name_prefix(),
236 Self::ControlRegionLabel(_) => ("label", "L"),
237
238 Self::ControlRegionInput { .. }
239 | Self::ControlNodeOutput { .. }
240 | Self::DataInstOutput(_) => ("", "v"),
241
242 Self::AlignmentAnchorForControlRegion(_)
243 | Self::AlignmentAnchorForControlNode(_)
244 | Self::AlignmentAnchorForDataInst(_) => ("", Self::ANCHOR_ALIGNMENT_NAME_PREFIX),
245 }
246 }
247}
248
249impl<'a> Plan<'a> {
250 pub fn for_root(
254 cx: &'a Context,
255 root: &'a (impl Visit + Print<Output = pretty::Fragment>),
256 ) -> Self {
257 Self::for_versions(cx, [("", root)])
258 }
259
260 pub fn for_module(module: &'a Module) -> Self {
264 Self::for_root(module.cx_ref(), module)
265 }
266
267 pub fn for_versions(
277 cx: &'a Context,
278 versions: impl IntoIterator<
279 Item = (impl Into<String>, &'a (impl Visit + Print<Output = pretty::Fragment> + 'a)),
280 >,
281 ) -> Self {
282 let mut plan = Self {
283 cx,
284 current_module: None,
285 versions: vec![],
286 use_counts: FxIndexMap::default(),
287 attrs_to_unique_spv_name: FxHashMap::default(),
288 claimed_spv_names: FxHashMap::default(),
289 };
290 for (version_name, version_root) in versions {
291 let mut combined_use_counts = mem::take(&mut plan.use_counts);
292 let mut combined_attrs_to_unique_spv_name =
293 mem::take(&mut plan.attrs_to_unique_spv_name);
294 plan.claimed_spv_names.clear();
295
296 plan.versions.push(PlanVersion {
297 name: version_name.into(),
298 node_defs: FxHashMap::default(),
299 root: version_root,
300 });
301
302 version_root.visit_with(&mut plan);
303
304 if !combined_use_counts.is_empty() {
306 for (use_kind, new_count) in plan.use_counts.drain(..) {
307 let count = combined_use_counts.entry(use_kind).or_default();
308 *count = new_count.max(*count);
309 }
310 plan.use_counts = combined_use_counts;
311 }
312
313 if !combined_attrs_to_unique_spv_name.is_empty() {
315 for (attrs, unique_spv_name) in plan.attrs_to_unique_spv_name.drain() {
316 let combined =
317 combined_attrs_to_unique_spv_name.entry(attrs).or_insert(unique_spv_name);
318
319 *combined = match (*combined, unique_spv_name) {
320 (Ok(combined), Ok(new)) if std::ptr::eq(combined, new) => Ok(combined),
324
325 _ => Err(AmbiguousName),
326 };
327 }
328 plan.attrs_to_unique_spv_name = combined_attrs_to_unique_spv_name;
329 }
330 }
331
332 mem::take(&mut plan.claimed_spv_names);
334
335 plan
336 }
337
338 fn use_interned(&mut self, interned: CxInterned) {
343 let use_kind = Use::CxInterned(interned);
344 if let Some(use_count) = self.use_counts.get_mut(&use_kind) {
345 *use_count += 1;
346 return;
347 }
348
349 match interned {
350 CxInterned::Type(ty) => {
351 self.visit_type_def(&self.cx[ty]);
352 }
353 CxInterned::Const(ct) => {
354 self.visit_const_def(&self.cx[ct]);
355 }
356 }
357
358 *self.use_counts.entry(Use::Node(Node::AllCxInterned)).or_default() += 1;
360
361 *self.use_counts.entry(use_kind).or_default() += 1;
362 }
363
364 fn use_node<D: Visit>(&mut self, node: Node, node_def: &'a D)
369 where
370 NodeDef<'a>: From<&'a D>,
371 {
372 if let Some(use_count) = self.use_counts.get_mut(&Use::Node(node)) {
373 *use_count += 1;
374 return;
375 }
376
377 let current_version = self.versions.last_mut().unwrap();
378 match current_version.node_defs.entry(node) {
379 Entry::Occupied(entry) => {
380 let old_ptr_eq_new = match (*entry.get(), NodeDef::from(node_def)) {
381 (NodeDef::ModuleDialect(old), NodeDef::ModuleDialect(new)) => {
382 std::ptr::eq(old, new)
383 }
384 (NodeDef::ModuleDebugInfo(old), NodeDef::ModuleDebugInfo(new)) => {
385 std::ptr::eq(old, new)
386 }
387 (NodeDef::GlobalVar(old), NodeDef::GlobalVar(new)) => std::ptr::eq(old, new),
388 (NodeDef::Func(old), NodeDef::Func(new)) => std::ptr::eq(old, new),
389 _ => false,
390 };
391
392 assert!(
399 old_ptr_eq_new,
400 "print: same `{}` node has multiple distinct definitions in `Plan`",
401 node.keyword_and_name_prefix().map_or_else(|s| s, |(_, s)| s)
402 );
403 return;
404 }
405 Entry::Vacant(entry) => {
406 entry.insert(NodeDef::from(node_def));
407 }
408 }
409
410 node_def.visit_with(self);
411
412 *self.use_counts.entry(Use::Node(node)).or_default() += 1;
413 }
414}
415
416impl<'a> Visitor<'a> for Plan<'a> {
417 fn visit_attr_set_use(&mut self, attrs: AttrSet) {
418 let wk = &spv::spec::Spec::get().well_known;
419
420 let attrs_def = &self.cx[attrs];
421 self.visit_attr_set_def(attrs_def);
422
423 let mut spv_names = attrs_def
425 .attrs
426 .iter()
427 .filter_map(|attr| match attr {
428 Attr::SpvAnnotation(spv_inst) if spv_inst.opcode == wk.OpName => Some(spv_inst),
429 _ => None,
430 })
431 .peekable();
432 if let Some(existing_entry) = self.attrs_to_unique_spv_name.get_mut(&attrs) {
433 *existing_entry = Err(AmbiguousName);
435 } else if let Some(&first_spv_name) = spv_names.peek() {
436 let mut result = Ok(first_spv_name);
437
438 for spv_name in spv_names {
443 let claim = self.claimed_spv_names.entry(spv_name).or_insert(Ok(attrs));
444
445 if let Ok(claimant) = *claim {
446 if claimant == attrs {
447 continue;
449 }
450
451 self.attrs_to_unique_spv_name.insert(claimant, Err(AmbiguousName));
453 }
454
455 *claim = Err(AmbiguousName);
457 result = Err(AmbiguousName);
458 }
459
460 self.attrs_to_unique_spv_name.insert(attrs, result);
461 }
462 }
463 fn visit_type_use(&mut self, ty: Type) {
464 self.use_interned(CxInterned::Type(ty));
465 }
466 fn visit_const_use(&mut self, ct: Const) {
467 self.use_interned(CxInterned::Const(ct));
468 }
469 fn visit_data_inst_form_use(&mut self, data_inst_form: DataInstForm) {
470 self.visit_data_inst_form_def(&self.cx[data_inst_form]);
474 }
475
476 fn visit_global_var_use(&mut self, gv: GlobalVar) {
477 if let Some(module) = self.current_module {
478 self.use_node(Node::GlobalVar(gv), &module.global_vars[gv]);
479 } else {
480 }
482 }
483
484 fn visit_func_use(&mut self, func: Func) {
485 if let Some(module) = self.current_module {
486 self.use_node(Node::Func(func), &module.funcs[func]);
487 } else {
488 }
490 }
491
492 fn visit_module(&mut self, module: &'a Module) {
493 assert!(
494 std::ptr::eq(self.cx, &**module.cx_ref()),
495 "print: `Plan::visit_module` does not support `Module`s from a \
496 different `Context` than the one it was initially created with",
497 );
498
499 let old_module = self.current_module.replace(module);
500 module.inner_visit_with(self);
501 self.current_module = old_module;
502 }
503 fn visit_module_dialect(&mut self, dialect: &'a ModuleDialect) {
504 self.use_node(Node::ModuleDialect, dialect);
505 }
506 fn visit_module_debug_info(&mut self, debug_info: &'a ModuleDebugInfo) {
507 self.use_node(Node::ModuleDebugInfo, debug_info);
508 }
509
510 fn visit_attr(&mut self, attr: &'a Attr) {
511 attr.inner_visit_with(self);
512
513 if let Attr::Diagnostics(OrdAssertEq(diags)) = attr {
516 for diag in diags {
517 let Diag { level, message } = diag;
518 match level {
519 DiagLevel::Bug(_) | DiagLevel::Error | DiagLevel::Warning => {}
520 }
521 message.inner_visit_with(self);
522 }
523 }
524 }
525
526 fn visit_const_def(&mut self, ct_def: &'a ConstDef) {
527 if let ConstKind::PtrToGlobalVar(gv) = ct_def.kind {
529 self.visit_attr_set_use(ct_def.attrs);
530 self.visit_global_var_use(gv);
531 } else {
532 ct_def.inner_visit_with(self);
533 }
534 }
535
536 fn visit_global_var_decl(&mut self, gv_decl: &'a GlobalVarDecl) {
537 let pointee_type = {
540 let wk = &spv::spec::Spec::get().well_known;
541
542 match &self.cx[gv_decl.type_of_ptr_to].kind {
543 TypeKind::SpvInst { spv_inst, type_and_const_inputs }
544 if spv_inst.opcode == wk.OpTypePointer =>
545 {
546 match type_and_const_inputs[..] {
547 [TypeOrConst::Type(ty)] => Some(ty),
548 _ => unreachable!(),
549 }
550 }
551 _ => None,
552 }
553 };
554
555 match (gv_decl, pointee_type) {
559 (
560 GlobalVarDecl {
561 attrs,
562 type_of_ptr_to: _,
563 shape: None,
564 addr_space: AddrSpace::SpvStorageClass(_),
565 def,
566 },
567 Some(pointee_type),
568 ) => {
569 self.visit_attr_set_use(*attrs);
570 self.visit_type_use(pointee_type);
571 def.inner_visit_with(self);
572 }
573
574 _ => {
575 gv_decl.inner_visit_with(self);
576 }
577 }
578 }
579
580 fn visit_func_decl(&mut self, func_decl: &'a FuncDecl) {
581 if let DeclDef::Present(func_def_body) = &func_decl.def {
582 if let Some(cfg) = &func_def_body.unstructured_cfg {
583 for region in cfg.rev_post_order(func_def_body) {
584 if let Some(control_inst) = cfg.control_inst_on_exit_from.get(region) {
585 for &target in &control_inst.targets {
586 *self.use_counts.entry(Use::ControlRegionLabel(target)).or_default() +=
587 1;
588 }
589 }
590 }
591 }
592 }
593
594 func_decl.inner_visit_with(self);
595 }
596
597 fn visit_value_use(&mut self, v: &'a Value) {
598 match *v {
599 Value::Const(_) => {}
600 _ => *self.use_counts.entry(Use::from(*v)).or_default() += 1,
601 }
602 v.inner_visit_with(self);
603 }
604}
605
606const MAX_LINE_WIDTH: usize = 120;
608
609impl Plan<'_> {
610 #[allow(rustdoc::private_intra_doc_links)]
611 pub fn pretty_print(&self) -> Versions<pretty::FragmentPostLayout> {
618 self.print(&Printer::new(self))
619 .map_pretty_fragments(|fragment| fragment.layout_with_max_line_width(MAX_LINE_WIDTH))
620 }
621
622 pub fn pretty_print_deps_and_root_separately(
625 &self,
626 ) -> (Versions<pretty::FragmentPostLayout>, Versions<pretty::FragmentPostLayout>) {
627 let printer = Printer::new(self);
628 (
629 self.print_all_nodes_and_or_root(&printer, true, false).map_pretty_fragments(
630 |fragment| fragment.layout_with_max_line_width(MAX_LINE_WIDTH),
631 ),
632 self.print_all_nodes_and_or_root(&printer, false, true).map_pretty_fragments(
633 |fragment| fragment.layout_with_max_line_width(MAX_LINE_WIDTH),
634 ),
635 )
636 }
637}
638
639pub struct Printer<'a> {
640 cx: &'a Context,
641 use_styles: FxIndexMap<Use, UseStyle>,
642
643 attrs_with_spv_name_in_use: FxHashMap<AttrSet, &'a spv::Inst>,
647}
648
649enum UseStyle {
651 Anon {
653 parent_func: Option<Func>,
656
657 idx: usize,
658 },
659
660 Named {
662 parent_func: Option<Func>,
665
666 name: String,
667 },
668
669 Inline,
671}
672
673impl<'a> Printer<'a> {
674 fn new(plan: &Plan<'a>) -> Self {
675 let cx = plan.cx;
676 let wk = &spv::spec::Spec::get().well_known;
677
678 enum SmallSet<T, const N: usize> {
680 Linear(ArrayVec<T, N>),
681 Hashed(Box<FxIndexSet<T>>),
682 }
683
684 type SmallSetIter<'a, T> = Either<std::slice::Iter<'a, T>, indexmap::set::Iter<'a, T>>;
685
686 impl<T, const N: usize> Default for SmallSet<T, N> {
687 fn default() -> Self {
688 Self::Linear(ArrayVec::new())
689 }
690 }
691
692 impl<T: Eq + Hash, const N: usize> SmallSet<T, N> {
693 fn insert(&mut self, x: T) {
694 match self {
695 Self::Linear(xs) => {
696 if !xs.iter().rev().any(|old| *old == x) {
699 if let Err(err) = xs.try_push(x) {
700 *self = Self::Hashed(Box::new(
701 xs.drain(..).chain([err.element()]).collect(),
702 ));
703 }
704 }
705 }
706 Self::Hashed(xs) => {
707 xs.insert(x);
708 }
709 }
710 }
711
712 fn iter(&self) -> SmallSetIter<'_, T> {
713 match self {
714 Self::Linear(xs) => Either::Left(xs.iter()),
715 Self::Hashed(xs) => Either::Right(xs.iter()),
716 }
717 }
718 }
719
720 let mut attrs_with_spv_name_in_use = FxHashMap::default();
721
722 let mut try_claim_name_from_attrs_across_versions =
725 |deduped_attrs_across_versions: SmallSetIter<'_, AttrSet>| {
726 deduped_attrs_across_versions
727 .copied()
728 .map(|attrs| Some((attrs, plan.attrs_to_unique_spv_name.get(&attrs)?.ok()?)))
729 .collect::<Option<SmallVec<[_; 4]>>>()
730 .filter(|all_names| all_names.iter().map(|(_, spv_name)| spv_name).all_equal())
731 .and_then(|all_names| {
732 let &(_, spv_name) = all_names.first()?;
733 let name = spv::extract_literal_string(&spv_name.imms).ok()?;
734
735 for (attrs, spv_name) in all_names {
738 attrs_with_spv_name_in_use.insert(attrs, spv_name);
739 }
740
741 Some(name)
742 })
743 };
744
745 #[derive(Default)]
746 struct AnonCounters {
747 types: usize,
748 consts: usize,
749
750 global_vars: usize,
751 funcs: usize,
752 }
753 let mut anon_counters = AnonCounters::default();
754
755 let mut use_styles: FxIndexMap<_, _> = plan
756 .use_counts
757 .iter()
758 .map(|(&use_kind, &use_count)| {
759 if let Use::ControlRegionLabel(_)
761 | Use::ControlRegionInput { .. }
762 | Use::ControlNodeOutput { .. }
763 | Use::DataInstOutput(_) = use_kind
764 {
765 return (use_kind, UseStyle::Inline);
766 }
767
768 if let Use::Node(
770 Node::AllCxInterned | Node::ModuleDialect | Node::ModuleDebugInfo,
771 ) = use_kind
772 {
773 return (use_kind, UseStyle::Anon { parent_func: None, idx: 0 });
774 }
775
776 let mut deduped_attrs_across_versions = SmallSet::<_, 8>::default();
777 match use_kind {
778 Use::CxInterned(interned) => {
779 deduped_attrs_across_versions.insert(match interned {
780 CxInterned::Type(ty) => cx[ty].attrs,
781 CxInterned::Const(ct) => cx[ct].attrs,
782 });
783 }
784 Use::Node(node) => {
785 for version in &plan.versions {
786 let attrs = match version.node_defs.get(&node) {
787 Some(NodeDef::GlobalVar(gv_decl)) => gv_decl.attrs,
788 Some(NodeDef::Func(func_decl)) => func_decl.attrs,
789 _ => continue,
790 };
791 deduped_attrs_across_versions.insert(attrs);
792 }
793 }
794 Use::ControlRegionLabel(_)
795 | Use::ControlRegionInput { .. }
796 | Use::ControlNodeOutput { .. }
797 | Use::DataInstOutput(_)
798 | Use::AlignmentAnchorForControlRegion(_)
799 | Use::AlignmentAnchorForControlNode(_)
800 | Use::AlignmentAnchorForDataInst(_) => unreachable!(),
801 }
802
803 if let Some(name) =
804 try_claim_name_from_attrs_across_versions(deduped_attrs_across_versions.iter())
805 {
806 return (use_kind, UseStyle::Named { parent_func: None, name });
807 }
808
809 let inline = match use_kind {
810 Use::CxInterned(interned) => {
811 use_count == 1
812 || match interned {
813 CxInterned::Type(ty) => {
814 let ty_def = &cx[ty];
815
816 let has_compact_print_or_is_leaf = match &ty_def.kind {
819 TypeKind::SpvInst { spv_inst, type_and_const_inputs } => {
820 [
821 wk.OpTypeBool,
822 wk.OpTypeInt,
823 wk.OpTypeFloat,
824 wk.OpTypeVector,
825 ]
826 .contains(&spv_inst.opcode)
827 || type_and_const_inputs.is_empty()
828 }
829
830 TypeKind::QPtr | TypeKind::SpvStringLiteralForExtInst => {
831 true
832 }
833 };
834
835 ty_def.attrs == AttrSet::default()
836 && has_compact_print_or_is_leaf
837 }
838 CxInterned::Const(ct) => {
839 let ct_def = &cx[ct];
840
841 let (has_compact_print, has_nested_consts) = match &ct_def.kind
844 {
845 ConstKind::SpvInst { spv_inst_and_const_inputs } => {
846 let (spv_inst, const_inputs) =
847 &**spv_inst_and_const_inputs;
848 (
849 [
850 wk.OpConstantFalse,
851 wk.OpConstantTrue,
852 wk.OpConstant,
853 ]
854 .contains(&spv_inst.opcode),
855 !const_inputs.is_empty(),
856 )
857 }
858 _ => (false, false),
859 };
860
861 ct_def.attrs == AttrSet::default()
862 && (has_compact_print || !has_nested_consts)
863 }
864 }
865 }
866 Use::Node(_) => false,
867
868 Use::ControlRegionLabel(_)
869 | Use::ControlRegionInput { .. }
870 | Use::ControlNodeOutput { .. }
871 | Use::DataInstOutput(_)
872 | Use::AlignmentAnchorForControlRegion(_)
873 | Use::AlignmentAnchorForControlNode(_)
874 | Use::AlignmentAnchorForDataInst(_) => {
875 unreachable!()
876 }
877 };
878 let style = if inline {
879 UseStyle::Inline
880 } else {
881 let ac = &mut anon_counters;
882 let counter = match use_kind {
883 Use::CxInterned(CxInterned::Type(_)) => &mut ac.types,
884 Use::CxInterned(CxInterned::Const(_)) => &mut ac.consts,
885 Use::Node(Node::GlobalVar(_)) => &mut ac.global_vars,
886 Use::Node(Node::Func(_)) => &mut ac.funcs,
887
888 Use::Node(
889 Node::AllCxInterned | Node::ModuleDialect | Node::ModuleDebugInfo,
890 )
891 | Use::ControlRegionLabel(_)
892 | Use::ControlRegionInput { .. }
893 | Use::ControlNodeOutput { .. }
894 | Use::DataInstOutput(_)
895 | Use::AlignmentAnchorForControlRegion(_)
896 | Use::AlignmentAnchorForControlNode(_)
897 | Use::AlignmentAnchorForDataInst(_) => {
898 unreachable!()
899 }
900 };
901 let idx = *counter;
902 *counter += 1;
903 UseStyle::Anon { parent_func: None, idx }
904 };
905 (use_kind, style)
906 })
907 .collect();
908
909 let all_funcs = plan.use_counts.keys().filter_map(|&use_kind| match use_kind {
910 Use::Node(Node::Func(func)) => Some(func),
911 _ => None,
912 });
913 for func in all_funcs {
914 assert!(matches!(
915 use_styles.get(&Use::Node(Node::Func(func))),
916 Some(UseStyle::Anon { .. } | UseStyle::Named { .. })
917 ));
918
919 #[derive(Default)]
926 struct IntraFuncDefAcrossVersions {
927 deduped_attrs_across_versions: SmallSet<AttrSet, 4>,
928 }
929 let mut intra_func_defs_across_versions: FxIndexMap<Use, IntraFuncDefAcrossVersions> =
930 FxIndexMap::default();
931
932 let func_def_bodies_across_versions = plan.versions.iter().filter_map(|version| {
933 match version.node_defs.get(&Node::Func(func))? {
934 NodeDef::Func(FuncDecl { def: DeclDef::Present(func_def_body), .. }) => {
935 Some(func_def_body)
936 }
937
938 _ => None,
939 }
940 });
941
942 for func_def_body in func_def_bodies_across_versions {
943 let mut define = |use_kind, attrs| {
944 let def = intra_func_defs_across_versions.entry(use_kind).or_default();
945 if let Some(attrs) = attrs {
946 def.deduped_attrs_across_versions.insert(attrs);
947 }
948 };
949 let visit_region = |func_at_region: FuncAt<'_, ControlRegion>| {
950 let region = func_at_region.position;
951
952 define(Use::AlignmentAnchorForControlRegion(region), None);
953 define(Use::ControlRegionLabel(region), None);
955
956 let ControlRegionDef { inputs, children, outputs: _ } =
957 func_def_body.at(region).def();
958
959 for (i, input_decl) in inputs.iter().enumerate() {
960 define(
961 Use::ControlRegionInput { region, input_idx: i.try_into().unwrap() },
962 Some(input_decl.attrs),
963 );
964 }
965
966 for func_at_control_node in func_def_body.at(*children) {
967 let control_node = func_at_control_node.position;
968
969 define(Use::AlignmentAnchorForControlNode(control_node), None);
970
971 let ControlNodeDef { kind, outputs } = func_at_control_node.def();
972
973 if let ControlNodeKind::Block { insts } = *kind {
974 for func_at_inst in func_def_body.at(insts) {
975 define(
976 Use::AlignmentAnchorForDataInst(func_at_inst.position),
977 None,
978 );
979 let inst_def = func_at_inst.def();
980 if cx[inst_def.form].output_type.is_some() {
981 define(
982 Use::DataInstOutput(func_at_inst.position),
983 Some(inst_def.attrs),
984 );
985 }
986 }
987 }
988
989 for (i, output_decl) in outputs.iter().enumerate() {
990 define(
991 Use::ControlNodeOutput {
992 control_node,
993 output_idx: i.try_into().unwrap(),
994 },
995 Some(output_decl.attrs),
996 );
997 }
998 }
999 };
1000
1001 struct VisitAllRegions<F>(F);
1003 impl<'a, F: FnMut(FuncAt<'a, ControlRegion>)> Visitor<'a> for VisitAllRegions<F> {
1004 fn visit_attr_set_use(&mut self, _: AttrSet) {}
1007 fn visit_type_use(&mut self, _: Type) {}
1008 fn visit_const_use(&mut self, _: Const) {}
1009 fn visit_data_inst_form_use(&mut self, _: DataInstForm) {}
1010 fn visit_global_var_use(&mut self, _: GlobalVar) {}
1011 fn visit_func_use(&mut self, _: Func) {}
1012
1013 fn visit_control_region_def(
1014 &mut self,
1015 func_at_control_region: FuncAt<'a, ControlRegion>,
1016 ) {
1017 self.0(func_at_control_region);
1018 func_at_control_region.inner_visit_with(self);
1019 }
1020 }
1021 func_def_body.inner_visit_with(&mut VisitAllRegions(visit_region));
1022 }
1023
1024 let mut control_region_label_counter = 0;
1025 let mut value_counter = 0;
1026 let mut alignment_anchor_counter = 0;
1027
1028 for (use_kind, def) in intra_func_defs_across_versions {
1032 let (counter, use_style_slot) = match use_kind {
1033 Use::ControlRegionLabel(_) => {
1034 (&mut control_region_label_counter, use_styles.get_mut(&use_kind))
1035 }
1036
1037 Use::ControlRegionInput { .. }
1038 | Use::ControlNodeOutput { .. }
1039 | Use::DataInstOutput(_) => (&mut value_counter, use_styles.get_mut(&use_kind)),
1040
1041 Use::AlignmentAnchorForControlRegion(_)
1042 | Use::AlignmentAnchorForControlNode(_)
1043 | Use::AlignmentAnchorForDataInst(_) => (
1044 &mut alignment_anchor_counter,
1045 Some(use_styles.entry(use_kind).or_insert(UseStyle::Inline)),
1046 ),
1047
1048 _ => unreachable!(),
1049 };
1050 if let Some(use_style) = use_style_slot {
1051 assert!(matches!(use_style, UseStyle::Inline));
1052
1053 let parent_func = Some(func);
1054 let named_style = try_claim_name_from_attrs_across_versions(
1055 def.deduped_attrs_across_versions.iter(),
1056 )
1057 .map(|name| UseStyle::Named { parent_func, name });
1058
1059 *use_style = named_style.unwrap_or_else(|| {
1060 let idx = *counter;
1061 *counter += 1;
1062 UseStyle::Anon { parent_func, idx }
1063 });
1064 }
1065 }
1066 }
1067
1068 Self { cx, use_styles, attrs_with_spv_name_in_use }
1069 }
1070
1071 pub fn cx(&self) -> &'a Context {
1072 self.cx
1073 }
1074}
1075
1076#[allow(clippy::unused_self)]
1081impl Printer<'_> {
1082 fn error_style(&self) -> pretty::Styles {
1083 pretty::Styles::color(pretty::palettes::simple::MAGENTA)
1084 }
1085 fn comment_style(&self) -> pretty::Styles {
1086 pretty::Styles {
1087 color_opacity: Some(0.3),
1088 size: Some(-4),
1089 ..pretty::Styles::color(pretty::palettes::simple::DARK_GRAY)
1092 }
1093 }
1094 fn named_argument_label_style(&self) -> pretty::Styles {
1095 pretty::Styles {
1096 size: Some(-5),
1097 ..pretty::Styles::color(pretty::palettes::simple::DARK_GRAY)
1098 }
1099 }
1100 fn numeric_literal_style(&self) -> pretty::Styles {
1101 pretty::Styles::color(pretty::palettes::simple::YELLOW)
1102 }
1103 fn string_literal_style(&self) -> pretty::Styles {
1104 pretty::Styles::color(pretty::palettes::simple::RED)
1105 }
1106 fn string_literal_escape_style(&self) -> pretty::Styles {
1107 pretty::Styles::color(pretty::palettes::simple::ORANGE)
1108 }
1109 fn declarative_keyword_style(&self) -> pretty::Styles {
1110 pretty::Styles::color(pretty::palettes::simple::BLUE)
1111 }
1112 fn imperative_keyword_style(&self) -> pretty::Styles {
1113 pretty::Styles {
1114 thickness: Some(2),
1115 ..pretty::Styles::color(pretty::palettes::simple::MAGENTA)
1116 }
1117 }
1118 fn spv_base_style(&self) -> pretty::Styles {
1119 pretty::Styles::color(pretty::palettes::simple::ORANGE)
1120 }
1121 fn spv_op_style(&self) -> pretty::Styles {
1122 pretty::Styles { thickness: Some(3), ..self.spv_base_style() }
1123 }
1124 fn spv_enumerand_name_style(&self) -> pretty::Styles {
1125 pretty::Styles::color(pretty::palettes::simple::CYAN)
1126 }
1127 fn attr_style(&self) -> pretty::Styles {
1128 pretty::Styles {
1129 color_opacity: Some(0.6),
1130 thickness: Some(-2),
1131 ..pretty::Styles::color(pretty::palettes::simple::GREEN)
1132 }
1133 }
1134
1135 fn demote_style_for_namespace_prefix(&self, mut style: pretty::Styles) -> pretty::Styles {
1138 style.color_opacity = Some(style.color_opacity.unwrap_or(1.0) * 0.6);
1142 style.size = Some(style.size.map_or(-4, |size| size - 1));
1144 style
1145 }
1146}
1147
1148impl Printer<'_> {
1149 fn pretty_string_literal(&self, s: &str) -> pretty::Fragment {
1153 pretty::Fragment::new(
1156 (s.contains('\n').then_some(Either::Left(' ')).into_iter())
1159 .chain([Either::Left('"')])
1160 .chain(s.chars().flat_map(|c| {
1161 let escaped = c.escape_debug();
1162 let maybe_escaped = if c == '\'' {
1163 assert_eq!(escaped.collect_tuple(), Some(('\\', c)));
1165 Either::Left(c)
1166 } else if let Some((single,)) = escaped.clone().collect_tuple() {
1167 assert_eq!(single, c);
1168 Either::Left(c)
1169 } else {
1170 assert_eq!(escaped.clone().next(), Some('\\'));
1171 Either::Right(escaped)
1172 };
1173
1174 let extra_prefix_unescaped = if c == '\n' { "\\\n" } else { "" };
1178
1179 (extra_prefix_unescaped.chars().map(Either::Left)).chain([maybe_escaped])
1180 }))
1181 .chain([Either::Left('"')])
1182 .group_by(|maybe_escaped| maybe_escaped.is_right())
1183 .into_iter()
1184 .map(|(escaped, group)| {
1185 if escaped {
1186 self.string_literal_escape_style()
1187 .apply(group.flat_map(Either::unwrap_right).collect::<String>())
1188 } else {
1189 self.string_literal_style()
1190 .apply(group.map(Either::unwrap_left).collect::<String>())
1191 }
1192 }),
1193 )
1194 }
1195
1196 fn pretty_named_argument_prefix<'b>(&self, name: impl Into<Cow<'b, str>>) -> pretty::Fragment {
1198 self.named_argument_label_style().apply(format!("{}: ", name.into())).into()
1200 }
1201
1202 fn pretty_type_ascription_suffix(&self, ty: Type) -> pretty::Fragment {
1207 pretty::join_space(":", [ty.print(self)])
1208 }
1209
1210 fn pretty_spv_opcode(
1212 &self,
1213 opcode_name_style: pretty::Styles,
1214 opcode: spv::spec::Opcode,
1215 ) -> pretty::Fragment {
1216 pretty::Fragment::new([
1217 self.demote_style_for_namespace_prefix(self.spv_base_style()).apply("spv."),
1218 opcode_name_style.apply(opcode.name()),
1219 ])
1220 }
1221
1222 #[allow(clippy::unused_self)]
1224 fn sanitize_spv_operand_name<'b>(&self, name: &'b str) -> Option<Cow<'b, str>> {
1225 Some(name).and_then(|name| {
1226 if name == "Type"
1228 || name
1229 .strip_prefix("Operand ")
1230 .is_some_and(|s| s.chars().all(|c| c.is_ascii_digit()))
1231 {
1232 return None;
1233 }
1234
1235 let name = name
1238 .split_ascii_whitespace()
1239 .map(|word| {
1240 if word.starts_with(|c: char| c.is_ascii_lowercase()) {
1241 let mut word = word.to_string();
1242 word[..1].make_ascii_uppercase();
1243 Cow::Owned(word)
1244 } else {
1245 word.into()
1246 }
1247 })
1248 .reduce(|out, extra| (out.into_owned() + &extra).into())
1249 .unwrap_or_default();
1250
1251 Some(name)
1252 })
1253 }
1254
1255 fn pretty_spv_print_tokens_for_operand(
1257 &self,
1258 operand: spv::print::TokensForOperand<Option<pretty::Fragment>>,
1259 ) -> pretty::Fragment {
1260 pretty::Fragment::new(operand.tokens.into_iter().map(|token| {
1261 match token {
1262 spv::print::Token::Error(s) => self.error_style().apply(s).into(),
1263 spv::print::Token::OperandName(s) => self
1264 .sanitize_spv_operand_name(s)
1265 .map(|name| self.pretty_named_argument_prefix(name))
1266 .unwrap_or_default(),
1267 spv::print::Token::Punctuation(s) => s.into(),
1268 spv::print::Token::OperandKindNamespacePrefix(s) => {
1269 pretty::Fragment::new([
1270 self.demote_style_for_namespace_prefix(
1273 self.demote_style_for_namespace_prefix(self.spv_base_style()),
1274 )
1275 .apply("spv."),
1276 self.demote_style_for_namespace_prefix(self.declarative_keyword_style())
1278 .apply(format!("{s}.")),
1279 ])
1280 }
1281 spv::print::Token::EnumerandName(s) => {
1282 self.spv_enumerand_name_style().apply(s).into()
1283 }
1284 spv::print::Token::NumericLiteral(s) => {
1285 self.numeric_literal_style().apply(s).into()
1286 }
1287 spv::print::Token::StringLiteral(s) => self.string_literal_style().apply(s).into(),
1288 spv::print::Token::Id(id) => {
1289 id.unwrap_or_else(|| self.comment_style().apply("/* implicit ID */").into())
1290 }
1291 }
1292 }))
1293 }
1294
1295 fn pretty_spv_operand_from_imms(
1298 &self,
1299 imms: impl IntoIterator<Item = spv::Imm>,
1300 ) -> pretty::Fragment {
1301 self.pretty_spv_print_tokens_for_operand(spv::print::operand_from_imms(imms))
1302 }
1303
1304 fn pretty_spv_imm(&self, kind: spv::spec::OperandKind, word: u32) -> pretty::Fragment {
1306 self.pretty_spv_operand_from_imms([spv::Imm::Short(kind, word)])
1307 }
1308
1309 fn pretty_spv_inst<OPF: Into<Option<pretty::Fragment>>>(
1324 &self,
1325 spv_inst_name_style: pretty::Styles,
1326 opcode: spv::spec::Opcode,
1327 imms: &[spv::Imm],
1328 printed_ids: impl IntoIterator<Item = OPF>,
1329 ) -> pretty::Fragment {
1330 let mut operands = spv::print::inst_operands(
1331 opcode,
1332 imms.iter().copied(),
1333 printed_ids.into_iter().map(|printed_id| printed_id.into()),
1334 )
1335 .filter_map(|operand| match operand.tokens[..] {
1336 [spv::print::Token::Id(None)]
1337 | [spv::print::Token::OperandName(_), spv::print::Token::Id(None)] => None,
1338
1339 _ => Some(self.pretty_spv_print_tokens_for_operand(operand)),
1340 })
1341 .peekable();
1342
1343 let mut out = self.pretty_spv_opcode(spv_inst_name_style, opcode);
1344
1345 if operands.peek().is_some() {
1346 out = pretty::Fragment::new([out, pretty::join_comma_sep("(", operands, ")")]);
1347 }
1348
1349 out
1350 }
1351}
1352
1353#[derive(Default)]
1356pub struct AttrsAndDef {
1357 pub attrs: pretty::Fragment,
1358
1359 pub def_without_name: pretty::Fragment,
1365}
1366
1367impl AttrsAndDef {
1368 fn insert_name_before_def(self, name: impl Into<pretty::Fragment>) -> pretty::Fragment {
1375 let Self { attrs, def_without_name } = self;
1376
1377 let mut maybe_hoisted_anchor = pretty::Fragment::default();
1378 let mut maybe_def_start_anchor = pretty::Fragment::default();
1379 let mut maybe_def_end_anchor = pretty::Fragment::default();
1380 let mut name = name.into();
1381 if let [
1382 pretty::Node::Anchor { is_def: ref mut original_anchor_is_def @ true, anchor, text: _ },
1383 ..,
1384 ] = &mut name.nodes[..]
1385 {
1386 if !attrs.nodes.is_empty() {
1387 *original_anchor_is_def = false;
1388 maybe_hoisted_anchor = pretty::Node::Anchor {
1389 is_def: true,
1390 anchor: anchor.clone(),
1391 text: vec![].into(),
1392 }
1393 .into();
1394 }
1395
1396 let has_alignment_anchor = match &def_without_name.nodes[..] {
1401 [pretty::Node::Anchor { is_def: true, anchor, text }, ..] => {
1402 anchor.contains(Use::ANCHOR_ALIGNMENT_NAME_PREFIX) && text.is_empty()
1403 }
1404
1405 _ => false,
1406 };
1407 let mk_anchor_def = |suffix| {
1408 pretty::Node::Anchor {
1409 is_def: true,
1410 anchor: format!("{anchor}.{suffix}").into(),
1411 text: vec![].into(),
1412 }
1413 .into()
1414 };
1415 if !has_alignment_anchor {
1416 maybe_def_start_anchor = mk_anchor_def("start");
1417 if false {
1421 maybe_def_end_anchor = mk_anchor_def("end");
1422 }
1423 }
1424 }
1425 pretty::Fragment::new([
1426 maybe_hoisted_anchor,
1427 attrs,
1428 name,
1429 maybe_def_start_anchor,
1430 def_without_name,
1431 maybe_def_end_anchor,
1432 ])
1433 }
1434}
1435
1436pub trait Print {
1437 type Output;
1441 fn print(&self, printer: &Printer<'_>) -> Self::Output;
1442}
1443
1444impl Use {
1445 fn print_as_ref_or_def(&self, printer: &Printer<'_>, is_def: bool) -> pretty::Fragment {
1447 let style = printer.use_styles.get(self).unwrap_or(&UseStyle::Inline);
1448 match style {
1449 &UseStyle::Anon { parent_func, idx: _ } | &UseStyle::Named { parent_func, name: _ } => {
1450 #[derive(Debug, PartialEq, Eq)]
1452 enum Suffix<'a> {
1453 Num(usize),
1454 Name(&'a str),
1455 }
1456
1457 impl Suffix<'_> {
1458 fn write_escaped_to(&self, w: &mut impl fmt::Write) -> fmt::Result {
1462 match *self {
1463 Suffix::Num(idx) => write!(w, "{idx}"),
1464 Suffix::Name(mut name) => {
1465 w.write_str("_")?;
1469
1470 while !name.is_empty() {
1471 let is_valid = |c: char| c.is_ascii_alphanumeric() || c == '_';
1474 let name_after_valid = name.trim_start_matches(is_valid);
1475 let valid_prefix = &name[..name.len() - name_after_valid.len()];
1476 name = name_after_valid;
1477
1478 if !valid_prefix.is_empty() {
1479 w.write_str(valid_prefix)?;
1480 }
1481
1482 let mut chars = name.chars();
1485 if let Some(c) = chars.next() {
1486 assert!(!is_valid(c));
1487 write!(w, "&#{};", c as u32)?;
1488 }
1489 name = chars.as_str();
1490 }
1491 Ok(())
1492 }
1493 }
1494 }
1495 }
1496
1497 let suffix_of = |style| match style {
1498 &UseStyle::Anon { idx, .. } => Suffix::Num(idx),
1499 UseStyle::Named { name, .. } => Suffix::Name(name),
1500 UseStyle::Inline => unreachable!(),
1501 };
1502
1503 let (keyword, name_prefix) = self.keyword_and_name_prefix();
1504 let suffix = suffix_of(style);
1505
1506 let mk_anchor = |anchor: String, text: Vec<_>| pretty::Node::Anchor {
1508 is_def,
1509 anchor: anchor.into(),
1510 text: text.into(),
1511 };
1512
1513 if let Use::Node(Node::ModuleDialect | Node::ModuleDebugInfo) = self {
1515 assert_eq!((is_def, name_prefix, suffix), (true, "", Suffix::Num(0)));
1516 return mk_anchor(keyword.into(), vec![(None, keyword.into())]).into();
1517 }
1518
1519 let mut anchor = String::new();
1520 if let Some(func) = parent_func {
1521 let func = Use::Node(Node::Func(func));
1524 write!(anchor, "{}", func.keyword_and_name_prefix().1).unwrap();
1525 suffix_of(&printer.use_styles[&func]).write_escaped_to(&mut anchor).unwrap();
1526 anchor += ".";
1527 }
1528 anchor += name_prefix;
1529 suffix.write_escaped_to(&mut anchor).unwrap();
1530
1531 let name = if let Self::AlignmentAnchorForControlRegion(_)
1532 | Self::AlignmentAnchorForControlNode(_)
1533 | Self::AlignmentAnchorForDataInst(_) = self
1534 {
1535 vec![]
1536 } else {
1537 let suffix_size = if name_prefix.ends_with(|c: char| c.is_ascii_uppercase()) {
1539 -1
1540 } else {
1541 -2
1542 };
1543 let suffix = match suffix {
1544 Suffix::Num(idx) => (
1545 Some(pretty::Styles {
1546 size: Some(suffix_size),
1547 subscript: true,
1548 ..Default::default()
1549 }),
1550 format!("{idx}").into(),
1551 ),
1552 Suffix::Name(name) => (
1553 Some(pretty::Styles {
1554 thickness: Some(0),
1555 size: Some(suffix_size - 1),
1556 color: Some(pretty::palettes::simple::LIGHT_GRAY),
1557 ..Default::default()
1558 }),
1559 format!("`{name}`").into(),
1560 ),
1561 };
1562 Some(keyword)
1563 .filter(|kw| is_def && !kw.is_empty())
1564 .into_iter()
1565 .flat_map(|kw| [(None, kw.into()), (None, " ".into())])
1566 .chain([(None, name_prefix.into()), suffix])
1567 .collect()
1568 };
1569 mk_anchor(anchor, name).into()
1570 }
1571 UseStyle::Inline => match *self {
1572 Self::CxInterned(interned) => {
1573 interned.print(printer).insert_name_before_def(pretty::Fragment::default())
1574 }
1575 Self::Node(node) => printer
1576 .error_style()
1577 .apply(format!(
1578 "/* undefined {} */_",
1579 node.keyword_and_name_prefix().map_or_else(|s| s, |(s, _)| s)
1580 ))
1581 .into(),
1582 Self::ControlRegionLabel(_)
1583 | Self::ControlRegionInput { .. }
1584 | Self::ControlNodeOutput { .. }
1585 | Self::DataInstOutput(_) => "_".into(),
1586
1587 Self::AlignmentAnchorForControlRegion(_)
1588 | Self::AlignmentAnchorForControlNode(_)
1589 | Self::AlignmentAnchorForDataInst(_) => unreachable!(),
1590 },
1591 }
1592 }
1593
1594 fn print_as_def(&self, printer: &Printer<'_>) -> pretty::Fragment {
1595 self.print_as_ref_or_def(printer, true)
1596 }
1597}
1598
1599impl Print for Use {
1600 type Output = pretty::Fragment;
1601 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
1602 self.print_as_ref_or_def(printer, false)
1603 }
1604}
1605
1606impl Print for Type {
1608 type Output = pretty::Fragment;
1609 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
1610 Use::CxInterned(CxInterned::Type(*self)).print(printer)
1611 }
1612}
1613impl Print for Const {
1614 type Output = pretty::Fragment;
1615 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
1616 Use::CxInterned(CxInterned::Const(*self)).print(printer)
1617 }
1618}
1619impl Print for GlobalVar {
1620 type Output = pretty::Fragment;
1621 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
1622 Use::Node(Node::GlobalVar(*self)).print(printer)
1623 }
1624}
1625impl Print for Func {
1626 type Output = pretty::Fragment;
1627 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
1628 Use::Node(Node::Func(*self)).print(printer)
1629 }
1630}
1631
1632impl Print for Plan<'_> {
1636 type Output = Versions<pretty::Fragment>;
1637 fn print(&self, printer: &Printer<'_>) -> Versions<pretty::Fragment> {
1638 self.print_all_nodes_and_or_root(printer, true, true)
1639 }
1640}
1641
1642impl Plan<'_> {
1643 fn print_all_nodes_and_or_root(
1644 &self,
1645 printer: &Printer<'_>,
1646 print_all_nodes: bool,
1647 print_root: bool,
1648 ) -> Versions<pretty::Fragment> {
1649 enum NodeOrRoot {
1650 Node(Node),
1651 Root,
1652 }
1653
1654 let all_nodes = printer
1655 .use_styles
1656 .keys()
1657 .filter_map(|&use_kind| match use_kind {
1658 Use::Node(node) => Some(node),
1659 _ => None,
1660 })
1661 .map(NodeOrRoot::Node);
1662 let root = [NodeOrRoot::Root].into_iter();
1663 let all_nodes_and_or_root = Some(all_nodes)
1664 .filter(|_| print_all_nodes)
1665 .into_iter()
1666 .flatten()
1667 .chain(Some(root).filter(|_| print_root).into_iter().flatten());
1668
1669 let per_node_versions_with_repeat_count =
1670 all_nodes_and_or_root.map(|node_or_root| -> SmallVec<[_; 1]> {
1671 if let NodeOrRoot::Node(node @ Node::AllCxInterned) = node_or_root {
1674 node.keyword_and_name_prefix().unwrap_err();
1675
1676 return [(CxInterned::print_all(printer), self.versions.len())]
1677 .into_iter()
1678 .collect();
1679 }
1680
1681 self.versions
1682 .iter()
1683 .map(move |version| match node_or_root {
1684 NodeOrRoot::Node(node) => version
1685 .node_defs
1686 .get(&node)
1687 .map(|def| {
1688 def.print(printer)
1689 .insert_name_before_def(Use::Node(node).print_as_def(printer))
1690 })
1691 .unwrap_or_default(),
1692 NodeOrRoot::Root => version.root.print(printer),
1693 })
1694 .dedup_with_count()
1695 .map(|(repeat_count, fragment)| {
1696 (fragment, repeat_count)
1701 })
1702 .collect()
1703 });
1704
1705 if self.versions.len() == 1 && self.versions[0].name.is_empty() {
1707 Versions::Single(pretty::Fragment::new(
1708 per_node_versions_with_repeat_count
1709 .map(|mut versions_with_repeat_count| {
1710 versions_with_repeat_count.pop().unwrap().0
1711 })
1712 .filter(|fragment| !fragment.nodes.is_empty())
1713 .intersperse({
1714 "\n\n".into()
1718 }),
1719 ))
1720 } else {
1721 Versions::Multiple {
1722 version_names: self.versions.iter().map(|v| v.name.clone()).collect(),
1723 per_row_versions_with_repeat_count: per_node_versions_with_repeat_count.collect(),
1724 }
1725 }
1726 }
1727}
1728
1729impl Print for Module {
1730 type Output = pretty::Fragment;
1731 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
1732 if self.exports.is_empty() {
1733 return pretty::Fragment::default();
1734 }
1735
1736 pretty::Fragment::new([
1737 printer.declarative_keyword_style().apply("export").into(),
1738 " ".into(),
1739 pretty::join_comma_sep(
1740 "{",
1741 self.exports
1742 .iter()
1743 .map(|(export_key, exportee)| {
1744 pretty::Fragment::new([
1745 export_key.print(printer),
1746 ": ".into(),
1747 exportee.print(printer),
1748 ])
1749 })
1750 .map(|entry| {
1751 pretty::Fragment::new([pretty::Node::ForceLineSeparation.into(), entry])
1752 }),
1753 "}",
1754 ),
1755 ])
1756 }
1757}
1758
1759impl Print for NodeDef<'_> {
1760 type Output = AttrsAndDef;
1761 fn print(&self, printer: &Printer<'_>) -> AttrsAndDef {
1762 match self {
1763 Self::ModuleDialect(dialect) => dialect.print(printer),
1764 Self::ModuleDebugInfo(debug_info) => debug_info.print(printer),
1765 Self::GlobalVar(gv_decl) => gv_decl.print(printer),
1766 Self::Func(func_decl) => func_decl.print(printer),
1767 }
1768 }
1769}
1770
1771impl Print for ModuleDialect {
1772 type Output = AttrsAndDef;
1773 fn print(&self, printer: &Printer<'_>) -> AttrsAndDef {
1774 let dialect = match self {
1775 Self::Spv(dialect) => dialect.print(printer),
1776 };
1777
1778 AttrsAndDef {
1779 attrs: pretty::Fragment::default(),
1780 def_without_name: pretty::Fragment::new([" = ".into(), dialect]),
1781 }
1782 }
1783}
1784impl Print for spv::Dialect {
1785 type Output = pretty::Fragment;
1786 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
1787 let Self {
1788 version_major,
1789 version_minor,
1790 capabilities,
1791 extensions,
1792 addressing_model,
1793 memory_model,
1794 } = self;
1795
1796 let wk = &spv::spec::Spec::get().well_known;
1797 pretty::Fragment::new([
1798 printer
1799 .demote_style_for_namespace_prefix(printer.spv_base_style())
1800 .apply("spv.")
1801 .into(),
1802 printer.spv_base_style().apply("Module").into(),
1803 pretty::join_comma_sep(
1804 "(",
1805 [pretty::Fragment::new([
1806 printer.pretty_named_argument_prefix("version"),
1807 printer.numeric_literal_style().apply(format!("{version_major}")).into(),
1808 ".".into(),
1809 printer.numeric_literal_style().apply(format!("{version_minor}")).into(),
1810 ])]
1811 .into_iter()
1812 .chain((!extensions.is_empty()).then(|| {
1813 pretty::Fragment::new([
1814 printer.pretty_named_argument_prefix("extensions"),
1815 pretty::join_comma_sep(
1816 "{",
1817 extensions.iter().map(|ext| printer.pretty_string_literal(ext)),
1818 "}",
1819 ),
1820 ])
1821 }))
1822 .chain(
1823 (!capabilities.is_empty()).then(|| {
1825 let cap_imms = |cap| [spv::Imm::Short(wk.Capability, cap)];
1826
1827 let capability_namespace_prefix = printer
1829 .pretty_spv_print_tokens_for_operand({
1830 let mut tokens = spv::print::operand_from_imms(cap_imms(0));
1831 assert!(matches!(
1832 tokens.tokens.pop(),
1833 Some(spv::print::Token::EnumerandName(_))
1834 ));
1835 tokens
1836 });
1837
1838 let mut cap_names = capabilities.iter().map(|&cap| {
1839 printer.pretty_spv_print_tokens_for_operand({
1840 let mut tokens = spv::print::operand_from_imms(cap_imms(cap));
1841 tokens.tokens.drain(..tokens.tokens.len() - 1);
1842 assert!(matches!(tokens.tokens[..], [
1843 spv::print::Token::EnumerandName(_)
1844 ]));
1845 tokens
1846 })
1847 });
1848
1849 pretty::Fragment::new([
1850 capability_namespace_prefix,
1851 if cap_names.len() == 1 {
1852 cap_names.next().unwrap()
1853 } else {
1854 pretty::join_comma_sep("{", cap_names, "}")
1855 },
1856 ])
1857 }),
1858 )
1859 .chain(
1860 (*addressing_model != wk.Logical)
1861 .then(|| printer.pretty_spv_imm(wk.AddressingModel, *addressing_model)),
1862 )
1863 .chain([printer.pretty_spv_imm(wk.MemoryModel, *memory_model)]),
1864 ")",
1865 ),
1866 ])
1867 }
1868}
1869
1870impl Print for ModuleDebugInfo {
1871 type Output = AttrsAndDef;
1872 fn print(&self, printer: &Printer<'_>) -> AttrsAndDef {
1873 let debug_info = match self {
1874 Self::Spv(debug_info) => debug_info.print(printer),
1875 };
1876
1877 AttrsAndDef {
1878 attrs: pretty::Fragment::default(),
1879 def_without_name: pretty::Fragment::new([" = ".into(), debug_info]),
1880 }
1881 }
1882}
1883
1884impl Print for spv::ModuleDebugInfo {
1885 type Output = pretty::Fragment;
1886 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
1887 let Self {
1888 original_generator_magic,
1889 source_languages,
1890 source_extensions,
1891 module_processes,
1892 } = self;
1893
1894 let wk = &spv::spec::Spec::get().well_known;
1895 pretty::Fragment::new([
1896 printer
1897 .demote_style_for_namespace_prefix(
1898 printer.demote_style_for_namespace_prefix(printer.spv_base_style()),
1899 )
1900 .apply("spv.")
1901 .into(),
1902 printer
1903 .demote_style_for_namespace_prefix(printer.spv_base_style())
1904 .apply("Module.")
1905 .into(),
1906 printer.spv_base_style().apply("DebugInfo").into(),
1907 pretty::join_comma_sep(
1908 "(",
1909 [
1910 original_generator_magic.map(|generator_magic| {
1911 let (tool_id, tool_version) =
1912 (generator_magic.get() >> 16, generator_magic.get() as u16);
1913 pretty::Fragment::new([
1914 printer.pretty_named_argument_prefix("generator"),
1915 printer
1916 .demote_style_for_namespace_prefix(printer.spv_base_style())
1917 .apply("spv.")
1918 .into(),
1919 printer.spv_base_style().apply("Tool").into(),
1920 pretty::join_comma_sep(
1921 "(",
1922 [
1923 Some(pretty::Fragment::new([
1924 printer.pretty_named_argument_prefix("id"),
1925 printer
1926 .numeric_literal_style()
1927 .apply(format!("{tool_id}"))
1928 .into(),
1929 ])),
1930 (tool_version != 0).then(|| {
1931 pretty::Fragment::new([
1932 printer.pretty_named_argument_prefix("version"),
1933 printer
1934 .numeric_literal_style()
1935 .apply(format!("{tool_version}"))
1936 .into(),
1937 ])
1938 }),
1939 ]
1940 .into_iter()
1941 .flatten(),
1942 ")",
1943 ),
1944 ])
1945 }),
1946 (!source_languages.is_empty()).then(|| {
1947 pretty::Fragment::new([
1948 printer.pretty_named_argument_prefix("source_languages"),
1949 pretty::join_comma_sep(
1950 "{",
1951 source_languages
1952 .iter()
1953 .map(|(lang, sources)| {
1954 let spv::DebugSources { file_contents } = sources;
1955 pretty::Fragment::new([
1956 printer.pretty_spv_imm(wk.SourceLanguage, lang.lang),
1957 "(".into(),
1958 printer.pretty_named_argument_prefix("version"),
1959 printer
1960 .numeric_literal_style()
1961 .apply(format!("{}", lang.version))
1962 .into(),
1963 "): ".into(),
1964 pretty::join_comma_sep(
1965 "{",
1966 file_contents
1967 .iter()
1968 .map(|(&file, contents)| {
1969 pretty::Fragment::new([
1970 printer.pretty_string_literal(
1971 &printer.cx[file],
1972 ),
1973 pretty::join_space(":", [printer
1974 .pretty_string_literal(contents)]),
1975 ])
1976 })
1977 .map(|entry| {
1978 pretty::Fragment::new([
1979 pretty::Node::ForceLineSeparation
1980 .into(),
1981 entry,
1982 ])
1983 }),
1984 "}",
1985 ),
1986 ])
1987 })
1988 .map(|entry| {
1989 pretty::Fragment::new([
1990 pretty::Node::ForceLineSeparation.into(),
1991 entry,
1992 ])
1993 }),
1994 "}",
1995 ),
1996 ])
1997 }),
1998 (!source_extensions.is_empty()).then(|| {
1999 pretty::Fragment::new([
2000 printer.pretty_named_argument_prefix("source_extensions"),
2001 pretty::join_comma_sep(
2002 "[",
2003 source_extensions
2004 .iter()
2005 .map(|ext| printer.pretty_string_literal(ext)),
2006 "]",
2007 ),
2008 ])
2009 }),
2010 (!module_processes.is_empty()).then(|| {
2011 pretty::Fragment::new([
2012 printer.pretty_named_argument_prefix("module_processes"),
2013 pretty::join_comma_sep(
2014 "[",
2015 module_processes
2016 .iter()
2017 .map(|proc| printer.pretty_string_literal(proc)),
2018 "]",
2019 ),
2020 ])
2021 }),
2022 ]
2023 .into_iter()
2024 .flatten(),
2025 ")",
2026 ),
2027 ])
2028 }
2029}
2030
2031impl Print for ExportKey {
2032 type Output = pretty::Fragment;
2033 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
2034 match self {
2035 &Self::LinkName(name) => printer.pretty_string_literal(&printer.cx[name]),
2036
2037 Self::SpvEntryPoint { imms, interface_global_vars: _ } => {
2040 let wk = &spv::spec::Spec::get().well_known;
2041
2042 printer.pretty_spv_inst(printer.spv_op_style(), wk.OpEntryPoint, imms, [None])
2043 }
2044 }
2045 }
2046}
2047
2048impl Print for Exportee {
2049 type Output = pretty::Fragment;
2050 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
2051 match *self {
2052 Self::GlobalVar(gv) => gv.print(printer),
2053 Self::Func(func) => func.print(printer),
2054 }
2055 }
2056}
2057
2058impl CxInterned {
2059 fn print_all(printer: &Printer<'_>) -> pretty::Fragment {
2060 let fragments = printer
2061 .use_styles
2062 .iter()
2063 .filter_map(|(&use_kind, use_style)| match (use_kind, use_style) {
2064 (Use::CxInterned(interned), UseStyle::Anon { .. } | UseStyle::Named { .. }) => {
2065 Some(interned)
2066 }
2067 _ => None,
2068 })
2069 .map(|interned| {
2070 interned.print(printer).insert_name_before_def(pretty::Fragment::new([
2071 Use::CxInterned(interned).print_as_def(printer),
2072 " = ".into(),
2073 ]))
2074 })
2075 .intersperse({
2076 "\n\n".into()
2080 });
2081
2082 pretty::Fragment::new(fragments)
2083 }
2084}
2085
2086impl Print for CxInterned {
2087 type Output = AttrsAndDef;
2088 fn print(&self, printer: &Printer<'_>) -> AttrsAndDef {
2089 match *self {
2090 Self::Type(ty) => printer.cx[ty].print(printer),
2091 Self::Const(ct) => printer.cx[ct].print(printer),
2092 }
2093 }
2094}
2095
2096impl Print for AttrSet {
2097 type Output = pretty::Fragment;
2098 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
2099 let AttrSetDef { attrs } = &printer.cx[*self];
2100
2101 let spv_name_to_hide = printer.attrs_with_spv_name_in_use.get(self).copied();
2104
2105 pretty::Fragment::new(
2106 attrs
2107 .iter()
2108 .filter(|attr| match attr {
2109 Attr::SpvAnnotation(spv_inst) => Some(spv_inst) != spv_name_to_hide,
2110 _ => true,
2111 })
2112 .map(|attr| attr.print(printer))
2113 .flat_map(|entry| [entry, pretty::Node::ForceLineSeparation.into()]),
2114 )
2115 }
2116}
2117
2118impl Print for Attr {
2119 type Output = pretty::Fragment;
2120 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
2121 let non_comment_attr = match self {
2122 Attr::Diagnostics(diags) => {
2123 return pretty::Fragment::new(
2124 diags
2125 .0
2126 .iter()
2127 .map(|diag| {
2128 let Diag { level, message } = diag;
2129
2130 let (icon, icon_color) = match level {
2134 DiagLevel::Bug(_) => ("BUG", pretty::palettes::simple::MAGENTA),
2135 DiagLevel::Error => ("ERR", pretty::palettes::simple::RED),
2136 DiagLevel::Warning => ("WARN", pretty::palettes::simple::YELLOW),
2137 };
2138
2139 let grayish =
2140 |[r, g, b]: [u8; 3]| [(r / 2) + 64, (g / 2) + 64, (b / 2) + 64];
2141 let comment_style = pretty::Styles::color(grayish(icon_color));
2142
2143 let bug_location_prefix = match level {
2145 DiagLevel::Bug(location) => {
2146 let location = location.to_string();
2147 let location = match location.rsplit_once("/src/") {
2148 Some((_path_prefix, intra_src)) => intra_src,
2149 None => &location,
2150 };
2151 comment_style.apply(format!("[{location}] ")).into()
2152 }
2153 DiagLevel::Error | DiagLevel::Warning => {
2154 pretty::Fragment::default()
2155 }
2156 };
2157
2158 let mut printed_message = message.print(printer);
2159
2160 for node in &mut printed_message.nodes {
2164 if let pretty::Node::Text(style @ None, _) = node {
2165 *style = Some(comment_style);
2166 }
2167 }
2168
2169 pretty::Fragment::new([
2173 comment_style.apply("/*"),
2174 pretty::Node::BreakingOnlySpace,
2175 pretty::Node::InlineOrIndentedBlock(vec![pretty::Fragment::new([
2176 pretty::Styles {
2177 thickness: Some(3),
2178
2179 subscript: true,
2182 size: Some(2),
2183
2184 ..pretty::Styles::color(icon_color)
2185 }
2186 .apply(icon)
2187 .into(),
2188 " ".into(),
2189 bug_location_prefix,
2190 printed_message,
2191 ])]),
2192 pretty::Node::BreakingOnlySpace,
2193 comment_style.apply("*/"),
2194 ])
2195 })
2196 .intersperse(pretty::Node::ForceLineSeparation.into()),
2197 );
2198 }
2199
2200 Attr::QPtr(attr) => {
2201 let (name, params_inputs) = match attr {
2202 QPtrAttr::ToSpvPtrInput { input_idx, pointee } => (
2203 "to_spv_ptr_input",
2204 pretty::Fragment::new([pretty::join_comma_sep(
2205 "(",
2206 [
2207 pretty::Fragment::new([
2208 printer.pretty_named_argument_prefix("input_idx"),
2209 printer
2210 .numeric_literal_style()
2211 .apply(format!("{input_idx}"))
2212 .into(),
2213 ]),
2214 pointee.0.print(printer),
2215 ],
2216 ")",
2217 )]),
2218 ),
2219
2220 QPtrAttr::FromSpvPtrOutput { addr_space, pointee } => (
2221 "from_spv_ptr_output",
2222 pretty::join_comma_sep(
2223 "(",
2224 [addr_space.0.print(printer), pointee.0.print(printer)],
2225 ")",
2226 ),
2227 ),
2228
2229 QPtrAttr::Usage(usage) => {
2230 ("usage", pretty::join_comma_sep("(", [usage.0.print(printer)], ")"))
2231 }
2232 };
2233 pretty::Fragment::new([
2234 printer
2235 .demote_style_for_namespace_prefix(printer.attr_style())
2236 .apply("qptr.")
2237 .into(),
2238 printer.attr_style().apply(name).into(),
2239 params_inputs,
2240 ])
2241 }
2242
2243 Attr::SpvAnnotation(spv::Inst { opcode, imms }) => {
2244 let wk = &spv::spec::Spec::get().well_known;
2245
2246 if [wk.OpDecorate, wk.OpDecorateString, wk.OpExecutionMode].contains(opcode) {
2248 printer.pretty_spv_operand_from_imms(imms.iter().copied())
2249 } else if *opcode == wk.OpName {
2250 pretty::Fragment::new([
2254 printer.attr_style().apply("name = ").into(),
2255 printer.pretty_spv_operand_from_imms(imms.iter().copied()),
2256 ])
2257 } else {
2258 printer.pretty_spv_inst(printer.attr_style(), *opcode, imms, [None])
2259 }
2260 }
2261 &Attr::SpvDebugLine { file_path, line, col } => {
2262 let col = col + 1;
2268
2269 let file_path = &printer.cx[file_path.0];
2272 let comment = if file_path.chars().all(|c| c.is_ascii_graphic() && c != ':') {
2273 format!("// at {file_path}:{line}:{col}")
2274 } else {
2275 format!("// at {file_path:?}:{line}:{col}")
2276 };
2277 return printer.comment_style().apply(comment).into();
2278 }
2279 &Attr::SpvBitflagsOperand(imm) => printer.pretty_spv_operand_from_imms([imm]),
2280 };
2281 pretty::Fragment::new([
2282 printer.attr_style().apply("#[").into(),
2283 non_comment_attr,
2284 printer.attr_style().apply("]").into(),
2285 ])
2286 }
2287}
2288
2289impl Print for Vec<DiagMsgPart> {
2290 type Output = pretty::Fragment;
2291 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
2292 pretty::Fragment::new(self.iter().map(|part| match part {
2293 DiagMsgPart::Plain(text) => pretty::Node::Text(None, text.clone()).into(),
2294 DiagMsgPart::Attrs(attrs) => attrs.print(printer),
2295 DiagMsgPart::Type(ty) => ty.print(printer),
2296 DiagMsgPart::Const(ct) => ct.print(printer),
2297 DiagMsgPart::QPtrUsage(usage) => usage.print(printer),
2298 }))
2299 }
2300}
2301
2302impl Print for QPtrUsage {
2303 type Output = pretty::Fragment;
2304 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
2305 match self {
2306 QPtrUsage::Handles(qptr::shapes::Handle::Opaque(ty)) => ty.print(printer),
2307 QPtrUsage::Handles(qptr::shapes::Handle::Buffer(_, data_usage)) => {
2308 pretty::Fragment::new([
2309 printer.declarative_keyword_style().apply("buffer_data").into(),
2310 pretty::join_comma_sep("(", [data_usage.print(printer)], ")"),
2311 ])
2312 }
2313 QPtrUsage::Memory(usage) => usage.print(printer),
2314 }
2315 }
2316}
2317
2318impl Print for QPtrMemUsage {
2319 type Output = pretty::Fragment;
2320 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
2321 let num_lit = |x: u32| printer.numeric_literal_style().apply(format!("{x}")).into();
2323
2324 match &self.kind {
2325 QPtrMemUsageKind::Unused => "_".into(),
2326 &QPtrMemUsageKind::StrictlyTyped(ty) | &QPtrMemUsageKind::DirectAccess(ty) => {
2328 ty.print(printer)
2329 }
2330 QPtrMemUsageKind::OffsetBase(entries) => pretty::join_comma_sep(
2331 "{",
2332 entries
2333 .iter()
2334 .map(|(&offset, sub_usage)| {
2335 pretty::Fragment::new([
2336 num_lit(offset),
2337 "..".into(),
2338 sub_usage
2339 .max_size
2340 .and_then(|max_size| offset.checked_add(max_size))
2341 .map(num_lit)
2342 .unwrap_or_default(),
2343 " => ".into(),
2344 sub_usage.print(printer),
2345 ])
2346 })
2347 .map(|entry| {
2348 pretty::Fragment::new([pretty::Node::ForceLineSeparation.into(), entry])
2349 }),
2350 "}",
2351 ),
2352 QPtrMemUsageKind::DynOffsetBase { element, stride } => pretty::Fragment::new([
2353 "(".into(),
2354 num_lit(0),
2355 "..".into(),
2356 self.max_size
2357 .map(|max_size| max_size / stride.get())
2358 .map(num_lit)
2359 .unwrap_or_default(),
2360 ") × ".into(),
2361 num_lit(stride.get()),
2362 " => ".into(),
2363 element.print(printer),
2364 ]),
2365 }
2366 }
2367}
2368
2369impl Print for TypeDef {
2370 type Output = AttrsAndDef;
2371 fn print(&self, printer: &Printer<'_>) -> AttrsAndDef {
2372 let Self { attrs, kind } = self;
2373
2374 let wk = &spv::spec::Spec::get().well_known;
2375
2376 let kw = |kw| printer.declarative_keyword_style().apply(kw).into();
2378 let compact_def = if let &TypeKind::SpvInst {
2379 spv_inst: spv::Inst { opcode, ref imms },
2380 ref type_and_const_inputs,
2381 } = kind
2382 {
2383 if opcode == wk.OpTypeBool {
2384 Some(kw("bool".into()))
2385 } else if opcode == wk.OpTypeInt {
2386 let (width, signed) = match imms[..] {
2387 [spv::Imm::Short(_, width), spv::Imm::Short(_, signedness)] => {
2388 (width, signedness != 0)
2389 }
2390 _ => unreachable!(),
2391 };
2392
2393 Some(if signed { kw(format!("s{width}")) } else { kw(format!("u{width}")) })
2394 } else if opcode == wk.OpTypeFloat {
2395 let width = match imms[..] {
2396 [spv::Imm::Short(_, width)] => width,
2397 _ => unreachable!(),
2398 };
2399
2400 Some(kw(format!("f{width}")))
2401 } else if opcode == wk.OpTypeVector {
2402 let (elem_ty, elem_count) = match (&imms[..], &type_and_const_inputs[..]) {
2403 (&[spv::Imm::Short(_, elem_count)], &[TypeOrConst::Type(elem_ty)]) => {
2404 (elem_ty, elem_count)
2405 }
2406 _ => unreachable!(),
2407 };
2408
2409 Some(pretty::Fragment::new([
2410 elem_ty.print(printer),
2411 "×".into(),
2412 printer.numeric_literal_style().apply(format!("{elem_count}")).into(),
2413 ]))
2414 } else {
2415 None
2416 }
2417 } else {
2418 None
2419 };
2420
2421 AttrsAndDef {
2422 attrs: attrs.print(printer),
2423 def_without_name: if let Some(def) = compact_def {
2424 def
2425 } else {
2426 match kind {
2427 TypeKind::QPtr => printer.declarative_keyword_style().apply("qptr").into(),
2429
2430 TypeKind::SpvInst { spv_inst, type_and_const_inputs } => printer
2431 .pretty_spv_inst(
2432 printer.spv_op_style(),
2433 spv_inst.opcode,
2434 &spv_inst.imms,
2435 type_and_const_inputs.iter().map(|&ty_or_ct| match ty_or_ct {
2436 TypeOrConst::Type(ty) => ty.print(printer),
2437 TypeOrConst::Const(ct) => ct.print(printer),
2438 }),
2439 ),
2440 TypeKind::SpvStringLiteralForExtInst => pretty::Fragment::new([
2441 printer.error_style().apply("type_of").into(),
2442 "(".into(),
2443 printer.pretty_spv_opcode(printer.spv_op_style(), wk.OpString),
2444 ")".into(),
2445 ]),
2446 }
2447 },
2448 }
2449 }
2450}
2451
2452impl Print for ConstDef {
2453 type Output = AttrsAndDef;
2454 fn print(&self, printer: &Printer<'_>) -> AttrsAndDef {
2455 let Self { attrs, ty, kind } = self;
2456
2457 let wk = &spv::spec::Spec::get().well_known;
2458
2459 let kw = |kw| printer.declarative_keyword_style().apply(kw).into();
2460 let literal_ty_suffix = |ty| {
2461 pretty::Styles {
2462 color_opacity: Some(0.4),
2464 subscript: true,
2465 ..printer.declarative_keyword_style()
2466 }
2467 .apply(ty)
2468 };
2469 let compact_def = if let ConstKind::SpvInst { spv_inst_and_const_inputs } = kind {
2470 let (spv_inst, _const_inputs) = &**spv_inst_and_const_inputs;
2471 let &spv::Inst { opcode, ref imms } = spv_inst;
2472
2473 if opcode == wk.OpConstantFalse {
2474 Some(kw("false"))
2475 } else if opcode == wk.OpConstantTrue {
2476 Some(kw("true"))
2477 } else if opcode == wk.OpConstant {
2478 let raw_bits = match imms[..] {
2481 [spv::Imm::Short(_, x)] => Some(u64::from(x)),
2482 [spv::Imm::LongStart(_, lo), spv::Imm::LongCont(_, hi)] => {
2483 Some(u64::from(lo) | (u64::from(hi) << 32))
2484 }
2485 _ => None,
2486 };
2487
2488 if let (
2489 Some(raw_bits),
2490 &TypeKind::SpvInst {
2491 spv_inst: spv::Inst { opcode: ty_opcode, imms: ref ty_imms },
2492 ..
2493 },
2494 ) = (raw_bits, &printer.cx[*ty].kind)
2495 {
2496 if ty_opcode == wk.OpTypeInt {
2497 let (width, signed) = match ty_imms[..] {
2498 [spv::Imm::Short(_, width), spv::Imm::Short(_, signedness)] => {
2499 (width, signedness != 0)
2500 }
2501 _ => unreachable!(),
2502 };
2503
2504 if width <= 64 {
2505 let (printed_value, ty) = if signed {
2506 let sext_raw_bits =
2507 (raw_bits as u128 as i128) << (128 - width) >> (128 - width);
2508 (format!("{sext_raw_bits}"), format!("s{width}"))
2509 } else {
2510 (format!("{raw_bits}"), format!("u{width}"))
2511 };
2512 Some(pretty::Fragment::new([
2513 printer.numeric_literal_style().apply(printed_value),
2514 literal_ty_suffix(ty),
2515 ]))
2516 } else {
2517 None
2518 }
2519 } else if ty_opcode == wk.OpTypeFloat {
2520 let width = match ty_imms[..] {
2521 [spv::Imm::Short(_, width)] => width,
2522 _ => unreachable!(),
2523 };
2524
2525 fn bitwise_roundtrip_float_print<
2529 BITS: Copy + PartialEq,
2530 FLOAT: std::fmt::Debug + std::str::FromStr,
2531 >(
2532 bits: BITS,
2533 float_from_bits: impl FnOnce(BITS) -> FLOAT,
2534 float_to_bits: impl FnOnce(FLOAT) -> BITS,
2535 ) -> Option<String> {
2536 let float = float_from_bits(bits);
2537 Some(format!("{float:?}")).filter(|s| {
2538 s.parse::<FLOAT>()
2539 .map(float_to_bits)
2540 .map_or(false, |roundtrip_bits| roundtrip_bits == bits)
2541 })
2542 }
2543
2544 let printed_value = match width {
2545 32 => bitwise_roundtrip_float_print(
2546 raw_bits as u32,
2547 f32::from_bits,
2548 f32::to_bits,
2549 ),
2550 64 => bitwise_roundtrip_float_print(
2551 raw_bits,
2552 f64::from_bits,
2553 f64::to_bits,
2554 ),
2555 _ => None,
2556 };
2557 printed_value.map(|s| {
2558 pretty::Fragment::new([
2559 printer.numeric_literal_style().apply(s),
2560 literal_ty_suffix(format!("f{width}")),
2561 ])
2562 })
2563 } else {
2564 None
2565 }
2566 } else {
2567 None
2568 }
2569 } else {
2570 None
2571 }
2572 } else {
2573 None
2574 };
2575
2576 AttrsAndDef {
2577 attrs: attrs.print(printer),
2578 def_without_name: compact_def.unwrap_or_else(|| match kind {
2579 &ConstKind::PtrToGlobalVar(gv) => {
2580 pretty::Fragment::new(["&".into(), gv.print(printer)])
2581 }
2582 ConstKind::SpvInst { spv_inst_and_const_inputs } => {
2583 let (spv_inst, const_inputs) = &**spv_inst_and_const_inputs;
2584 pretty::Fragment::new([
2585 printer.pretty_spv_inst(
2586 printer.spv_op_style(),
2587 spv_inst.opcode,
2588 &spv_inst.imms,
2589 const_inputs.iter().map(|ct| ct.print(printer)),
2590 ),
2591 printer.pretty_type_ascription_suffix(*ty),
2592 ])
2593 }
2594 &ConstKind::SpvStringLiteralForExtInst(s) => pretty::Fragment::new([
2595 printer.pretty_spv_opcode(printer.spv_op_style(), wk.OpString),
2596 "(".into(),
2597 printer.pretty_string_literal(&printer.cx[s]),
2598 ")".into(),
2599 ]),
2600 }),
2601 }
2602 }
2603}
2604
2605impl Print for Import {
2606 type Output = pretty::Fragment;
2607 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
2608 match self {
2609 &Self::LinkName(name) => pretty::Fragment::new([
2610 printer.declarative_keyword_style().apply("import").into(),
2611 " ".into(),
2612 printer.pretty_string_literal(&printer.cx[name]),
2613 ]),
2614 }
2615 }
2616}
2617
2618impl Print for GlobalVarDecl {
2619 type Output = AttrsAndDef;
2620 fn print(&self, printer: &Printer<'_>) -> AttrsAndDef {
2621 let Self { attrs, type_of_ptr_to, shape, addr_space, def } = self;
2622
2623 let wk = &spv::spec::Spec::get().well_known;
2624
2625 let type_ascription_suffix = match &printer.cx[*type_of_ptr_to].kind {
2628 TypeKind::QPtr if shape.is_some() => match shape.unwrap() {
2629 qptr::shapes::GlobalVarShape::Handles { handle, fixed_count } => {
2630 let handle = match handle {
2631 qptr::shapes::Handle::Opaque(ty) => ty.print(printer),
2632 qptr::shapes::Handle::Buffer(addr_space, buf) => pretty::Fragment::new([
2633 printer.declarative_keyword_style().apply("buffer").into(),
2634 pretty::join_comma_sep(
2635 "(",
2636 [
2637 addr_space.print(printer),
2638 pretty::Fragment::new([
2639 printer.pretty_named_argument_prefix("size"),
2640 pretty::Fragment::new(
2641 Some(buf.fixed_base.size)
2642 .filter(|&base_size| {
2643 base_size > 0 || buf.dyn_unit_stride.is_none()
2644 })
2645 .map(|base_size| {
2646 printer
2647 .numeric_literal_style()
2648 .apply(base_size.to_string())
2649 .into()
2650 })
2651 .into_iter()
2652 .chain(buf.dyn_unit_stride.map(|stride| {
2653 pretty::Fragment::new([
2654 "N × ".into(),
2655 printer
2656 .numeric_literal_style()
2657 .apply(stride.to_string()),
2658 ])
2659 }))
2660 .intersperse_with(|| " + ".into()),
2661 ),
2662 ]),
2663 pretty::Fragment::new([
2664 printer.pretty_named_argument_prefix("align"),
2665 printer
2666 .numeric_literal_style()
2667 .apply(buf.fixed_base.align.to_string())
2668 .into(),
2669 ]),
2670 ],
2671 ")",
2672 ),
2673 ]),
2674 };
2675
2676 let handles = if fixed_count.map_or(0, |c| c.get()) == 1 {
2677 handle
2678 } else {
2679 pretty::Fragment::new([
2680 "[".into(),
2681 fixed_count
2682 .map(|count| {
2683 pretty::Fragment::new([
2684 printer.numeric_literal_style().apply(count.to_string()),
2685 " × ".into(),
2686 ])
2687 })
2688 .unwrap_or_default(),
2689 handle,
2690 "]".into(),
2691 ])
2692 };
2693 pretty::join_space(":", [handles])
2694 }
2695 qptr::shapes::GlobalVarShape::UntypedData(mem_layout) => pretty::Fragment::new([
2696 " ".into(),
2697 printer.declarative_keyword_style().apply("layout").into(),
2698 pretty::join_comma_sep(
2699 "(",
2700 [
2701 pretty::Fragment::new([
2702 printer.pretty_named_argument_prefix("size"),
2703 printer
2704 .numeric_literal_style()
2705 .apply(mem_layout.size.to_string())
2706 .into(),
2707 ]),
2708 pretty::Fragment::new([
2709 printer.pretty_named_argument_prefix("align"),
2710 printer
2711 .numeric_literal_style()
2712 .apply(mem_layout.align.to_string())
2713 .into(),
2714 ]),
2715 ],
2716 ")",
2717 ),
2718 ]),
2719 qptr::shapes::GlobalVarShape::TypedInterface(ty) => {
2720 printer.pretty_type_ascription_suffix(ty)
2721 }
2722 },
2723 TypeKind::SpvInst { spv_inst, type_and_const_inputs }
2724 if spv_inst.opcode == wk.OpTypePointer =>
2725 {
2726 match type_and_const_inputs[..] {
2727 [TypeOrConst::Type(ty)] => printer.pretty_type_ascription_suffix(ty),
2728 _ => unreachable!(),
2729 }
2730 }
2731 _ => pretty::Fragment::new([
2732 ": ".into(),
2733 printer.error_style().apply("pointee_type_of").into(),
2734 "(".into(),
2735 type_of_ptr_to.print(printer),
2736 ")".into(),
2737 ]),
2738 };
2739 let addr_space_suffix = match addr_space {
2740 AddrSpace::Handles => pretty::Fragment::default(),
2741 AddrSpace::SpvStorageClass(_) => {
2742 pretty::Fragment::new([" in ".into(), addr_space.print(printer)])
2743 }
2744 };
2745 let header = pretty::Fragment::new([addr_space_suffix, type_ascription_suffix]);
2746
2747 let maybe_rhs = match def {
2748 DeclDef::Imported(import) => Some(import.print(printer)),
2749 DeclDef::Present(GlobalVarDefBody { initializer }) => {
2750 initializer.map(|initializer| initializer.print(printer))
2753 }
2754 };
2755 let body = maybe_rhs.map(|rhs| pretty::Fragment::new(["= ".into(), rhs]));
2756
2757 let def_without_name = pretty::Fragment::new([header, pretty::join_space("", body)]);
2758
2759 AttrsAndDef { attrs: attrs.print(printer), def_without_name }
2760 }
2761}
2762
2763impl Print for AddrSpace {
2764 type Output = pretty::Fragment;
2765 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
2766 match *self {
2767 AddrSpace::Handles => printer.declarative_keyword_style().apply("handles").into(),
2768 AddrSpace::SpvStorageClass(sc) => {
2769 let wk = &spv::spec::Spec::get().well_known;
2770 printer.pretty_spv_imm(wk.StorageClass, sc)
2771 }
2772 }
2773 }
2774}
2775
2776impl Print for FuncDecl {
2777 type Output = AttrsAndDef;
2778 fn print(&self, printer: &Printer<'_>) -> AttrsAndDef {
2779 let Self { attrs, ret_type, params, def } = self;
2780
2781 let sig = pretty::Fragment::new([
2782 pretty::join_comma_sep(
2783 "(",
2784 params.iter().enumerate().map(|(i, param)| {
2785 let param_name = match def {
2786 DeclDef::Imported(_) => "_".into(),
2787 DeclDef::Present(def) => Value::ControlRegionInput {
2788 region: def.body,
2789 input_idx: i.try_into().unwrap(),
2790 }
2791 .print_as_def(printer),
2792 };
2793 param.print(printer).insert_name_before_def(param_name)
2794 }),
2795 ")",
2796 ),
2797 " -> ".into(),
2798 ret_type.print(printer),
2799 ]);
2800
2801 let def_without_name = match def {
2802 DeclDef::Imported(import) => {
2803 pretty::Fragment::new([sig, " = ".into(), import.print(printer)])
2804 }
2805
2806 DeclDef::Present(def) => pretty::Fragment::new([
2808 sig,
2809 " {".into(),
2810 pretty::Node::IndentedBlock(match &def.unstructured_cfg {
2811 None => vec![def.at_body().print(printer)],
2812 Some(cfg) => cfg
2813 .rev_post_order(def)
2814 .map(|region| {
2815 let label = Use::ControlRegionLabel(region);
2816 let label_header = if printer.use_styles.contains_key(&label) {
2817 let inputs = &def.at(region).def().inputs;
2818 let label_inputs = if !inputs.is_empty() {
2819 pretty::join_comma_sep(
2820 "(",
2821 inputs.iter().enumerate().map(|(input_idx, input)| {
2822 input.print(printer).insert_name_before_def(
2823 Value::ControlRegionInput {
2824 region,
2825 input_idx: input_idx.try_into().unwrap(),
2826 }
2827 .print_as_def(printer),
2828 )
2829 }),
2830 ")",
2831 )
2832 } else {
2833 pretty::Fragment::default()
2834 };
2835
2836 pretty::Fragment::new([
2839 pretty::Node::ForceLineSeparation.into(),
2840 label.print_as_def(printer),
2841 label_inputs,
2842 ":".into(),
2843 pretty::Node::ForceLineSeparation.into(),
2844 ])
2845 } else {
2846 pretty::Fragment::default()
2847 };
2848
2849 pretty::Fragment::new([
2850 label_header,
2851 pretty::Node::IndentedBlock(vec![def.at(region).print(printer)])
2852 .into(),
2853 cfg.control_inst_on_exit_from[region].print(printer),
2854 ])
2855 })
2856 .intersperse({
2857 "\n\n".into()
2861 })
2862 .collect(),
2863 })
2864 .into(),
2865 "}".into(),
2866 ]),
2867 };
2868
2869 AttrsAndDef { attrs: attrs.print(printer), def_without_name }
2870 }
2871}
2872
2873impl Print for FuncParam {
2874 type Output = AttrsAndDef;
2875 fn print(&self, printer: &Printer<'_>) -> AttrsAndDef {
2876 let Self { attrs, ty } = *self;
2877
2878 AttrsAndDef {
2879 attrs: attrs.print(printer),
2880 def_without_name: printer.pretty_type_ascription_suffix(ty),
2881 }
2882 }
2883}
2884
2885impl Print for FuncAt<'_, ControlRegion> {
2886 type Output = pretty::Fragment;
2887 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
2888 let ControlRegionDef { inputs: _, children, outputs } = self.def();
2889
2890 let outputs_footer = if !outputs.is_empty() {
2893 let mut outputs = outputs.iter().map(|v| v.print(printer));
2894 let outputs = if outputs.len() == 1 {
2895 outputs.next().unwrap()
2896 } else {
2897 pretty::join_comma_sep("(", outputs, ")")
2898 };
2899 pretty::Fragment::new([pretty::Node::ForceLineSeparation.into(), outputs])
2900 } else {
2901 pretty::Fragment::default()
2902 };
2903
2904 pretty::Fragment::new([
2905 Use::AlignmentAnchorForControlRegion(self.position).print_as_def(printer),
2906 self.at(*children).into_iter().print(printer),
2907 outputs_footer,
2908 ])
2909 }
2910}
2911
2912impl Print for FuncAt<'_, EntityListIter<ControlNode>> {
2913 type Output = pretty::Fragment;
2914 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
2915 pretty::Fragment::new(
2916 self.map(|func_at_control_node| func_at_control_node.print(printer))
2917 .intersperse(pretty::Node::ForceLineSeparation.into()),
2918 )
2919 }
2920}
2921
2922impl Print for FuncAt<'_, ControlNode> {
2923 type Output = pretty::Fragment;
2924 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
2925 let control_node = self.position;
2926 let ControlNodeDef { kind, outputs } = self.def();
2927
2928 let outputs_header = if !outputs.is_empty() {
2929 let mut outputs = outputs.iter().enumerate().map(|(output_idx, output)| {
2930 output.print(printer).insert_name_before_def(
2931 Value::ControlNodeOutput {
2932 control_node,
2933 output_idx: output_idx.try_into().unwrap(),
2934 }
2935 .print_as_def(printer),
2936 )
2937 });
2938 let outputs_lhs = if outputs.len() == 1 {
2939 outputs.next().unwrap()
2940 } else {
2941 pretty::join_comma_sep("(", outputs, ")")
2942 };
2943 pretty::Fragment::new([outputs_lhs, " = ".into()])
2944 } else {
2945 pretty::Fragment::default()
2946 };
2947
2948 let kw_style = printer.imperative_keyword_style();
2951 let kw = |kw| kw_style.apply(kw).into();
2952 let control_node_body = match kind {
2953 ControlNodeKind::Block { insts } => {
2954 assert!(outputs.is_empty());
2955
2956 pretty::Fragment::new(
2957 self.at(*insts)
2958 .into_iter()
2959 .map(|func_at_inst| func_at_inst.print(printer))
2960 .flat_map(|entry| [pretty::Node::ForceLineSeparation.into(), entry]),
2961 )
2962 }
2963 ControlNodeKind::Select { kind, scrutinee, cases } => kind
2964 .print_with_scrutinee_and_cases(
2965 printer,
2966 kw_style,
2967 *scrutinee,
2968 cases.iter().map(|&case| self.at(case).print(printer)),
2969 ),
2970 ControlNodeKind::Loop { initial_inputs, body, repeat_condition } => {
2971 assert!(outputs.is_empty());
2972
2973 let inputs = &self.at(*body).def().inputs;
2974 assert_eq!(initial_inputs.len(), inputs.len());
2975
2976 let (inputs_header, body_suffix) = if !inputs.is_empty() {
2995 let input_decls_and_uses =
2996 inputs.iter().enumerate().map(|(input_idx, input)| {
2997 (input, Value::ControlRegionInput {
2998 region: *body,
2999 input_idx: input_idx.try_into().unwrap(),
3000 })
3001 });
3002 (
3003 pretty::join_comma_sep(
3004 "(",
3005 input_decls_and_uses.clone().zip(initial_inputs).map(
3006 |((input_decl, input_use), initial)| {
3007 pretty::Fragment::new([
3008 input_decl.print(printer).insert_name_before_def(
3009 input_use.print_as_def(printer),
3010 ),
3011 " <- ".into(),
3012 initial.print(printer),
3013 ])
3014 },
3015 ),
3016 ")",
3017 ),
3018 pretty::Fragment::new([" -> ".into(), {
3019 let mut input_dests =
3020 input_decls_and_uses.map(|(_, input_use)| input_use.print(printer));
3021 if input_dests.len() == 1 {
3022 input_dests.next().unwrap()
3023 } else {
3024 pretty::join_comma_sep("(", input_dests, ")")
3025 }
3026 }]),
3027 )
3028 } else {
3029 (pretty::Fragment::default(), pretty::Fragment::default())
3030 };
3031
3032 pretty::Fragment::new([
3034 kw("loop"),
3035 inputs_header,
3036 " {".into(),
3037 pretty::Node::IndentedBlock(vec![pretty::Fragment::new([
3038 self.at(*body).print(printer),
3039 body_suffix,
3040 ])])
3041 .into(),
3042 "} ".into(),
3043 kw("while"),
3044 " ".into(),
3045 repeat_condition.print(printer),
3046 ])
3047 }
3048 ControlNodeKind::ExitInvocation {
3049 kind: cfg::ExitInvocationKind::SpvInst(spv::Inst { opcode, imms }),
3050 inputs,
3051 } => printer.pretty_spv_inst(
3052 kw_style,
3053 *opcode,
3054 imms,
3055 inputs.iter().map(|v| v.print(printer)),
3056 ),
3057 };
3058 pretty::Fragment::new([
3059 Use::AlignmentAnchorForControlNode(self.position).print_as_def(printer),
3060 outputs_header,
3061 control_node_body,
3062 ])
3063 }
3064}
3065
3066impl Print for ControlRegionInputDecl {
3067 type Output = AttrsAndDef;
3068 fn print(&self, printer: &Printer<'_>) -> AttrsAndDef {
3069 let Self { attrs, ty } = *self;
3070
3071 AttrsAndDef {
3072 attrs: attrs.print(printer),
3073 def_without_name: printer.pretty_type_ascription_suffix(ty),
3074 }
3075 }
3076}
3077
3078impl Print for ControlNodeOutputDecl {
3079 type Output = AttrsAndDef;
3080 fn print(&self, printer: &Printer<'_>) -> AttrsAndDef {
3081 let Self { attrs, ty } = *self;
3082
3083 AttrsAndDef {
3084 attrs: attrs.print(printer),
3085 def_without_name: printer.pretty_type_ascription_suffix(ty),
3086 }
3087 }
3088}
3089
3090impl Print for FuncAt<'_, DataInst> {
3091 type Output = pretty::Fragment;
3092 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
3093 let DataInstDef { attrs, form, inputs } = self.def();
3094
3095 let attrs = attrs.print(printer);
3096
3097 let DataInstFormDef { kind, output_type } = &printer.cx[*form];
3098
3099 let mut output_use_to_print_as_lhs =
3100 output_type.map(|_| Use::DataInstOutput(self.position));
3101
3102 let mut output_type_to_print = *output_type;
3103
3104 let def_without_type = match kind {
3105 &DataInstKind::FuncCall(func) => pretty::Fragment::new([
3106 printer.declarative_keyword_style().apply("call").into(),
3107 " ".into(),
3108 func.print(printer),
3109 pretty::join_comma_sep("(", inputs.iter().map(|v| v.print(printer)), ")"),
3110 ]),
3111
3112 DataInstKind::QPtr(op) => {
3113 let (qptr_input, extra_inputs) = match op {
3114 QPtrOp::FuncLocalVar(_) => (None, &inputs[..]),
3116 _ => (Some(inputs[0]), &inputs[1..]),
3117 };
3118 let (name, extra_inputs): (_, SmallVec<[_; 1]>) = match op {
3119 QPtrOp::FuncLocalVar(mem_layout) => {
3120 assert!(extra_inputs.len() <= 1);
3121 (
3122 "func_local_var",
3123 [
3124 pretty::Fragment::new([
3125 printer.pretty_named_argument_prefix("size"),
3126 printer
3127 .numeric_literal_style()
3128 .apply(mem_layout.size.to_string())
3129 .into(),
3130 ]),
3131 pretty::Fragment::new([
3132 printer.pretty_named_argument_prefix("align"),
3133 printer
3134 .numeric_literal_style()
3135 .apply(mem_layout.align.to_string())
3136 .into(),
3137 ]),
3138 ]
3139 .into_iter()
3140 .chain(extra_inputs.first().map(|&init| {
3141 pretty::Fragment::new([
3142 printer.pretty_named_argument_prefix("initializer"),
3143 init.print(printer),
3144 ])
3145 }))
3146 .collect(),
3147 )
3148 }
3149
3150 QPtrOp::HandleArrayIndex => {
3151 assert_eq!(extra_inputs.len(), 1);
3152 (
3153 "handle_array_index",
3154 [extra_inputs[0].print(printer)].into_iter().collect(),
3155 )
3156 }
3157 QPtrOp::BufferData => {
3158 assert_eq!(extra_inputs.len(), 0);
3159 ("buffer_data", [].into_iter().collect())
3160 }
3161 &QPtrOp::BufferDynLen { fixed_base_size, dyn_unit_stride } => {
3162 assert_eq!(extra_inputs.len(), 0);
3163
3164 (
3167 "buffer_dyn_len",
3168 [
3169 pretty::Fragment::new([
3170 printer.pretty_named_argument_prefix("fixed_base_size"),
3171 printer
3172 .numeric_literal_style()
3173 .apply(fixed_base_size.to_string())
3174 .into(),
3175 ]),
3176 pretty::Fragment::new([
3177 printer.pretty_named_argument_prefix("dyn_unit_stride"),
3178 printer
3179 .numeric_literal_style()
3180 .apply(dyn_unit_stride.to_string())
3181 .into(),
3182 ]),
3183 ]
3184 .into_iter()
3185 .collect(),
3186 )
3187 }
3188
3189 QPtrOp::Offset(offset) => {
3190 assert_eq!(extra_inputs.len(), 0);
3191 (
3192 "offset",
3193 [printer.numeric_literal_style().apply(format!("{offset}")).into()]
3194 .into_iter()
3195 .collect(),
3196 )
3197 }
3198 &QPtrOp::DynOffset { stride, index_bounds: _ } => {
3199 assert_eq!(extra_inputs.len(), 1);
3200 (
3201 "dyn_offset",
3202 [pretty::Fragment::new([
3203 extra_inputs[0].print(printer),
3204 " × ".into(),
3205 printer.numeric_literal_style().apply(format!("{stride}")).into(),
3206 ])]
3207 .into_iter()
3208 .collect(),
3209 )
3210 }
3211
3212 QPtrOp::Load => {
3213 assert_eq!(extra_inputs.len(), 0);
3214 ("load", [].into_iter().collect())
3215 }
3216 QPtrOp::Store => {
3217 assert_eq!(extra_inputs.len(), 1);
3218 ("store", [extra_inputs[0].print(printer)].into_iter().collect())
3219 }
3220 };
3221
3222 pretty::Fragment::new([
3223 printer
3224 .demote_style_for_namespace_prefix(printer.declarative_keyword_style())
3225 .apply("qptr.")
3226 .into(),
3227 printer.declarative_keyword_style().apply(name).into(),
3228 pretty::join_comma_sep(
3229 "(",
3230 qptr_input.map(|v| v.print(printer)).into_iter().chain(extra_inputs),
3231 ")",
3232 ),
3233 ])
3234 }
3235
3236 DataInstKind::SpvInst(inst) => printer.pretty_spv_inst(
3237 printer.spv_op_style(),
3238 inst.opcode,
3239 &inst.imms,
3240 inputs.iter().map(|v| v.print(printer)),
3241 ),
3242 &DataInstKind::SpvExtInst { ext_set, inst } => {
3243 let spv_spec = spv::spec::Spec::get();
3244 let wk = &spv_spec.well_known;
3245
3246 output_type_to_print = output_type_to_print.filter(|&ty| {
3250 let is_void = match &printer.cx[ty].kind {
3251 TypeKind::SpvInst { spv_inst, .. } => spv_inst.opcode == wk.OpTypeVoid,
3252 _ => false,
3253 };
3254 !is_void
3255 });
3256 if output_type_to_print.is_none() {
3258 output_use_to_print_as_lhs = output_use_to_print_as_lhs.filter(|output_use| {
3259 printer
3260 .use_styles
3261 .get(output_use)
3262 .is_some_and(|style| !matches!(style, UseStyle::Inline))
3263 });
3264 }
3265
3266 let ext_set_name = &printer.cx[ext_set];
3268 let lowercase_ext_set_name = ext_set_name.to_ascii_lowercase();
3269 let (ext_set_alias, known_inst_desc) = (spv_spec
3270 .get_ext_inst_set_by_lowercase_name(&lowercase_ext_set_name))
3271 .or_else(|| {
3272 printer.cx.get_custom_ext_inst_set_by_lowercase_name(&lowercase_ext_set_name)
3273 })
3274 .map_or((&None, None), |ext_inst_set| {
3275 (&ext_inst_set.short_alias, ext_inst_set.instructions.get(&inst))
3278 });
3279
3280 let ext_set_name = ext_set_alias.as_deref().unwrap_or(ext_set_name);
3282
3283 let operand_names = known_inst_desc
3285 .into_iter()
3286 .flat_map(|inst_desc| inst_desc.operand_names.iter().map(|name| &name[..]))
3287 .chain(std::iter::repeat(""));
3288
3289 enum PseudoImm<'a> {
3292 Str(&'a str),
3293 U32(u32),
3294 }
3295 let pseudo_imm_from_value = |v: Value| {
3296 if let Value::Const(ct) = v {
3297 match &printer.cx[ct].kind {
3298 &ConstKind::SpvStringLiteralForExtInst(s) => {
3299 return Some(PseudoImm::Str(&printer.cx[s]));
3300 }
3301 ConstKind::SpvInst { spv_inst_and_const_inputs } => {
3302 let (spv_inst, _const_inputs) = &**spv_inst_and_const_inputs;
3303 if spv_inst.opcode == wk.OpConstant {
3304 if let [spv::Imm::Short(_, x)] = spv_inst.imms[..] {
3305 if i32::try_from(x).and_then(u32::try_from) == Ok(x) {
3307 return Some(PseudoImm::U32(x));
3308 }
3309 }
3310 }
3311 }
3312 ConstKind::PtrToGlobalVar(_) => {}
3313 }
3314 }
3315 None
3316 };
3317
3318 let debuginfo_with_pseudo_imm_inputs: Option<SmallVec<[_; 8]>> = known_inst_desc
3319 .filter(|inst_desc| {
3320 inst_desc.is_debuginfo && output_use_to_print_as_lhs.is_none()
3321 })
3322 .and_then(|_| inputs.iter().copied().map(pseudo_imm_from_value).collect());
3323 let printing_debuginfo_as_comment = debuginfo_with_pseudo_imm_inputs.is_some();
3324
3325 let [spv_base_style, string_literal_style, numeric_literal_style] =
3326 if printing_debuginfo_as_comment {
3327 [printer.comment_style(); 3]
3328 } else {
3329 [
3330 printer.spv_base_style(),
3331 printer.string_literal_style(),
3332 printer.numeric_literal_style(),
3333 ]
3334 };
3335
3336 let inst_name_or_num = {
3337 let (style, s) = match known_inst_desc {
3338 Some(inst_desc) => (spv_base_style, inst_desc.name.clone()),
3339 None => (numeric_literal_style, format!("{inst}").into()),
3340 };
3341 pretty::Styles { thickness: Some(3), ..style }.apply(s)
3343 };
3344
3345 pretty::Fragment::new([
3346 if printing_debuginfo_as_comment {
3347 printer.comment_style().apply("// ").into()
3348 } else {
3349 pretty::Fragment::default()
3350 },
3351 printer
3354 .demote_style_for_namespace_prefix(
3355 printer.demote_style_for_namespace_prefix(
3356 printer.demote_style_for_namespace_prefix(spv_base_style),
3357 ),
3358 )
3359 .apply("spv.")
3360 .into(),
3361 printer
3362 .demote_style_for_namespace_prefix(
3363 printer.demote_style_for_namespace_prefix(spv_base_style),
3364 )
3365 .apply("extinst.")
3366 .into(),
3367 printer
3369 .demote_style_for_namespace_prefix(string_literal_style)
3370 .apply(format!("{ext_set_name:?}"))
3371 .into(),
3372 printer.demote_style_for_namespace_prefix(spv_base_style).apply(".").into(),
3373 inst_name_or_num.into(),
3374 if let Some(inputs) = debuginfo_with_pseudo_imm_inputs {
3375 let style = printer.comment_style();
3376 let inputs = inputs.into_iter().zip(operand_names).map(|(input, name)| {
3377 pretty::Fragment::new([
3378 Some(name)
3379 .filter(|name| !name.is_empty())
3380 .and_then(|name| {
3381 Some(printer.pretty_named_argument_prefix(
3382 printer.sanitize_spv_operand_name(name)?,
3383 ))
3384 })
3385 .unwrap_or_default(),
3386 style
3387 .apply(match input {
3388 PseudoImm::Str(s) => format!("{s:?}"),
3389 PseudoImm::U32(x) => format!("{x}"),
3390 })
3391 .into(),
3392 ])
3393 });
3394 pretty::Fragment::new(
3395 ([style.apply("(").into()].into_iter())
3396 .chain(inputs.intersperse(style.apply(", ").into()))
3397 .chain([style.apply(")").into()]),
3398 )
3399 } else {
3400 pretty::join_comma_sep(
3401 "(",
3402 inputs.iter().zip(operand_names).map(|(&input, name)| {
3403 let printed_input = match pseudo_imm_from_value(input) {
3405 Some(PseudoImm::Str(s)) => printer.pretty_string_literal(s),
3406 _ => input.print(printer),
3407 };
3408 let name = Some(name)
3409 .filter(|name| !name.is_empty())
3410 .and_then(|name| printer.sanitize_spv_operand_name(name));
3411 if let Some(name) = name {
3412 pretty::Fragment::new([
3413 printer
3417 .named_argument_label_style()
3418 .apply(format!("{name}:"))
3419 .into(),
3420 pretty::join_space("", [printed_input]),
3421 ])
3422 } else {
3423 printed_input
3424 }
3425 }),
3426 ")",
3427 )
3428 },
3429 ])
3430 }
3431 };
3432
3433 let def_without_name = pretty::Fragment::new([
3434 def_without_type,
3435 output_type_to_print
3436 .map(|ty| printer.pretty_type_ascription_suffix(ty))
3437 .unwrap_or_default(),
3438 ]);
3439
3440 let def_without_name = pretty::Fragment::new([
3442 Use::AlignmentAnchorForDataInst(self.position).print_as_def(printer),
3443 def_without_name,
3444 ]);
3445
3446 AttrsAndDef { attrs, def_without_name }.insert_name_before_def(
3447 output_use_to_print_as_lhs
3448 .map(|output_use| {
3449 pretty::Fragment::new([output_use.print_as_def(printer), " = ".into()])
3450 })
3451 .unwrap_or_default(),
3452 )
3453 }
3454}
3455
3456impl Print for cfg::ControlInst {
3457 type Output = pretty::Fragment;
3458 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
3459 let Self { attrs, kind, inputs, targets, target_inputs } = self;
3460
3461 let attrs = attrs.print(printer);
3462
3463 let kw_style = printer.imperative_keyword_style();
3464 let kw = |kw| kw_style.apply(kw).into();
3465
3466 let mut targets = targets.iter().map(|&target_region| {
3467 let mut target = pretty::Fragment::new([
3468 kw("branch"),
3469 " ".into(),
3470 Use::ControlRegionLabel(target_region).print(printer),
3471 ]);
3472 if let Some(inputs) = target_inputs.get(&target_region) {
3473 target = pretty::Fragment::new([
3474 target,
3475 pretty::join_comma_sep("(", inputs.iter().map(|v| v.print(printer)), ")"),
3476 ]);
3477 }
3478 target
3479 });
3480
3481 let def = match kind {
3482 cfg::ControlInstKind::Unreachable => {
3483 assert!(targets.len() == 0 && inputs.is_empty());
3485 kw("unreachable")
3486 }
3487 cfg::ControlInstKind::Return => {
3488 assert!(targets.len() == 0);
3490 match inputs[..] {
3491 [] => kw("return"),
3492 [v] => pretty::Fragment::new([kw("return"), " ".into(), v.print(printer)]),
3493 _ => unreachable!(),
3494 }
3495 }
3496 cfg::ControlInstKind::ExitInvocation(cfg::ExitInvocationKind::SpvInst(spv::Inst {
3497 opcode,
3498 imms,
3499 })) => {
3500 assert!(targets.len() == 0);
3502 printer.pretty_spv_inst(
3503 kw_style,
3504 *opcode,
3505 imms,
3506 inputs.iter().map(|v| v.print(printer)),
3507 )
3508 }
3509
3510 cfg::ControlInstKind::Branch => {
3511 assert_eq!((targets.len(), inputs.len()), (1, 0));
3512 targets.next().unwrap()
3513 }
3514
3515 cfg::ControlInstKind::SelectBranch(kind) => {
3516 assert_eq!(inputs.len(), 1);
3517 kind.print_with_scrutinee_and_cases(printer, kw_style, inputs[0], targets)
3518 }
3519 };
3520
3521 pretty::Fragment::new([attrs, def])
3522 }
3523}
3524
3525impl SelectionKind {
3526 fn print_with_scrutinee_and_cases(
3527 &self,
3528 printer: &Printer<'_>,
3529 kw_style: pretty::Styles,
3530 scrutinee: Value,
3531 mut cases: impl ExactSizeIterator<Item = pretty::Fragment>,
3532 ) -> pretty::Fragment {
3533 let kw = |kw| kw_style.apply(kw).into();
3534 match *self {
3535 SelectionKind::BoolCond => {
3536 assert_eq!(cases.len(), 2);
3537 let [then_case, else_case] = [cases.next().unwrap(), cases.next().unwrap()];
3538 pretty::Fragment::new([
3539 kw("if"),
3540 " ".into(),
3541 scrutinee.print(printer),
3542 " {".into(),
3543 pretty::Node::IndentedBlock(vec![then_case]).into(),
3544 "} ".into(),
3545 kw("else"),
3546 " {".into(),
3547 pretty::Node::IndentedBlock(vec![else_case]).into(),
3548 "}".into(),
3549 ])
3550 }
3551 SelectionKind::SpvInst(spv::Inst { opcode, ref imms }) => {
3552 let header = printer.pretty_spv_inst(
3553 kw_style,
3554 opcode,
3555 imms,
3556 [Some(scrutinee.print(printer))]
3557 .into_iter()
3558 .chain((0..cases.len()).map(|_| None)),
3559 );
3560
3561 pretty::Fragment::new([
3562 header,
3563 " {".into(),
3564 pretty::Node::IndentedBlock(
3565 cases
3566 .map(|case| {
3567 pretty::Fragment::new([
3568 pretty::Node::ForceLineSeparation.into(),
3569 kw("case"),
3572 " => {".into(),
3573 pretty::Node::IndentedBlock(vec![case]).into(),
3574 "}".into(),
3575 pretty::Node::ForceLineSeparation.into(),
3576 ])
3577 })
3578 .collect(),
3579 )
3580 .into(),
3581 "}".into(),
3582 ])
3583 }
3584 }
3585 }
3586}
3587
3588impl Value {
3589 fn print_as_def(&self, printer: &Printer<'_>) -> pretty::Fragment {
3590 Use::from(*self).print_as_def(printer)
3591 }
3592}
3593
3594impl Print for Value {
3595 type Output = pretty::Fragment;
3596 fn print(&self, printer: &Printer<'_>) -> pretty::Fragment {
3597 Use::from(*self).print(printer)
3598 }
3599}