gaia-assembler 0.1.1

Universal assembler framework for Gaia project
Documentation
//! Gaia Unified Compiler
//! 
//! Provides a unified compilation interface supporting multiple target platforms.

mod performance;

use crate::{backends::*, program::GaiaModule};
use gaia_types::{helpers::CompilationTarget, GaiaErrorKind, *};
use std::collections::HashMap;
use performance::{PerformanceGuard, PerformanceMonitor};

/// Gaia Assembler
pub struct GaiaAssembler {
    backends: Vec<Box<dyn Backend>>,
    backend_cache: std::sync::Arc<std::sync::Mutex<HashMap<CompilationTarget, Box<dyn Backend>>>>,
    performance_monitor: PerformanceMonitor,
}

impl GaiaAssembler {
    /// Create a new assembler instance with all available backends.
    pub fn new() -> Self {
        #[allow(unused_mut)]
        let mut backends: Vec<Box<dyn Backend>> = vec![];

        #[cfg(feature = "jvm-assembler")]
        backends.push(Box::new(JvmBackend {}));
        #[cfg(feature = "pe-assembler")]
        backends.push(Box::new(WindowsBackend {}));
        #[cfg(feature = "wasi-assembler")]
        backends.push(Box::new(WasiBackend {}));
        #[cfg(feature = "x86_64-assembler")]
        backends.push(Box::new(X64Backend::new()));
        #[cfg(feature = "gcn-assembler")]
        backends.push(Box::new(GcnBackend::new()));
        #[cfg(feature = "sass-assembler")]
        backends.push(Box::new(SassBackend::new()));
        #[cfg(feature = "clr-assembler")]
        backends.push(Box::new(ClrBackend {}));
        #[cfg(feature = "elf-assembler")]
        backends.push(Box::new(ElfBackend {}));
        #[cfg(feature = "macho-assembler")]
        backends.push(Box::new(MachoBackend {}));
        #[cfg(feature = "lua-assembler")]
        backends.push(Box::new(LuaBackend {}));
        #[cfg(feature = "llvm-assembler")]
        backends.push(Box::new(LlvmBackend {}));
        #[cfg(feature = "msl-assembler")]
        backends.push(Box::new(MslBackend {}));
        #[cfg(feature = "spirv-assembler")]
        backends.push(Box::new(SpirvBackend {}));
        #[cfg(feature = "python-assembler")]
        backends.push(Box::new(PythonBackend {}));
        #[cfg(feature = "nyar-assembler")]
        backends.push(Box::new(NyarBackend {}));

        Self { 
            backends, 
            backend_cache: std::sync::Arc::new(std::sync::Mutex::new(HashMap::new())),
            performance_monitor: PerformanceMonitor::new()
        }
    }

    /// Compile a Gaia program for the specified target.
    pub fn compile(&mut self, program: &GaiaModule, target: &CompilationTarget) -> Result<GeneratedFiles> {
        self.performance_monitor.start("compile");
        
        // Check cache first
        self.performance_monitor.start("cache_check");
        let cache_result: Option<Result<GeneratedFiles>> = {
            if let Ok(mut cache) = self.backend_cache.lock() {
                if let Some(cached_backend) = cache.get(target) {
                    let mut config = crate::config::GaiaConfig::default();
                    config.target = target.clone();
                    let result = cached_backend.generate(program, &config);
                    if let Ok(mut generated_files) = result {
                        // Add backend selection diagnostic
                        let backend_name = cached_backend.name();
                        let diagnostic = GaiaError::custom_error(format!("Using cached backend: {}", backend_name));
                        generated_files.diagnostics.push(diagnostic);
                        self.performance_monitor.stop("cache_check");
                        self.performance_monitor.stop("compile");
                        return Ok(generated_files);
                    }
                    self.performance_monitor.stop("cache_check");
                    self.performance_monitor.stop("compile");
                    return result;
                }
            }
            None
        };
        self.performance_monitor.stop("cache_check");

        // Prioritize backends matching the host exactly to avoid misselection due to scores.
        let mut best_backend: Option<&Box<dyn Backend>> = None;
        let mut best_score = 0.0;

        // Try exact match with the primary target of the backend.
        self.performance_monitor.start("backend_selection");
        if let Some(candidate) = self.backends.iter().find(|b| {
            let pt = b.primary_target();
            pt.host == target.host && pt.build == target.build
        }) {
            best_backend = Some(candidate);
            best_score = 100.0; // Exact match priority
        }

        // If no exact match found, fall back to scoring.
        if best_backend.is_none() {
            for backend in &self.backends {
                let score = backend.match_score(target);
                if score > best_score {
                    best_score = score;
                    best_backend = Some(backend);
                }
            }
        }
        self.performance_monitor.stop("backend_selection");

        if best_backend.is_none() || best_score <= 0.0 {
            self.performance_monitor.stop("compile");
            return Err(GaiaErrorKind::UnsupportedTarget { target: target.clone() }.into());
        }

        // Create config and pass the target so the backend can output the correct file type.
        let mut config = crate::config::GaiaConfig::default();
        config.target = target.clone();

        // Compile using the selected backend.
        self.performance_monitor.start("backend_generate");
        let result = best_backend.unwrap().generate(program, &config);
        self.performance_monitor.stop("backend_generate");
        
        if let Ok(mut generated_files) = result {
            // Add backend selection diagnostic
            let backend_name = best_backend.unwrap().name();
            let diagnostic = GaiaError::custom_error(format!("Selected backend: {} with score: {:.2}", backend_name, best_score));
            generated_files.diagnostics.push(diagnostic);
            self.performance_monitor.stop("compile");
            return Ok(generated_files);
        }
        self.performance_monitor.stop("compile");
        result
    }

    /// Print performance report
    pub fn print_performance_report(&self) {
        self.performance_monitor.print_report();
    }

    /// Get performance monitor
    pub fn performance_monitor(&self) -> &PerformanceMonitor {
        &self.performance_monitor
    }

    /// Get all available backends.
    pub fn backends(&self) -> &[Box<dyn Backend>] {
        &self.backends
    }
}

/// Compile to a specific platform.
pub fn compile_to_platform(program: &GaiaModule, target: CompilationTarget) -> Result<Vec<u8>> {
    let mut compiler = GaiaAssembler::new();
    let generated_files = compiler.compile(program, &target)?;

    // Extract the primary binary file from the generated files.
    // Usually the first file is the primary output file.
    if let Some((_, bytes)) = generated_files.files.iter().next() {
        Ok(bytes.clone())
    }
    else {
        Err(GaiaError::invalid_data("No output files generated"))
    }
}