Skip to main content

chipi_core/bindings/
lower.rs

1//! Lower a resolved bindings model into internal `GenTarget` and
2//! `LutTarget` structures. Those are what the codegen pipeline consumes.
3
4use std::collections::HashMap;
5use std::path::PathBuf;
6
7use crate::backend::{FlowConfig, OperandKind};
8use crate::config::{
9    BinjaOptions, CppGuardStyle, CppOptions, Dispatch, GenTarget, IdaOptions, LangOptions,
10    LutTarget,
11};
12use crate::error::{Error, ErrorKind};
13
14use super::resolve::ResolvedBindings;
15use super::types::*;
16
17/// One lowered target. The kind is either decoder-style codegen
18/// (`GenTarget`) or emulator-dispatch (`LutTarget`). Each value keeps its
19/// `target` and `decoder` keys so CLI filters can pick a subset.
20pub struct LoweredItem {
21    pub target_kind: TargetKind,
22    pub decoder_name: String,
23    pub kind: LoweredKind,
24}
25
26pub enum LoweredKind {
27    Gen(GenTarget),
28    Lut(LutTarget),
29    /// Generate an instruction-type newtype only.
30    /// Carries the output path and struct name. `subdecoder` is `Some`
31    /// when the newtype is for a sub-decoder.
32    InstrType {
33        input: PathBuf,
34        output: String,
35        struct_name: String,
36        subdecoder: Option<String>,
37    },
38}
39
40pub struct LoweredBindings {
41    pub items: Vec<LoweredItem>,
42}
43
44pub fn lower_resolved(resolved: &ResolvedBindings) -> Result<LoweredBindings, Vec<Error>> {
45    let mut items: Vec<LoweredItem> = Vec::new();
46    let mut errors: Vec<Error> = Vec::new();
47
48    for target in &resolved.file.targets {
49        match target.kind {
50            TargetKind::Rust => {
51                for d in &target.rust_decoders {
52                    if let Err(mut errs) = lower_rust_decoder(resolved, d, &mut items) {
53                        errors.append(&mut errs);
54                    }
55                }
56                for d in &target.rust_dispatches {
57                    if let Err(mut errs) = lower_rust_dispatch(resolved, d, None, &mut items) {
58                        errors.append(&mut errs);
59                    }
60                }
61            }
62            TargetKind::Cpp => {
63                for d in &target.cpp_decoders {
64                    if let Err(mut errs) = lower_cpp_decoder(resolved, d, &mut items) {
65                        errors.append(&mut errs);
66                    }
67                }
68            }
69            TargetKind::Ida => {
70                for p in &target.ida_processors {
71                    if let Err(mut errs) = lower_ida_processor(resolved, p, &mut items) {
72                        errors.append(&mut errs);
73                    }
74                }
75            }
76            TargetKind::Binja => {
77                for a in &target.binja_architectures {
78                    if let Err(mut errs) = lower_binja_architecture(resolved, a, &mut items) {
79                        errors.append(&mut errs);
80                    }
81                }
82            }
83        }
84    }
85
86    if errors.is_empty() {
87        Ok(LoweredBindings { items })
88    } else {
89        Err(errors)
90    }
91}
92
93fn spec_path(resolved: &ResolvedBindings, decoder_name: &str) -> Result<PathBuf, Error> {
94    if let Some((spec, _)) = resolved.find_decoder_or_sub(decoder_name) {
95        Ok(spec.path.clone())
96    } else {
97        Err(Error::new(
98            ErrorKind::UnknownDecoderInBinding {
99                name: decoder_name.to_string(),
100                suggestion: None,
101            },
102            crate::error::Span::new("<lower>", 0, 0, 0),
103        ))
104    }
105}
106
107fn lower_rust_decoder(
108    resolved: &ResolvedBindings,
109    d: &DecoderBinding,
110    items: &mut Vec<LoweredItem>,
111) -> Result<(), Vec<Error>> {
112    let input = spec_path(resolved, &d.decoder_name).map_err(|e| vec![e])?;
113    let mut type_map = HashMap::new();
114    for (k, v) in &d.type_map {
115        type_map.insert(k.clone(), v.clone());
116    }
117    let target = GenTarget {
118        input: input.to_string_lossy().into_owned(),
119        lang: "rust".to_string(),
120        output: d.output.clone(),
121        format: false,
122        dispatch: Dispatch::default(),
123        dispatch_overrides: HashMap::new(),
124        type_map,
125        lang_options: LangOptions::None,
126    };
127    items.push(LoweredItem {
128        target_kind: TargetKind::Rust,
129        decoder_name: d.decoder_name.clone(),
130        kind: LoweredKind::Gen(target),
131    });
132
133    // Sub-decoders share the same generated decoder file in the existing
134    // backend, but the user may still want a separate output for nested
135    // sub-decoder type maps. Today the Rust backend doesn't generate a
136    // separate file per sub-decoder beyond what the parent decoder emits,
137    // so we ignore subdecoders here. (The dispatch side handles them.)
138    let _ = &d.subdecoders;
139
140    Ok(())
141}
142
143fn lower_cpp_decoder(
144    resolved: &ResolvedBindings,
145    d: &DecoderBinding,
146    items: &mut Vec<LoweredItem>,
147) -> Result<(), Vec<Error>> {
148    let input = spec_path(resolved, &d.decoder_name).map_err(|e| vec![e])?;
149    let mut type_map = HashMap::new();
150    for (k, v) in &d.type_map {
151        type_map.insert(k.clone(), v.clone());
152    }
153    let guard_style = match d.cpp_guard_style.as_deref() {
154        Some("ifndef") => CppGuardStyle::Ifndef,
155        _ => CppGuardStyle::Pragma,
156    };
157    let cpp = CppOptions {
158        namespace: d.cpp_namespace.clone(),
159        guard_style,
160        includes: d.cpp_includes.clone(),
161    };
162    let target = GenTarget {
163        input: input.to_string_lossy().into_owned(),
164        lang: "cpp".to_string(),
165        output: d.output.clone(),
166        format: false,
167        dispatch: Dispatch::default(),
168        dispatch_overrides: HashMap::new(),
169        type_map,
170        lang_options: LangOptions::Cpp(cpp),
171    };
172    items.push(LoweredItem {
173        target_kind: TargetKind::Cpp,
174        decoder_name: d.decoder_name.clone(),
175        kind: LoweredKind::Gen(target),
176    });
177    Ok(())
178}
179
180fn lower_ida_processor(
181    resolved: &ResolvedBindings,
182    p: &IdaProcessorBinding,
183    items: &mut Vec<LoweredItem>,
184) -> Result<(), Vec<Error>> {
185    let input = spec_path(resolved, &p.decoder_name).map_err(|e| vec![e])?;
186    let opts = IdaOptions {
187        processor_name: p.name.clone().unwrap_or_default(),
188        processor_long_name: p.long_name.clone().unwrap_or_default(),
189        processor_id: p.id.unwrap_or(0),
190        register_names: p.registers.clone(),
191        segment_registers: p.segment_registers.iter().map(|(s, _)| s.clone()).collect(),
192        address_size: p.address_size.unwrap_or(32),
193        bytes_per_unit: p.bytes_per_unit.unwrap_or(1),
194        flags: Vec::new(),
195        operand_types: HashMap::<String, OperandKind>::new(),
196        display_prefixes: HashMap::new(),
197        flow: FlowConfig {
198            calls: p.flow.calls.iter().map(|(s, _)| s.clone()).collect(),
199            branches: Vec::new(),
200            unconditional_branches: Vec::new(),
201            returns: p.flow.returns.iter().map(|(s, _)| s.clone()).collect(),
202            stops: p.flow.stops.iter().map(|(s, _)| s.clone()).collect(),
203        },
204    };
205    let output = p.output.clone().unwrap_or_default();
206    let target = GenTarget {
207        input: input.to_string_lossy().into_owned(),
208        lang: "ida".to_string(),
209        output,
210        format: false,
211        dispatch: Dispatch::default(),
212        dispatch_overrides: HashMap::new(),
213        type_map: HashMap::new(),
214        lang_options: LangOptions::Ida(opts),
215    };
216    items.push(LoweredItem {
217        target_kind: TargetKind::Ida,
218        decoder_name: p.decoder_name.clone(),
219        kind: LoweredKind::Gen(target),
220    });
221    Ok(())
222}
223
224fn lower_binja_architecture(
225    resolved: &ResolvedBindings,
226    a: &BinjaArchitectureBinding,
227    items: &mut Vec<LoweredItem>,
228) -> Result<(), Vec<Error>> {
229    let input = spec_path(resolved, &a.decoder_name).map_err(|e| vec![e])?;
230    let endian = a
231        .endianness
232        .as_ref()
233        .map(|(s, _)| match s.as_str() {
234            "big" => "BigEndian",
235            "little" => "LittleEndian",
236            _ => "LittleEndian",
237        })
238        .unwrap_or("LittleEndian")
239        .to_string();
240    let address_size = a.address_size.unwrap_or(4);
241    let opts = BinjaOptions {
242        architecture_name: a.name.clone().unwrap_or_default(),
243        address_size,
244        default_int_size: a.default_int_size.unwrap_or(address_size),
245        max_instr_length: address_size,
246        endianness: endian,
247        register_names: a.registers.clone(),
248        register_size: address_size,
249        stack_pointer: None,
250        link_register: None,
251        bytes_per_unit: 1,
252        display_prefixes: HashMap::new(),
253        operand_types: HashMap::<String, OperandKind>::new(),
254        flow: FlowConfig::default(),
255    };
256    let output = a.output.clone().unwrap_or_default();
257    let target = GenTarget {
258        input: input.to_string_lossy().into_owned(),
259        lang: "binja".to_string(),
260        output,
261        format: false,
262        dispatch: Dispatch::default(),
263        dispatch_overrides: HashMap::new(),
264        type_map: HashMap::new(),
265        lang_options: LangOptions::Binja(opts),
266    };
267    items.push(LoweredItem {
268        target_kind: TargetKind::Binja,
269        decoder_name: a.decoder_name.clone(),
270        kind: LoweredKind::Gen(target),
271    });
272    Ok(())
273}
274
275fn lower_rust_dispatch(
276    resolved: &ResolvedBindings,
277    d: &DispatchBinding,
278    parent: Option<&DispatchBinding>,
279    items: &mut Vec<LoweredItem>,
280) -> Result<(), Vec<Error>> {
281    let input = spec_path(resolved, &d.decoder_name).map_err(|e| vec![e])?;
282
283    let context = d
284        .context
285        .clone()
286        .or_else(|| parent.and_then(|p| p.context.clone()))
287        .unwrap_or_default();
288    let handlers = d
289        .handlers
290        .clone()
291        .or_else(|| parent.and_then(|p| p.handlers.clone()))
292        .unwrap_or_default();
293    let strategy = d
294        .strategy
295        .or_else(|| parent.and_then(|p| p.strategy))
296        .unwrap_or(Dispatch::FnPtrLut);
297    let invalid_handler = d
298        .invalid_handler
299        .clone()
300        .or_else(|| parent.and_then(|p| p.invalid_handler.clone()));
301
302    let mut groups: HashMap<String, Vec<String>> = HashMap::new();
303    for g in &d.handler_groups {
304        let names: Vec<String> = g.instructions.iter().map(|(n, _)| n.clone()).collect();
305        groups.insert(g.handler_name.clone(), names);
306    }
307
308    let instr_type = d
309        .instruction_type
310        .as_ref()
311        .map(|it| it.type_path.clone())
312        .or_else(|| {
313            parent.and_then(|p| p.instruction_type.as_ref().map(|it| it.type_path.clone()))
314        });
315    let raw_expr = if instr_type.is_some() {
316        Some("instr.0".to_string())
317    } else {
318        None
319    };
320    let instr_type_output = d.instruction_type.as_ref().and_then(|it| it.output.clone());
321
322    let lut_mod: Option<String> = None;
323
324    let mut subdecoder_groups: HashMap<String, HashMap<String, Vec<String>>> = HashMap::new();
325    let mut subdecoder_instr_types: HashMap<String, String> = HashMap::new();
326    let mut subdecoder_instr_type_outputs: HashMap<String, String> = HashMap::new();
327    let mut subdecoder_dispatch: HashMap<String, Dispatch> = HashMap::new();
328    let mut subdecoder_invalid_handlers: HashMap<String, String> = HashMap::new();
329    let mut subdecoder_handler_mods: HashMap<String, String> = HashMap::new();
330
331    for sd in &d.subdispatches {
332        let mut sg: HashMap<String, Vec<String>> = HashMap::new();
333        for g in &sd.handler_groups {
334            let names: Vec<String> = g.instructions.iter().map(|(n, _)| n.clone()).collect();
335            sg.insert(g.handler_name.clone(), names);
336        }
337        if !sg.is_empty() {
338            subdecoder_groups.insert(sd.decoder_name.clone(), sg);
339        }
340        if let Some(it) = &sd.instruction_type {
341            subdecoder_instr_types.insert(sd.decoder_name.clone(), it.type_path.clone());
342            if let Some(out) = &it.output {
343                subdecoder_instr_type_outputs.insert(sd.decoder_name.clone(), out.clone());
344            }
345        }
346        let strat = sd.strategy.unwrap_or(strategy);
347        subdecoder_dispatch.insert(sd.decoder_name.clone(), strat);
348
349        if let Some(h) = &sd.invalid_handler {
350            subdecoder_invalid_handlers.insert(sd.decoder_name.clone(), h.clone());
351        } else if let Some(h) = &invalid_handler {
352            subdecoder_invalid_handlers.insert(sd.decoder_name.clone(), h.clone());
353        }
354        if let Some(h) = &sd.handlers {
355            subdecoder_handler_mods.insert(sd.decoder_name.clone(), h.clone());
356        }
357    }
358
359    let target = LutTarget {
360        input: input.to_string_lossy().into_owned(),
361        output: d.output.clone().unwrap_or_default(),
362        handler_mod: handlers,
363        ctx_type: context,
364        dispatch: strategy,
365        groups,
366        lut_mod,
367        instr_type,
368        raw_expr,
369        instr_type_output,
370        subdecoder_groups,
371        subdecoder_instr_type_outputs,
372        subdecoder_instr_types,
373        subdecoder_dispatch,
374        invalid_handler,
375        subdecoder_invalid_handlers,
376        subdecoder_handler_mods,
377    };
378    items.push(LoweredItem {
379        target_kind: TargetKind::Rust,
380        decoder_name: d.decoder_name.clone(),
381        kind: LoweredKind::Lut(target),
382    });
383
384    // Subdispatches feed in via the sub_decoder_* fields above; we don't
385    // emit a separate LoweredItem per subdispatch.
386    let _ = parent;
387
388    Ok(())
389}