Skip to main content

gaia_assembler/assembler/
mod.rs

1//! Gaia Unified Compiler
2//! 
3//! Provides a unified compilation interface supporting multiple target platforms.
4
5mod performance;
6
7use crate::{backends::*, program::GaiaModule};
8use gaia_types::{helpers::CompilationTarget, GaiaErrorKind, *};
9use std::collections::HashMap;
10use performance::{PerformanceGuard, PerformanceMonitor};
11
12/// Gaia Assembler
13pub struct GaiaAssembler {
14    backends: Vec<Box<dyn Backend>>,
15    backend_cache: std::sync::Arc<std::sync::Mutex<HashMap<CompilationTarget, Box<dyn Backend>>>>,
16    performance_monitor: PerformanceMonitor,
17}
18
19impl GaiaAssembler {
20    /// Create a new assembler instance with all available backends.
21    pub fn new() -> Self {
22        #[allow(unused_mut)]
23        let mut backends: Vec<Box<dyn Backend>> = vec![];
24
25        #[cfg(feature = "jvm-assembler")]
26        backends.push(Box::new(JvmBackend {}));
27        #[cfg(feature = "pe-assembler")]
28        backends.push(Box::new(WindowsBackend {}));
29        #[cfg(feature = "wasi-assembler")]
30        backends.push(Box::new(WasiBackend {}));
31        #[cfg(feature = "x86_64-assembler")]
32        backends.push(Box::new(X64Backend::new()));
33        #[cfg(feature = "gcn-assembler")]
34        backends.push(Box::new(GcnBackend::new()));
35        #[cfg(feature = "sass-assembler")]
36        backends.push(Box::new(SassBackend::new()));
37        #[cfg(feature = "clr-assembler")]
38        backends.push(Box::new(ClrBackend {}));
39        #[cfg(feature = "elf-assembler")]
40        backends.push(Box::new(ElfBackend {}));
41        #[cfg(feature = "macho-assembler")]
42        backends.push(Box::new(MachoBackend {}));
43        #[cfg(feature = "lua-assembler")]
44        backends.push(Box::new(LuaBackend {}));
45        #[cfg(feature = "llvm-assembler")]
46        backends.push(Box::new(LlvmBackend {}));
47        #[cfg(feature = "msl-assembler")]
48        backends.push(Box::new(MslBackend {}));
49        #[cfg(feature = "spirv-assembler")]
50        backends.push(Box::new(SpirvBackend {}));
51        #[cfg(feature = "python-assembler")]
52        backends.push(Box::new(PythonBackend {}));
53        #[cfg(feature = "nyar-assembler")]
54        backends.push(Box::new(NyarBackend {}));
55
56        Self { 
57            backends, 
58            backend_cache: std::sync::Arc::new(std::sync::Mutex::new(HashMap::new())),
59            performance_monitor: PerformanceMonitor::new()
60        }
61    }
62
63    /// Compile a Gaia program for the specified target.
64    pub fn compile(&mut self, program: &GaiaModule, target: &CompilationTarget) -> Result<GeneratedFiles> {
65        self.performance_monitor.start("compile");
66        
67        // Check cache first
68        self.performance_monitor.start("cache_check");
69        let cache_result: Option<Result<GeneratedFiles>> = {
70            if let Ok(mut cache) = self.backend_cache.lock() {
71                if let Some(cached_backend) = cache.get(target) {
72                    let mut config = crate::config::GaiaConfig::default();
73                    config.target = target.clone();
74                    let result = cached_backend.generate(program, &config);
75                    if let Ok(mut generated_files) = result {
76                        // Add backend selection diagnostic
77                        let backend_name = cached_backend.name();
78                        let diagnostic = GaiaError::custom_error(format!("Using cached backend: {}", backend_name));
79                        generated_files.diagnostics.push(diagnostic);
80                        self.performance_monitor.stop("cache_check");
81                        self.performance_monitor.stop("compile");
82                        return Ok(generated_files);
83                    }
84                    self.performance_monitor.stop("cache_check");
85                    self.performance_monitor.stop("compile");
86                    return result;
87                }
88            }
89            None
90        };
91        self.performance_monitor.stop("cache_check");
92
93        // Prioritize backends matching the host exactly to avoid misselection due to scores.
94        let mut best_backend: Option<&Box<dyn Backend>> = None;
95        let mut best_score = 0.0;
96
97        // Try exact match with the primary target of the backend.
98        self.performance_monitor.start("backend_selection");
99        if let Some(candidate) = self.backends.iter().find(|b| {
100            let pt = b.primary_target();
101            pt.host == target.host && pt.build == target.build
102        }) {
103            best_backend = Some(candidate);
104            best_score = 100.0; // Exact match priority
105        }
106
107        // If no exact match found, fall back to scoring.
108        if best_backend.is_none() {
109            for backend in &self.backends {
110                let score = backend.match_score(target);
111                if score > best_score {
112                    best_score = score;
113                    best_backend = Some(backend);
114                }
115            }
116        }
117        self.performance_monitor.stop("backend_selection");
118
119        if best_backend.is_none() || best_score <= 0.0 {
120            self.performance_monitor.stop("compile");
121            return Err(GaiaErrorKind::UnsupportedTarget { target: target.clone() }.into());
122        }
123
124        // Create config and pass the target so the backend can output the correct file type.
125        let mut config = crate::config::GaiaConfig::default();
126        config.target = target.clone();
127
128        // Compile using the selected backend.
129        self.performance_monitor.start("backend_generate");
130        let result = best_backend.unwrap().generate(program, &config);
131        self.performance_monitor.stop("backend_generate");
132        
133        if let Ok(mut generated_files) = result {
134            // Add backend selection diagnostic
135            let backend_name = best_backend.unwrap().name();
136            let diagnostic = GaiaError::custom_error(format!("Selected backend: {} with score: {:.2}", backend_name, best_score));
137            generated_files.diagnostics.push(diagnostic);
138            self.performance_monitor.stop("compile");
139            return Ok(generated_files);
140        }
141        self.performance_monitor.stop("compile");
142        result
143    }
144
145    /// Print performance report
146    pub fn print_performance_report(&self) {
147        self.performance_monitor.print_report();
148    }
149
150    /// Get performance monitor
151    pub fn performance_monitor(&self) -> &PerformanceMonitor {
152        &self.performance_monitor
153    }
154
155    /// Get all available backends.
156    pub fn backends(&self) -> &[Box<dyn Backend>] {
157        &self.backends
158    }
159}
160
161/// Compile to a specific platform.
162pub fn compile_to_platform(program: &GaiaModule, target: CompilationTarget) -> Result<Vec<u8>> {
163    let mut compiler = GaiaAssembler::new();
164    let generated_files = compiler.compile(program, &target)?;
165
166    // Extract the primary binary file from the generated files.
167    // Usually the first file is the primary output file.
168    if let Some((_, bytes)) = generated_files.files.iter().next() {
169        Ok(bytes.clone())
170    }
171    else {
172        Err(GaiaError::invalid_data("No output files generated"))
173    }
174}