Skip to main content

chipi_core/
backend.rs

1//! Code generation backends.
2//!
3//! Each backend implements [`CodegenBackend`]. Each one emits source code
4//! for one target language.
5
6pub mod binja;
7pub mod cpp;
8pub mod ida;
9pub mod rust;
10
11use crate::config::GenTarget;
12use crate::types::ValidatedDef;
13
14/// Operand classification shared across backends.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum OperandKind {
17    Register,
18    Immediate,
19    Address,
20    Memory,
21}
22
23/// Flow analysis configuration shared across backends.
24#[derive(Debug, Clone, Default)]
25pub struct FlowConfig {
26    pub calls: Vec<String>,
27    pub branches: Vec<String>,
28    pub unconditional_branches: Vec<String>,
29    pub returns: Vec<String>,
30    pub stops: Vec<String>,
31}
32
33/// Trait for language-specific code generation backends.
34pub trait CodegenBackend {
35    /// Language identifier. Examples: `"rust"`, `"cpp"`, `"ida"`, `"binja"`.
36    fn lang(&self) -> &str;
37
38    /// Generate source code from the decoder IR and configuration.
39    fn generate(&self, ir: &ValidatedDef, config: &GenTarget) -> Result<String, CodegenError>;
40
41    /// Command to format the generated source. `None` skips formatting.
42    fn formatter_command(&self) -> Option<&[&str]>;
43}
44
45#[derive(Debug)]
46pub enum CodegenError {
47    Internal(String),
48}
49
50impl std::fmt::Display for CodegenError {
51    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52        match self {
53            CodegenError::Internal(msg) => write!(f, "codegen error: {}", msg),
54        }
55    }
56}
57
58impl std::error::Error for CodegenError {}
59
60/// Get a backend by language name.
61pub fn get_backend(lang: &str) -> Option<Box<dyn CodegenBackend>> {
62    match lang {
63        "rust" => Some(Box::new(rust::RustBackend)),
64        "ida" => Some(Box::new(ida::IdaBackend)),
65        "binja" => Some(Box::new(binja::BinjaBackend)),
66        "cpp" => Some(Box::new(cpp::CppBackend)),
67        _ => None,
68    }
69}
70
71/// Run a formatter command on a file. Warns on failure; does not error.
72pub fn run_formatter(cmd: &[&str], path: &str) {
73    let status = std::process::Command::new(cmd[0])
74        .args(&cmd[1..])
75        .arg(path)
76        .status();
77
78    match status {
79        Ok(s) if s.success() => {}
80        Ok(s) => eprintln!("warning: formatter exited with {}", s),
81        Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
82            eprintln!("warning: formatter '{}' not found, skipping", cmd[0]);
83        }
84        Err(e) => eprintln!("warning: failed to run formatter: {}", e),
85    }
86}