1use std::collections::{BTreeMap, HashSet};
2use vyre_foundation::ir::Program;
3use vyre_lower::KernelDescriptor;
4
5#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
6pub struct DescriptorDiff {
7 pub bindings_dropped: Vec<u32>,
8 pub bindings_added: Vec<u32>,
9 #[serde(
10 serialize_with = "crate::path_map_serde::serialize_i64",
11 deserialize_with = "crate::path_map_serde::deserialize_i64"
12 )]
13 pub op_count_delta: BTreeMap<Vec<usize>, i64>,
14 pub root_shape_changed: bool,
15}
16
17pub fn diff_descriptors(before: &KernelDescriptor, after: &KernelDescriptor) -> DescriptorDiff {
18 let before_bindings: HashSet<u32> = before.bindings.slots.iter().map(|s| s.slot).collect();
19 let after_bindings: HashSet<u32> = after.bindings.slots.iter().map(|s| s.slot).collect();
20
21 let mut dropped: Vec<u32> = before_bindings
22 .difference(&after_bindings)
23 .copied()
24 .collect();
25 dropped.sort_unstable();
26 let mut added: Vec<u32> = after_bindings
27 .difference(&before_bindings)
28 .copied()
29 .collect();
30 added.sort_unstable();
31
32 let mut before_counts = BTreeMap::new();
33 let mut after_counts = BTreeMap::new();
34
35 fn walk_counts(
36 body: &vyre_lower::KernelBody,
37 path: Vec<usize>,
38 map: &mut BTreeMap<Vec<usize>, usize>,
39 ) {
40 map.insert(path.clone(), body.ops.len());
41 for (i, child) in body.child_bodies.iter().enumerate() {
42 let mut p = path.clone();
43 p.push(i);
44 walk_counts(child, p, map);
45 }
46 }
47
48 walk_counts(&before.body, vec![], &mut before_counts);
49 walk_counts(&after.body, vec![], &mut after_counts);
50
51 let mut op_count_delta = BTreeMap::new();
52 for (path, before_count) in &before_counts {
53 let after_count = after_counts.get(path).copied().unwrap_or(0);
54 if *before_count != after_count {
55 op_count_delta.insert(path.clone(), (after_count as i64) - (*before_count as i64));
56 }
57 }
58 for (path, after_count) in &after_counts {
59 if !before_counts.contains_key(path) {
60 op_count_delta.insert(path.clone(), *after_count as i64);
61 }
62 }
63
64 let mut root_shape_changed = false;
65 if before.body.ops.len() != after.body.ops.len() {
66 root_shape_changed = true;
67 } else {
68 for (a, b) in before.body.ops.iter().zip(after.body.ops.iter()) {
69 if std::mem::discriminant(&a.kind) != std::mem::discriminant(&b.kind) {
70 root_shape_changed = true;
71 break;
72 }
73 }
74 }
75
76 DescriptorDiff {
77 bindings_dropped: dropped,
78 bindings_added: added,
79 op_count_delta,
80 root_shape_changed,
81 }
82}
83
84#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
85pub struct RewriteBisectResult {
86 pub first_failing_rewrite: Option<String>,
87 pub verify_errors: Vec<String>,
88 pub rewrite_history: Vec<(String, DescriptorDiff)>,
89}
90
91pub fn bisect_rewrites(program: &Program) -> Result<RewriteBisectResult, String> {
92 let lower_result = vyre_lower::lower(program).map_err(|e| format!("{:?}", e))?;
93
94 let mut current = lower_result;
98
99 if let Err(errs) = vyre_lower::verify::verify(¤t) {
100 return Ok(RewriteBisectResult {
101 first_failing_rewrite: Some("initial".to_string()),
102 verify_errors: errs.iter().map(|e| format!("{:?}", e)).collect(),
103 rewrite_history: vec![],
104 });
105 }
106
107 let mut first_failing_rewrite = None;
108 let mut verify_errors = Vec::new();
109 let mut rewrite_history = Vec::new();
110
111 let steps: Vec<(&str, fn(&KernelDescriptor) -> KernelDescriptor)> = vec![
112 ("strength_reduce", vyre_lower::rewrites::strength_reduce),
113 (
114 "shared_mem_promote",
115 vyre_lower::rewrites::shared_mem_promote,
116 ),
117 ("bank_conflict_pad", vyre_lower::rewrites::bank_conflict_pad),
118 (
119 "const_buffer_promote",
120 vyre_lower::rewrites::const_buffer_promote,
121 ),
122 (
123 "descriptor_const_fold",
124 vyre_lower::rewrites::descriptor_const_fold,
125 ),
126 ("identity_elim", vyre_lower::rewrites::identity_elim),
127 ("branch_collapse", vyre_lower::rewrites::branch_collapse),
128 ("loop_unroll", vyre_lower::rewrites::loop_unroll),
129 ("licm", vyre_lower::rewrites::licm),
130 ("load_forwarding", vyre_lower::rewrites::load_forwarding),
131 ("descriptor_dce#1", vyre_lower::rewrites::descriptor_dce),
132 ("dead_store", vyre_lower::rewrites::dead_store),
133 ("descriptor_dce#2", vyre_lower::rewrites::descriptor_dce),
134 ("canonicalize", vyre_lower::rewrites::canonicalize),
135 ("descriptor_cse", vyre_lower::rewrites::descriptor_cse),
136 (
137 "drop_unused_bindings",
138 vyre_lower::rewrites::drop_unused_bindings,
139 ),
140 (
141 "drop_unused_literals",
142 vyre_lower::rewrites::drop_unused_literals,
143 ),
144 (
145 "drop_unused_child_bodies",
146 vyre_lower::rewrites::drop_unused_child_bodies,
147 ),
148 ];
149
150 for (name, func) in steps {
151 let next = func(¤t);
152 let diff = diff_descriptors(¤t, &next);
153 rewrite_history.push((name.to_string(), diff));
154
155 if let Err(errs) = vyre_lower::verify::verify(&next) {
156 first_failing_rewrite = Some(name.to_string());
157 verify_errors = errs.iter().map(|e| format!("{:?}", e)).collect();
158 break;
159 }
160
161 current = next;
162 }
163
164 Ok(RewriteBisectResult {
165 first_failing_rewrite,
166 verify_errors,
167 rewrite_history,
168 })
169}