Skip to main content

chipi_core/bindings/
run.rs

1//! Execute lowered bindings.
2//!
3//! Drives the codegen backends to write output files. Or prints a
4//! human-readable preview.
5
6use std::collections::HashMap;
7use std::path::{Path, PathBuf};
8
9use crate::backend::{self, run_formatter};
10use crate::config::{
11    self, BinjaOptions, GenTarget, IdaOptions, LangOptions, LutTarget, resolve_gen_paths,
12    resolve_lut_paths,
13};
14use crate::error::{Error, ErrorKind, Errors, Span};
15use crate::instr_gen;
16use crate::lut_gen::{self, generate_lut_code, generate_subdecoder_flat_dispatch};
17use crate::tree;
18use crate::types::ValidatedDef;
19use crate::validate::validate as validate_spec;
20
21use super::lower::{LoweredBindings, LoweredItem, LoweredKind, lower_resolved};
22use super::parser::parse_file_with_includes;
23use super::resolve::{ResolvedBindings, resolve};
24use super::types::{TargetBinding, TargetKind};
25use super::validate::validate as validate_bindings;
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum RunMode {
29    Generate,
30    Preview,
31    Check,
32}
33
34/// One-shot pipeline. Runs parse, resolve, validate, lower, and execute.
35pub fn run(
36    bindings_path: &Path,
37    target_filter: Option<&str>,
38    decoder_filter: Option<&str>,
39    mode: RunMode,
40) -> Result<(), Vec<Error>> {
41    let parsed = parse_file_with_includes(bindings_path)?;
42    let resolved = resolve(parsed)?;
43    validate_bindings(&resolved)?;
44    let lowered = lower_resolved(&resolved)?;
45    run_lowered(
46        &resolved,
47        &lowered,
48        bindings_path,
49        target_filter,
50        decoder_filter,
51        mode,
52    )
53}
54
55/// Run a pre-resolved and pre-lowered set of items.
56/// The CLI uses this so it can also print resolution and dependency lists.
57pub fn run_lowered(
58    resolved: &ResolvedBindings,
59    lowered: &LoweredBindings,
60    bindings_path: &Path,
61    target_filter: Option<&str>,
62    decoder_filter: Option<&str>,
63    mode: RunMode,
64) -> Result<(), Vec<Error>> {
65    if mode == RunMode::Check {
66        // Validation already ran; nothing else to do.
67        return Ok(());
68    }
69
70    // Filter by target
71    let target_kind = match target_filter {
72        Some(t) => Some(parse_target_kind(t)?),
73        None => None,
74    };
75    let target_kinds_present: Vec<TargetKind> =
76        resolved.file.targets.iter().map(|t| t.kind).collect();
77    if target_kind.is_none() && distinct(&target_kinds_present).len() > 1 {
78        return Err(vec![Error::new(
79            ErrorKind::MultipleTargetsAmbiguous(
80                distinct(&target_kinds_present)
81                    .iter()
82                    .map(|k| k.name().to_string())
83                    .collect(),
84            ),
85            Span::new(&bindings_path.display().to_string(), 1, 1, 0),
86        )]);
87    }
88
89    let base_dir = bindings_path
90        .parent()
91        .map(|p| p.to_path_buf())
92        .unwrap_or_else(|| PathBuf::from("."));
93
94    let filtered: Vec<&LoweredItem> = lowered
95        .items
96        .iter()
97        .filter(|it| target_kind.map_or(true, |k| k == it.target_kind))
98        .filter(|it| decoder_filter.map_or(true, |d| d == it.decoder_name))
99        .collect();
100
101    if filtered.is_empty() {
102        return Err(vec![Error::new(
103            ErrorKind::BindingsParse(format!(
104                "no targets match {}{}",
105                target_filter
106                    .map(|s| format!("--target {} ", s))
107                    .unwrap_or_default(),
108                decoder_filter
109                    .map(|s| format!("--decoder {}", s))
110                    .unwrap_or_default()
111            )),
112            Span::new(&bindings_path.display().to_string(), 1, 1, 0),
113        )]);
114    }
115
116    // Decoder ambiguity: if a decoder filter is needed for the requested
117    // target but absent and multiple decoders exist
118    if decoder_filter.is_none() {
119        let names: Vec<String> = filtered.iter().map(|i| i.decoder_name.clone()).collect();
120        if distinct(&names).len() > 1
121            && lowered.items.len() > filtered.len()
122            && target_kind.is_none()
123        {
124            // multi-target multi-decoder -> ambiguous targets already handled
125        } else if distinct(&names).len() > 1 && target_kind.is_some() {
126            return Err(vec![Error::new(
127                ErrorKind::MultipleDecodersAmbiguous(distinct(&names)),
128                Span::new(&bindings_path.display().to_string(), 1, 1, 0),
129            )]);
130        }
131    }
132
133    for item in &filtered {
134        match &item.kind {
135            LoweredKind::Gen(g) => run_gen(g, &base_dir, mode)?,
136            LoweredKind::Lut(l) => run_lut(l, &base_dir, mode)?,
137            LoweredKind::InstrType {
138                input,
139                output,
140                struct_name,
141                subdecoder,
142            } => run_instr_type(input, output, struct_name, subdecoder.as_deref(), &base_dir)?,
143        }
144    }
145
146    Ok(())
147}
148
149fn parse_target_kind(name: &str) -> Result<TargetKind, Vec<Error>> {
150    Ok(match name {
151        "rust" => TargetKind::Rust,
152        "cpp" | "c++" => TargetKind::Cpp,
153        "ida" => TargetKind::Ida,
154        "binja" => TargetKind::Binja,
155        other => {
156            return Err(vec![Error::new(
157                ErrorKind::UnknownTargetKind(other.to_string()),
158                Span::new("<cli>", 1, 1, 0),
159            )]);
160        }
161    })
162}
163
164fn distinct<T: Clone + Eq>(v: &[T]) -> Vec<T> {
165    let mut out: Vec<T> = Vec::new();
166    for x in v {
167        if !out.iter().any(|y| y == x) {
168            out.push(x.clone());
169        }
170    }
171    out
172}
173
174fn run_gen(target: &GenTarget, base_dir: &Path, mode: RunMode) -> Result<(), Vec<Error>> {
175    let mut t = target.clone();
176    resolve_gen_paths(&mut t, base_dir);
177
178    if mode == RunMode::Preview {
179        print_gen_preview(&t);
180        return Ok(());
181    }
182
183    let (def, _deps) = crate::parser::parse_file_with_deps(Path::new(&t.input))?;
184    let validated = validate_spec(&def)?;
185    let backend = backend::get_backend(&t.lang).ok_or_else(|| {
186        vec![Error::new(
187            ErrorKind::BindingsParse(format!("unknown backend '{}'", t.lang)),
188            Span::new("<lower>", 0, 0, 0),
189        )]
190    })?;
191    let code = backend.generate(&validated, &t).map_err(|e| {
192        vec![Error::new(
193            ErrorKind::BindingsParse(format!("codegen error: {}", e)),
194            Span::new("<lower>", 0, 0, 0),
195        )]
196    })?;
197    std::fs::write(&t.output, code).map_err(|e| {
198        vec![Error::new(
199            ErrorKind::BindingsParse(format!("failed to write {}: {}", t.output, e)),
200            Span::new("<lower>", 0, 0, 0),
201        )]
202    })?;
203    if t.format {
204        if let Some(cmd) = backend.formatter_command() {
205            run_formatter(cmd, &t.output);
206        }
207    }
208    Ok(())
209}
210
211fn run_lut(target: &LutTarget, base_dir: &Path, mode: RunMode) -> Result<(), Vec<Error>> {
212    let mut t = target.clone();
213    resolve_lut_paths(&mut t, base_dir);
214
215    if mode == RunMode::Preview {
216        print_lut_preview(&t);
217        return Ok(());
218    }
219
220    let (def, _deps) = crate::parser::parse_file_with_deps(Path::new(&t.input))?;
221    let validated = validate_spec(&def)?;
222    let tree = tree::build_tree(&validated);
223
224    // Build group map (instr_name -> handler_fn_name)
225    let mut instr_to_group: HashMap<String, String> = HashMap::new();
226    for (group, instrs) in &t.groups {
227        for instr in instrs {
228            instr_to_group.insert(instr.clone(), group.clone());
229        }
230    }
231
232    let mut code = generate_lut_code(
233        &validated,
234        &tree,
235        &t.handler_mod,
236        &t.ctx_type,
237        &instr_to_group,
238        t.instr_type.as_deref(),
239        t.raw_expr.as_deref(),
240        t.dispatch,
241        t.invalid_handler.as_deref(),
242        &t.handler_consts,
243    )?;
244
245    // Sub-decoder dispatch generation
246    for sd in &validated.sub_decoders {
247        let strategy = t
248            .subdecoder_dispatch
249            .get(&sd.name)
250            .copied()
251            .unwrap_or(t.dispatch);
252        let sd_handler_mod = t
253            .subdecoder_handler_mods
254            .get(&sd.name)
255            .cloned()
256            .unwrap_or_else(|| t.handler_mod.clone());
257        let sd_invalid = t
258            .subdecoder_invalid_handlers
259            .get(&sd.name)
260            .cloned()
261            .or_else(|| t.invalid_handler.clone());
262        let sd_groups: HashMap<String, String> = t
263            .subdecoder_groups
264            .get(&sd.name)
265            .map(|g| {
266                let mut m = HashMap::new();
267                for (group, instrs) in g {
268                    for instr in instrs {
269                        m.insert(instr.clone(), group.clone());
270                    }
271                }
272                m
273            })
274            .unwrap_or_default();
275        let sd_instr_type = t.subdecoder_instr_types.get(&sd.name).cloned();
276
277        match strategy {
278            config::Dispatch::FlatLut | config::Dispatch::FlatMatch => {
279                code.push('\n');
280                let block = generate_subdecoder_flat_dispatch(
281                    sd,
282                    &sd_handler_mod,
283                    &t.ctx_type,
284                    &sd_groups,
285                    sd_instr_type.as_deref(),
286                    sd_invalid.as_deref(),
287                    strategy,
288                    &t.handler_consts,
289                )?;
290                code.push_str(&block);
291            }
292            _ => {
293                // Existing wide-match flat-style dispatch (precedence-based).
294                if !sd_groups.is_empty() || t.subdecoder_dispatch.contains_key(&sd.name) {
295                    code.push('\n');
296                    code.push_str(&lut_gen::generate_subdecoder_dispatch(
297                        &validated,
298                        sd,
299                        &sd_handler_mod,
300                        &t.ctx_type,
301                        &sd_groups,
302                        sd_instr_type.as_deref(),
303                        &t.handler_consts,
304                    ));
305                }
306            }
307        }
308    }
309
310    std::fs::write(&t.output, code).map_err(|e| {
311        vec![Error::new(
312            ErrorKind::BindingsParse(format!("failed to write {}: {}", t.output, e)),
313            Span::new("<lower>", 0, 0, 0),
314        )]
315    })?;
316
317    // Optional instruction-type newtype output
318    if let Some(it_out) = &t.instr_type_output {
319        let struct_name = t
320            .instr_type
321            .as_deref()
322            .and_then(|p| p.rsplit("::").next())
323            .unwrap_or("Instruction");
324        let (it_code, _warnings) = instr_gen::generate_instr_type(&validated, struct_name);
325        std::fs::write(it_out, it_code).map_err(|e| {
326            vec![Error::new(
327                ErrorKind::BindingsParse(format!("failed to write {}: {}", it_out, e)),
328                Span::new("<lower>", 0, 0, 0),
329            )]
330        })?;
331    }
332
333    // Sub-decoder instr-type newtype outputs
334    for (sd_name, out) in &t.subdecoder_instr_type_outputs {
335        let sd = validated
336            .sub_decoders
337            .iter()
338            .find(|s| &s.name == sd_name)
339            .ok_or_else(|| {
340                vec![Error::new(
341                    ErrorKind::UnknownDecoderInBinding {
342                        name: sd_name.clone(),
343                        suggestion: None,
344                    },
345                    Span::new("<lower>", 0, 0, 0),
346                )]
347            })?;
348        let (it_code, _warnings) = instr_gen::generate_subdecoder_instr_type(sd, sd_name);
349        std::fs::write(out, it_code).map_err(|e| {
350            vec![Error::new(
351                ErrorKind::BindingsParse(format!("failed to write {}: {}", out, e)),
352                Span::new("<lower>", 0, 0, 0),
353            )]
354        })?;
355    }
356
357    Ok(())
358}
359
360fn run_instr_type(
361    input: &Path,
362    output: &str,
363    struct_name: &str,
364    subdecoder: Option<&str>,
365    base_dir: &Path,
366) -> Result<(), Vec<Error>> {
367    let _ = base_dir;
368    let (def, _) = crate::parser::parse_file_with_deps(input)?;
369    let validated = validate_spec(&def)?;
370    let (code, _warnings) = match subdecoder {
371        Some(name) => {
372            let sd = validated
373                .sub_decoders
374                .iter()
375                .find(|s| s.name == name)
376                .ok_or_else(|| {
377                    vec![Error::new(
378                        ErrorKind::UnknownDecoderInBinding {
379                            name: name.to_string(),
380                            suggestion: None,
381                        },
382                        Span::new("<instr-type>", 0, 0, 0),
383                    )]
384                })?;
385            instr_gen::generate_subdecoder_instr_type(sd, struct_name)
386        }
387        None => instr_gen::generate_instr_type(&validated, struct_name),
388    };
389    std::fs::write(output, code).map_err(|e| {
390        vec![Error::new(
391            ErrorKind::BindingsParse(format!("failed to write {}: {}", output, e)),
392            Span::new("<instr-type>", 0, 0, 0),
393        )]
394    })?;
395    Ok(())
396}
397
398fn print_gen_preview(t: &GenTarget) {
399    println!("target: {}", t.lang);
400    println!();
401    println!("decoder:");
402    println!("  input: {}", t.input);
403    println!("  output: {}", t.output);
404    let mut keys: Vec<&String> = t.type_map.keys().collect();
405    keys.sort();
406    for k in keys {
407        println!("  type {} -> {}", k, t.type_map[k]);
408    }
409    match &t.lang_options {
410        LangOptions::Ida(o) => print_ida_preview(o),
411        LangOptions::Binja(o) => print_binja_preview(o),
412        _ => {}
413    }
414    println!();
415}
416
417fn print_ida_preview(o: &IdaOptions) {
418    println!();
419    println!("processor:");
420    println!("  name: {}", o.processor_name);
421    println!("  long name: {}", o.processor_long_name);
422    println!("  id: {:#x}", o.processor_id);
423    println!("  address size: {}", o.address_size);
424    println!("  bytes per unit: {}", o.bytes_per_unit);
425    println!();
426    println!("registers:");
427    for r in &o.register_names {
428        println!("  {}", r);
429    }
430    if !o.segment_registers.is_empty() {
431        println!();
432        println!("segment registers:");
433        for r in &o.segment_registers {
434            println!("  {}", r);
435        }
436    }
437    println!();
438    println!("flow:");
439    println!("  calls: {}", o.flow.calls.join(", "));
440    println!("  returns: {}", o.flow.returns.join(", "));
441    println!("  stops: {}", o.flow.stops.join(", "));
442}
443
444fn print_binja_preview(o: &BinjaOptions) {
445    println!();
446    println!("architecture:");
447    println!("  name: {}", o.architecture_name);
448    println!("  address size: {}", o.address_size);
449    println!("  default int size: {}", o.default_int_size);
450    println!("  endianness: {}", o.endianness);
451    println!();
452    println!("registers:");
453    for r in &o.register_names {
454        println!("  {}", r);
455    }
456}
457
458fn print_lut_preview(t: &LutTarget) {
459    println!("dispatch:");
460    println!("  input: {}", t.input);
461    println!("  output: {}", t.output);
462    println!("  context: {}", t.ctx_type);
463    println!("  handlers: {}", t.handler_mod);
464    println!("  strategy: {:?}", t.dispatch);
465    if let Some(ih) = &t.invalid_handler {
466        println!("  invalid handler: {}", ih);
467    }
468    if let Some(it) = &t.instr_type {
469        println!("  instruction_type: {}", it);
470    }
471    if !t.groups.is_empty() {
472        println!();
473        println!("handlers:");
474        let mut keys: Vec<&String> = t.groups.keys().collect();
475        keys.sort();
476        for g in keys {
477            for instr in &t.groups[g] {
478                println!(
479                    "  {} -> {}::{}::<{{ OP_{} }}>",
480                    instr,
481                    t.handler_mod,
482                    g,
483                    instr.to_uppercase()
484                );
485            }
486        }
487    }
488    println!();
489}
490
491// Re-export helpers used from validate
492fn _unused(_: &TargetBinding, _: &ValidatedDef, _: Errors) {}