spade_mir/
diff_printing.rs

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