spade_mir/
diff_printing.rs

1/// Utilities for printing the difference between two mir blocks with their var mappings.
2///
3/// Names are formatted as e(<left hand side> | <right hand side>) and the corresponding name is
4/// looked up from the specified hash map depending on which side we're printing. This allows some
5/// nice text-based diffs, but does require the use of quite a few generic parameters since we need
6/// to either look up names in a hash map, or just use the given name.
7use std::collections::HashMap;
8
9use itertools::Itertools;
10use spade_common::id_tracker::ExprID;
11
12use crate::{diff::VarMap, Entity};
13use crate::{Binding, MirInput, Register, Statement, ValueName};
14
15pub fn translate_expr(
16    name: ExprID,
17    lhs_trans: &impl Fn(ExprID) -> Option<ExprID>,
18    rhs_trans: &impl Fn(ExprID) -> Option<ExprID>,
19) -> String {
20    let lhs = lhs_trans(name)
21        .map(|n| format!("{}", n.0))
22        .unwrap_or_else(|| "?".to_string());
23    let rhs = rhs_trans(name)
24        .map(|n| format!("{}", n.0))
25        .unwrap_or_else(|| "?".to_string());
26
27    format!("e({}|{})", lhs, rhs)
28}
29
30pub fn translate_name(
31    (id, name): (u64, &str),
32    lhs_trans: &impl Fn(u64) -> Option<u64>,
33    rhs_trans: &impl Fn(u64) -> Option<u64>,
34) -> String {
35    let lhs = lhs_trans(id)
36        .map(|i| format!("{}", i))
37        .unwrap_or_else(|| "?".to_string());
38    let rhs = rhs_trans(id)
39        .map(|i| format!("{}", i))
40        .unwrap_or_else(|| "?".to_string());
41
42    format!("n({}|{}, {})", lhs, rhs, name)
43}
44
45pub struct NameTranslator<F, G>
46where
47    F: Fn(ExprID) -> Option<ExprID>,
48    G: Fn(u64) -> Option<u64>,
49{
50    expr: F,
51    name: G,
52}
53
54pub fn identity_name_translator(
55) -> NameTranslator<impl Fn(ExprID) -> Option<ExprID>, impl Fn(u64) -> Option<u64>> {
56    NameTranslator {
57        expr: Some,
58        name: Some,
59    }
60}
61
62pub fn map_name_translator(
63    expr: HashMap<ExprID, ExprID>,
64    name: HashMap<u64, u64>,
65) -> NameTranslator<impl Fn(ExprID) -> Option<ExprID>, impl Fn(u64) -> Option<u64>> {
66    NameTranslator {
67        expr: move |x| expr.get(&x).cloned(),
68        name: move |x| name.get(&x).cloned(),
69    }
70}
71
72pub fn translate_val_name<LF, LG, RF, RG>(
73    name: &ValueName,
74    lhs_trans: &NameTranslator<LF, LG>,
75    rhs_trans: &NameTranslator<RF, RG>,
76) -> String
77where
78    LF: Fn(ExprID) -> Option<ExprID>,
79    LG: Fn(u64) -> Option<u64>,
80    RF: Fn(ExprID) -> Option<ExprID>,
81    RG: Fn(u64) -> Option<u64>,
82{
83    match name {
84        ValueName::Named(id, n, _) => translate_name((*id, n), &lhs_trans.name, &rhs_trans.name),
85        ValueName::Expr(id) => translate_expr(*id, &lhs_trans.expr, &rhs_trans.expr),
86    }
87}
88
89pub fn translate_statement<LF, LG, RF, RG>(
90    statement: &Statement,
91    lhs_trans: &NameTranslator<LF, LG>,
92    rhs_trans: &NameTranslator<RF, RG>,
93) -> String
94where
95    LF: Fn(ExprID) -> Option<ExprID>,
96    LG: Fn(u64) -> Option<u64>,
97    RF: Fn(ExprID) -> Option<ExprID>,
98    RG: Fn(u64) -> Option<u64>,
99{
100    match statement {
101        Statement::Binding(Binding {
102            name,
103            operator,
104            operands,
105            ty,
106            loc: _,
107        }) => {
108            let name = translate_val_name(name, lhs_trans, rhs_trans);
109            let operands = operands
110                .iter()
111                .map(|op| translate_val_name(op, lhs_trans, rhs_trans))
112                .collect::<Vec<_>>()
113                .join(",");
114
115            format!("{}: {} <- {}({})", name, ty, operator, operands)
116        }
117        Statement::Register(Register {
118            name,
119            ty,
120            clock,
121            reset,
122            initial,
123            value,
124            loc: _,
125            traced,
126        }) => {
127            let name = translate_val_name(name, lhs_trans, rhs_trans);
128            let clock = translate_val_name(clock, lhs_trans, rhs_trans);
129            let reset = reset
130                .as_ref()
131                .map(|(trig, val)| {
132                    let trig = translate_val_name(trig, lhs_trans, rhs_trans);
133                    let val = translate_val_name(val, lhs_trans, rhs_trans);
134                    format!(" reset ({}, {})", trig, val)
135                })
136                .unwrap_or_else(|| "".to_string());
137            let initial = initial
138                .as_ref()
139                .map(|i| {
140                    format!(
141                        " initial ({})",
142                        i.iter().map(|v| format!("{v}").to_string()).join("; ")
143                    )
144                })
145                .unwrap_or_else(|| "".to_string());
146            let value = translate_val_name(value, lhs_trans, rhs_trans);
147            let traced = if let Some(traced) = traced {
148                format!(
149                    "traced({})",
150                    translate_val_name(traced, lhs_trans, rhs_trans)
151                )
152            } else {
153                "".to_string()
154            };
155
156            format!("{traced}reg {name}: {ty} clock {clock}{reset}{initial} {value}",)
157        }
158        Statement::Constant(name, ty, value) => {
159            let name = translate_expr(*name, &lhs_trans.expr, &rhs_trans.expr);
160
161            format!("const {}: {} = {}", name, ty, value)
162        }
163        Statement::Assert(value) => {
164            let value = translate_val_name(value, lhs_trans, rhs_trans);
165            format!("assert {value}")
166        }
167        Statement::Set { target, value } => {
168            let value = translate_val_name(value, lhs_trans, rhs_trans);
169            let target = translate_val_name(target, lhs_trans, rhs_trans);
170            format!("set {target} = {value}")
171        }
172        Statement::WalTrace {
173            name,
174            val,
175            suffix,
176            ty,
177        } => {
178            let name = translate_val_name(name, lhs_trans, rhs_trans);
179            let val = translate_val_name(val, lhs_trans, rhs_trans);
180            format!("wal_trace ({name}, {val}, '{suffix}', {ty})")
181        }
182    }
183}
184
185pub fn translate_entity<LF, LG, RF, RG>(
186    entity: &Entity,
187    lhs_trans: &NameTranslator<LF, LG>,
188    rhs_trans: &NameTranslator<RF, RG>,
189) -> String
190where
191    LF: Fn(ExprID) -> Option<ExprID>,
192    LG: Fn(u64) -> Option<u64>,
193    RF: Fn(ExprID) -> Option<ExprID>,
194    RG: Fn(u64) -> Option<u64>,
195{
196    let Entity {
197        name,
198        inputs,
199        output,
200        output_type,
201        statements,
202    } = entity;
203
204    let inputs = inputs
205        .iter()
206        .map(
207            |MirInput {
208                 name,
209                 val_name,
210                 ty,
211                 no_mangle,
212             }| {
213                let val_name = translate_val_name(val_name, lhs_trans, rhs_trans);
214
215                format!(
216                    "({}{name}, {val_name}: {ty})",
217                    no_mangle.map(|_| "#[no_mangle]").unwrap_or("")
218                )
219            },
220        )
221        .collect::<Vec<_>>()
222        .join(",");
223
224    let output = translate_val_name(output, lhs_trans, rhs_trans);
225
226    let statements = statements
227        .iter()
228        .map(|s| translate_statement(s, lhs_trans, rhs_trans))
229        .map(|s| format!("    {}", s))
230        .collect::<Vec<_>>()
231        .join("\n");
232
233    indoc::formatdoc!(
234        r#"entity {}({}) -> {} {{
235            {}
236        }} => {}"#,
237        name,
238        inputs,
239        output_type,
240        statements,
241        output
242    )
243}
244
245/// Returns string versions of lhs and rhs with the variable mapping `map`
246pub fn translated_strings(lhs: &Entity, rhs: &Entity, map: &VarMap) -> (String, String) {
247    let lhs_string = translate_entity(
248        lhs,
249        &identity_name_translator(),
250        &map_name_translator(map.expr_map.clone(), map.name_map.clone()),
251    );
252
253    let rhs_string = translate_entity(
254        rhs,
255        &map_name_translator(map.expr_map_rev.clone(), map.name_map_rev.clone()),
256        &identity_name_translator(),
257    );
258
259    (lhs_string, rhs_string)
260}