spade/
compiler_state.rs

1use std::{
2    collections::BTreeMap,
3    sync::{Arc, RwLock},
4};
5
6use color_eyre::eyre::anyhow;
7use itertools::Itertools;
8use rustc_hash::FxHashMap as HashMap;
9use serde::{Deserialize, Serialize};
10use spade_ast_lowering::id_tracker::{ExprIdTracker, ImplIdTracker};
11use spade_common::location_info::WithLocation;
12use spade_common::name::NameID;
13use spade_hir::{
14    query::QueryCache,
15    symbol_table::{FrozenSymtab, SymbolTable},
16    ItemList,
17};
18use spade_hir_lowering::{
19    name_map::{NameSource, NamedValue},
20    NameSourceMap,
21};
22use spade_mir::{
23    renaming::{VerilogNameMap, VerilogNameSource},
24    unit_name::InstanceMap,
25};
26use spade_typeinference::{
27    equation::TypedExpression, traits::TraitImplList, HasType, OwnedTypeState, SharedTypeState,
28    TypeState,
29};
30use spade_types::ConcreteType;
31
32use spade_common::sizes::{add_field, SerializedSize};
33
34#[derive(Serialize, Deserialize)]
35pub struct StoredMirContext {
36    /// Mapping to concrete types for this instantiation of the entity
37    pub type_state: OwnedTypeState,
38    pub reg_name_map: BTreeMap<NameID, NameID>,
39    pub verilog_name_map: VerilogNameMap,
40}
41
42impl StoredMirContext {
43    fn with_shared(self, shared: Arc<SharedTypeState>) -> MirContext {
44        MirContext {
45            type_state: TypeState {
46                owned: self.type_state,
47                shared,
48            },
49            reg_name_map: self.reg_name_map,
50            verilog_name_map: self.verilog_name_map,
51        }
52    }
53}
54
55impl SerializedSize for StoredMirContext {
56    fn accumulate_size(
57        &self,
58        field: &[&'static str],
59        into: &mut HashMap<Vec<&'static str>, usize>,
60    ) {
61        let Self {
62            type_state,
63            reg_name_map,
64            verilog_name_map,
65        } = self;
66
67        let mut ts_path = field.to_vec();
68        ts_path.push("type_state");
69        type_state.accumulate_size(&ts_path, into);
70
71        add_field(field, "type_state", type_state, into);
72        add_field(field, "reg_name_map", reg_name_map, into);
73        add_field(field, "verilog_name_map", verilog_name_map, into);
74    }
75}
76
77pub struct MirContext {
78    /// Mapping to concrete types for this instantiation of the entity
79    pub type_state: TypeState,
80    pub reg_name_map: BTreeMap<NameID, NameID>,
81    pub verilog_name_map: VerilogNameMap,
82}
83
84impl MirContext {
85    fn to_stored(self) -> StoredMirContext {
86        StoredMirContext {
87            type_state: self.type_state.owned,
88            reg_name_map: self.reg_name_map,
89            verilog_name_map: self.verilog_name_map,
90        }
91    }
92}
93
94/// All the state required in order to add more things to the compilation process
95#[derive(Serialize, Deserialize)]
96pub struct StoredCompilerState {
97    // (filename, file content) of all the compiled files
98    pub code: Vec<(String, String)>,
99    pub symtab: FrozenSymtab,
100    pub idtracker: Arc<ExprIdTracker>,
101    pub impl_idtracker: ImplIdTracker,
102    pub item_list: ItemList,
103    pub name_source_map: Arc<RwLock<NameSourceMap>>,
104    pub instance_map: InstanceMap,
105    pub mir_context: HashMap<NameID, StoredMirContext>,
106    pub shared_type_state: Arc<SharedTypeState>,
107    pub trait_impl_list: TraitImplList,
108}
109
110impl StoredCompilerState {
111    pub fn into_compiler_state(self) -> CompilerState {
112        let Self {
113            code,
114            symtab,
115            idtracker,
116            impl_idtracker,
117            item_list,
118            name_source_map,
119            instance_map,
120            mir_context,
121            shared_type_state,
122            trait_impl_list,
123        } = self;
124
125        CompilerState {
126            code,
127            symtab,
128            idtracker,
129            impl_idtracker,
130            item_list,
131            name_source_map,
132            instance_map,
133            mir_context: mir_context
134                .into_iter()
135                .map(|(k, v)| (k, v.with_shared(Arc::clone(&shared_type_state))))
136                .collect(),
137            shared_type_state,
138            trait_impl_list,
139        }
140    }
141}
142
143pub struct CompilerState {
144    // (filename, file content) of all the compiled files
145    pub code: Vec<(String, String)>,
146    pub symtab: FrozenSymtab,
147    pub idtracker: Arc<ExprIdTracker>,
148    pub impl_idtracker: ImplIdTracker,
149    pub item_list: ItemList,
150    pub name_source_map: Arc<RwLock<NameSourceMap>>,
151    pub instance_map: InstanceMap,
152    pub mir_context: HashMap<NameID, MirContext>,
153    pub shared_type_state: Arc<SharedTypeState>,
154    pub trait_impl_list: TraitImplList,
155}
156
157impl CompilerState {
158    pub fn into_stored(self) -> StoredCompilerState {
159        StoredCompilerState {
160            code: self.code,
161            symtab: self.symtab,
162            idtracker: self.idtracker,
163            impl_idtracker: self.impl_idtracker,
164            item_list: self.item_list,
165            name_source_map: self.name_source_map,
166            instance_map: self.instance_map,
167            mir_context: self
168                .mir_context
169                .into_iter()
170                .map(|(k, v)| (k, v.to_stored()))
171                .collect(),
172            shared_type_state: self.shared_type_state,
173            trait_impl_list: self.trait_impl_list,
174        }
175    }
176
177    pub fn build_query_cache<'a>(&'a self) -> QueryCache {
178        QueryCache::from_item_list(&self.item_list)
179    }
180
181    // Attempts to demangle the specified string to the corresponding snippet of source code
182    pub fn demangle_string(&self, mangled: &str) -> Option<String> {
183        // We'll need to first mangle the ValueNames into actual strings to search.
184        // NOTE: A smart non-lazy person would do this once, not every time we ask
185        // for demangling
186        let string_map = self
187            .name_source_map
188            .read()
189            .unwrap()
190            .inner
191            .iter()
192            .flat_map(|(k, v)| {
193                vec![
194                    (k.var_name(), v.clone()),
195                    (k.backward_var_name(), v.clone()),
196                ]
197            })
198            .collect::<HashMap<_, _>>();
199
200        string_map.get(mangled).map(|name| match name {
201            NamedValue::Primary(source) => self.demangle_name_source(source),
202            NamedValue::Secondary(source, description) => {
203                format!("{} ({description})", self.demangle_name_source(source))
204            }
205        })
206    }
207
208    pub fn demangle_name_source(&self, source: &NameSource) -> String {
209        match source {
210            NameSource::Name(n) => format!("{n}"),
211            NameSource::Expr(e) => {
212                format!(
213                    "(id) {}",
214                    &self.code[e.file_id].1[e.span.start().to_usize()..e.span.end().to_usize()]
215                )
216            }
217        }
218    }
219
220    pub fn name_source_of_hierarchical_value(
221        &self,
222        top_module: &NameID,
223        hierarchy: &[String],
224        query_cache: &QueryCache,
225    ) -> color_eyre::Result<Option<NameSource>> {
226        let (verilog_source, _mir_ctx) = source_of_hierarchical_value(
227            top_module,
228            hierarchy,
229            &self.instance_map,
230            &self.mir_context,
231        )?;
232
233        match verilog_source {
234            VerilogNameSource::ForwardName(n) | VerilogNameSource::BackwardName(n) => {
235                Ok(Some(NameSource::Name(n.clone().nowhere())))
236            }
237            VerilogNameSource::ForwardExpr(id) | VerilogNameSource::BackwardExpr(id) => {
238                Ok(query_cache
239                    .id_to_expression(*id)
240                    .map(|loc_expr| NameSource::Expr(loc_expr.map_ref(|_| *id))))
241            }
242        }
243    }
244
245    pub fn type_of_hierarchical_value(
246        &self,
247        top_module: &NameID,
248        hierarchy: &[String],
249    ) -> color_eyre::Result<ConcreteType> {
250        type_of_hierarchical_value(
251            top_module,
252            hierarchy,
253            &self.instance_map,
254            &self.mir_context,
255            self.symtab.symtab(),
256            &self.item_list,
257        )
258    }
259}
260
261impl SerializedSize for StoredCompilerState {
262    fn accumulate_size(
263        &self,
264        field: &[&'static str],
265        into: &mut HashMap<Vec<&'static str>, usize>,
266    ) {
267        let Self {
268            code,
269            symtab,
270            idtracker,
271            impl_idtracker,
272            item_list,
273            name_source_map,
274            instance_map,
275            mir_context,
276            shared_type_state,
277            trait_impl_list,
278        } = self;
279
280        for (_, mc) in mir_context {
281            let mut path = field.to_vec();
282            path.push("mir_context");
283            mc.accumulate_size(&path, into);
284        }
285
286        add_field(field, "code", code, into);
287        add_field(field, "symtab", symtab, into);
288        add_field(field, "idtracker", idtracker, into);
289        add_field(field, "impl_idtracker", impl_idtracker, into);
290        add_field(field, "item_list", item_list, into);
291        add_field(field, "name_source_map", name_source_map, into);
292        add_field(field, "instance_map", instance_map, into);
293        add_field(field, "mir_context", mir_context, into);
294        add_field(field, "shared_type_state", shared_type_state, into);
295        add_field(field, "trait_impl_list", trait_impl_list, into);
296    }
297}
298
299pub fn source_of_hierarchical_value<'a>(
300    top_module: &'a NameID,
301    hierarchy: &'a [String],
302    instance_map: &'a InstanceMap,
303    mir_contexts: &'a HashMap<NameID, MirContext>,
304) -> color_eyre::Result<(&'a VerilogNameSource, &'a MirContext)> {
305    let mut hierarchy = Vec::from(hierarchy);
306    let value_name = hierarchy.pop().unwrap();
307    hierarchy.reverse();
308
309    // Lookup the name_id of the instance we want to query for the value_name in
310    let mut current_unit = top_module;
311    let mut path_so_far = vec![format!("{}", top_module)];
312    while let Some(next_instance_name) = hierarchy.pop() {
313        let local_map = instance_map
314            .inner
315            .get(&current_unit.clone())
316            .ok_or_else(|| {
317                let candidates = instance_map
318                    .inner
319                    .keys()
320                    .map(|n| format!("    {n}"))
321                    .collect::<Vec<_>>();
322
323                let candidates_msg = if candidates.is_empty() {
324                    String::new()
325                } else {
326                    format!("  candidates\n    {}", candidates.join("    \n"))
327                };
328
329                anyhow!(
330                    "Did not find a unit named {} in {}\n{candidates_msg}",
331                    &next_instance_name,
332                    path_so_far.join(".")
333                )
334            })?;
335        let next = local_map.get(&next_instance_name);
336        if let Some(next) = next {
337            current_unit = next;
338        } else {
339            let candidates_msg = if local_map.is_empty() {
340                String::new()
341            } else {
342                format!("\n  candidates:\n    {}", local_map.keys().join("    \n"))
343            };
344
345            return Err(anyhow!(
346                "{} has no spade unit instance named {next_instance_name}{candidates_msg}",
347                path_so_far.join(".")
348            ));
349        };
350        path_so_far.push(next_instance_name.to_string());
351    }
352
353    // Look up the mir context of the unit we are observing
354    let mir_ctx = mir_contexts
355        .get(current_unit)
356        .ok_or_else(|| anyhow!("Did not find information a unit named {current_unit}"))?;
357
358    let source = mir_ctx
359        .verilog_name_map
360        .lookup_name(&value_name)
361        .ok_or_else(|| {
362            anyhow!(
363                "Did not find spade variable for verilog identifier '{value_name}' in '{path}'",
364                path = path_so_far.join(".")
365            )
366        })?;
367
368    Ok((source, mir_ctx))
369}
370
371pub fn type_of_hierarchical_value(
372    top_module: &NameID,
373    hierarchy: &[String],
374    instance_map: &InstanceMap,
375    mir_contexts: &HashMap<NameID, MirContext>,
376    symtab: &SymbolTable,
377    item_list: &ItemList,
378) -> color_eyre::Result<ConcreteType> {
379    let (source, mir_ctx) =
380        source_of_hierarchical_value(top_module, hierarchy, instance_map, mir_contexts)?;
381
382    let typed_expr = match source {
383        VerilogNameSource::ForwardName(n) => TypedExpression::Name(n.clone()),
384        VerilogNameSource::ForwardExpr(id) => TypedExpression::Id(*id),
385        VerilogNameSource::BackwardName(_) | VerilogNameSource::BackwardExpr(_) => {
386            return Err(anyhow!("Translation of backward port types is unsupported"))
387        }
388    };
389
390    let ty = typed_expr
391        .try_get_type(&mir_ctx.type_state)
392        .ok_or_else(|| anyhow!("Did not find a type for {}", typed_expr))?;
393
394    let concrete = mir_ctx
395        .type_state
396        .ungenerify_type(&ty, symtab, &item_list.types)
397        .ok_or_else(|| {
398            anyhow!(
399                "Tried to ungenerify generic type {ty}",
400                ty = ty.display(&mir_ctx.type_state)
401            )
402        })?;
403
404    Ok(concrete)
405}