mod performance;
use crate::{backends::*, program::GaiaModule};
use gaia_types::{helpers::CompilationTarget, GaiaErrorKind, *};
use std::collections::HashMap;
use performance::{PerformanceGuard, PerformanceMonitor};
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 {
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()
}
}
pub fn compile(&mut self, program: &GaiaModule, target: &CompilationTarget) -> Result<GeneratedFiles> {
self.performance_monitor.start("compile");
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 {
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");
let mut best_backend: Option<&Box<dyn Backend>> = None;
let mut best_score = 0.0;
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; }
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());
}
let mut config = crate::config::GaiaConfig::default();
config.target = target.clone();
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 {
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
}
pub fn print_performance_report(&self) {
self.performance_monitor.print_report();
}
pub fn performance_monitor(&self) -> &PerformanceMonitor {
&self.performance_monitor
}
pub fn backends(&self) -> &[Box<dyn Backend>] {
&self.backends
}
}
pub fn compile_to_platform(program: &GaiaModule, target: CompilationTarget) -> Result<Vec<u8>> {
let mut compiler = GaiaAssembler::new();
let generated_files = compiler.compile(program, &target)?;
if let Some((_, bytes)) = generated_files.files.iter().next() {
Ok(bytes.clone())
}
else {
Err(GaiaError::invalid_data("No output files generated"))
}
}