pub mod hotspot_detector;
pub mod compilation_tiers;
pub mod code_generator;
pub mod optimization_pipeline;
pub mod code_cache;
pub mod profile_guided_optimizer;
pub mod config;
pub mod metrics;
pub use hotspot_detector::{HotspotDetector, ExecutionProfile, CompilationCandidate};
pub use compilation_tiers::{TierManager, CompilationTier, TierTransition};
pub use code_generator::{CodeGenerator, NativeCode, TargetFeatures};
pub use optimization_pipeline::{OptimizationPipeline, SchemeOptimization};
pub use code_cache::{CodeCache, CacheEntry};
pub use config::EvictionPolicy;
pub use profile_guided_optimizer::{ProfileGuidedOptimizer, RuntimeProfile};
pub use config::{JitConfig, CompilationStrategy};
pub use metrics::{JitMetrics, PerformanceCounters};
use crate::ast::{Expr, Program};
use crate::eval::{Environment, Value};
use crate::diagnostics::{Error, Result};
use std::sync::{Arc, RwLock, Mutex};
use std::collections::HashMap;
use std::time::{Duration, Instant};
pub struct JitCompiler {
hotspot_detector: Arc<Mutex<HotspotDetector>>,
tier_manager: Arc<RwLock<TierManager>>,
code_generator: Arc<Mutex<CodeGenerator>>,
optimization_pipeline: Arc<OptimizationPipeline>,
code_cache: Arc<RwLock<CodeCache>>,
pgo: Arc<Mutex<ProfileGuidedOptimizer>>,
config: JitConfig,
metrics: Arc<RwLock<JitMetrics>>,
active_compilations: Arc<Mutex<HashMap<String, Instant>>>,
}
#[derive(Debug, Clone)]
pub struct JitContext {
pub identifier: String,
pub ast: Expr,
pub environment: Arc<Environment>,
pub execution_count: u64,
pub total_time: Duration,
pub average_time: Duration,
pub type_info: HashMap<String, TypeProfile>,
}
#[derive(Debug, Clone)]
pub struct TypeProfile {
pub primary_type: String,
pub confidence: f64,
pub observations: u64,
}
pub enum CompilationResult {
Success {
native_code: Arc<NativeCode>,
compilation_time: Duration,
optimizations_applied: Vec<String>,
},
Deferred {
reason: String,
retry_after: Duration,
},
Failed {
error: String,
fallback_tier: CompilationTier,
},
}
impl JitCompiler {
pub fn new() -> Result<Self> {
let config = JitConfig::default();
Self::with_config(config)
}
pub fn with_config(config: JitConfig) -> Result<Self> {
let code_generator = Arc::new(Mutex::new(CodeGenerator::new(config.to_codegen_config())?));
Ok(JitCompiler {
hotspot_detector: Arc::new(Mutex::new(HotspotDetector::new(config.hotspot_config.clone()))),
tier_manager: Arc::new(RwLock::new(TierManager::new(config.tier_config.clone())?)),
code_generator,
optimization_pipeline: Arc::new(OptimizationPipeline::new({
if config.optimization_config.enable_advanced_optimizations {
crate::jit::optimization_pipeline::OptimizationLevel::Aggressive
} else if config.optimization_config.enable_basic_optimizations {
crate::jit::optimization_pipeline::OptimizationLevel::Balanced
} else {
crate::jit::optimization_pipeline::OptimizationLevel::None
}
})?),
code_cache: Arc::new(RwLock::new(CodeCache::new(config.cache_config.to_code_cache_config())?)),
pgo: Arc::new(Mutex::new(ProfileGuidedOptimizer::new(config.pgo_config.clone().into())?)),
config,
metrics: Arc::new(RwLock::new(JitMetrics::new())),
active_compilations: Arc::new(Mutex::new(HashMap::new())),
})
}
pub fn record_execution(&self, context: JitContext, execution_time: Duration) -> Result<()> {
{
let mut detector = self.hotspot_detector.lock()
.map_err(|_| Error::runtime_error("Failed to acquire hotspot detector lock".to_string(), None))?;
detector.record_execution(
context.identifier.clone(),
context.ast.clone(),
execution_time,
context.environment.clone(),
)?;
}
{
let mut metrics = self.metrics.write()
.map_err(|_| Error::runtime_error("Failed to acquire metrics lock".to_string(), None))?;
metrics.record_execution(execution_time);
}
self.maybe_trigger_compilation(&context)?;
Ok(())
}
pub fn get_compiled_code(&self, ast: &Expr) -> Result<Option<NativeCode>> {
let cache = self.code_cache.read()
.map_err(|_| Error::runtime_error("Failed to acquire cache lock".to_string(), None))?;
cache.get(ast)
}
fn maybe_trigger_compilation(&self, context: &JitContext) -> Result<()> {
let should_compile = {
let detector = self.hotspot_detector.lock()
.map_err(|_| Error::runtime_error("Failed to acquire detector lock".to_string(), None))?;
detector.should_compile(&context.identifier)?
};
if should_compile {
self.trigger_compilation(context.clone())?;
}
Ok(())
}
fn trigger_compilation(&self, context: JitContext) -> Result<()> {
{
let mut active = self.active_compilations.lock()
.map_err(|_| Error::runtime_error("Failed to acquire compilation lock".to_string(), None))?;
if active.contains_key(&context.identifier) {
return Ok(()); }
active.insert(context.identifier.clone(), Instant::now());
}
let tier = {
let mut tier_manager = self.tier_manager.write()
.map_err(|_| Error::runtime_error("Failed to acquire tier manager".to_string(), None))?;
let mut profile = ExecutionProfile::new(context.identifier.clone(), context.ast.clone());
profile.execution_count = context.execution_count;
profile.total_time = context.total_time;
profile.average_time = context.average_time;
tier_manager.select_tier(&context.ast, &profile)?
};
self.compile_function(context, tier)?;
Ok(())
}
fn compile_function(&self, context: JitContext, tier: CompilationTier) -> Result<CompilationResult> {
let start_time = Instant::now();
let native_code = {
let mut generator = self.code_generator.lock()
.map_err(|_| Error::runtime_error("Failed to acquire code generator".to_string(), None))?;
generator.compile_expression(&context.ast, tier)?
};
let optimized_code = {
let mut pipeline = self.optimization_pipeline.clone();
let mut profile = ExecutionProfile::new(context.identifier.clone(), context.ast.clone());
profile.execution_count = context.execution_count;
profile.total_time = context.total_time;
profile.average_time = context.average_time;
native_code
};
let compilation_time = start_time.elapsed();
let native_code = optimized_code;
{
let cache = self.code_cache.read()
.map_err(|_| Error::runtime_error("Failed to acquire cache lock".to_string(), None))?;
cache.store(context.ast.clone(), native_code.clone())?;
}
{
let mut metrics = self.metrics.write()
.map_err(|_| Error::runtime_error("Failed to acquire metrics".to_string(), None))?;
metrics.record_compilation(compilation_time, tier);
}
{
let mut active = self.active_compilations.lock()
.map_err(|_| Error::runtime_error("Failed to acquire compilation lock".to_string(), None))?;
active.remove(&context.identifier);
}
Ok(CompilationResult::Success {
native_code: Arc::new(native_code),
compilation_time,
optimizations_applied: vec!["tier-specific".to_string()], })
}
pub fn execute_with_jit(
&self,
identifier: &str,
ast: &Expr,
env: &Arc<Environment>,
) -> Result<Value> {
let execution_start = Instant::now();
if let Some(native_fn) = self.get_compiled_code(ast)? {
{
let cache = self.code_cache.read()
.map_err(|_| Error::runtime_error("Failed to acquire cache lock".to_string(), None))?;
cache.record_execution(ast, execution_start.elapsed())?;
}
let result = Value::Nil;
let execution_time = execution_start.elapsed();
self.record_execution(JitContext {
identifier: identifier.to_string(),
ast: ast.clone(),
environment: env.clone(),
execution_count: 1, total_time: execution_time,
average_time: execution_time,
type_info: HashMap::new(), }, execution_time)?;
return Ok(result);
}
let result = self.execute_with_interpreter(ast, env)?;
let execution_time = execution_start.elapsed();
self.record_execution(JitContext {
identifier: identifier.to_string(),
ast: ast.clone(),
environment: env.clone(),
execution_count: 1,
total_time: execution_time,
average_time: execution_time,
type_info: HashMap::new(),
}, execution_time)?;
Ok(result)
}
fn execute_with_interpreter(&self, ast: &Expr, env: &Arc<Environment>) -> Result<Value> {
match ast {
Expr::Literal(lit) => Ok(Value::from_literal(lit.clone())),
_ => Ok(Value::Nil),
}
}
pub fn get_metrics(&self) -> Result<JitMetrics> {
let metrics = self.metrics.read()
.map_err(|_| Error::runtime_error("Failed to acquire metrics".to_string(), None))?;
Ok(metrics.clone())
}
pub fn optimize_cache(&self) -> Result<()> {
let cache = self.code_cache.read()
.map_err(|_| Error::runtime_error("Failed to acquire cache lock".to_string(), None))?;
cache.clear()?;
Ok(())
}
pub fn set_enabled(&mut self, enabled: bool) {
}
pub fn get_compilation_stats(&self) -> Result<HashMap<String, u64>> {
let mut stats = HashMap::new();
let cache_size = {
let cache = self.code_cache.read()
.map_err(|_| Error::runtime_error("Failed to acquire cache lock".to_string(), None))?;
cache.size()?
};
stats.insert("cached_functions".to_string(), cache_size as u64);
let metrics = self.metrics.read()
.map_err(|_| Error::runtime_error("Failed to acquire metrics".to_string(), None))?;
stats.insert("total_executions".to_string(), metrics.total_executions());
stats.insert("compilation_time_ms".to_string(), metrics.total_compilation_time().as_millis() as u64);
Ok(stats)
}
}
impl Default for JitCompiler {
fn default() -> Self {
Self::new().expect("Failed to create default JIT compiler")
}
}
pub mod utils {
use super::*;
pub fn create_context(
identifier: String,
ast: Expr,
environment: Arc<Environment>,
) -> JitContext {
JitContext {
identifier,
ast,
environment,
execution_count: 0,
total_time: Duration::ZERO,
average_time: Duration::ZERO,
type_info: HashMap::new(),
}
}
pub fn extract_function_name(ast: &Expr) -> String {
match ast {
Expr::Application { operator, .. } => {
match &operator.inner {
Expr::Identifier(name) => name.clone(),
_ => "anonymous_application".to_string(),
}
}
Expr::Lambda { .. } => "lambda".to_string(),
Expr::Identifier(name) => name.clone(),
_ => "expression".to_string(),
}
}
pub fn is_jit_suitable(ast: &Expr) -> bool {
matches!(ast, Expr::Lambda { .. } | Expr::Application { .. } | Expr::Let { .. } | Expr::LetRec { .. } | Expr::If { .. })
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::Literal;
#[test]
fn test_jit_compiler_creation() {
let jit = JitCompiler::new();
assert!(jit.is_ok());
}
#[test]
fn test_jit_context_creation() {
let ast = Expr::Literal(Literal::ExactInteger(42));
let env = Arc::new(Environment::new(None, 0));
let context = utils::create_context("test".to_string(), ast, env);
assert_eq!(context.identifier, "test");
}
#[test]
fn test_function_name_extraction() {
let ast = Expr::Identifier("test_function".to_string());
let name = utils::extract_function_name(&ast);
assert_eq!(name, "test_function");
}
#[test]
fn test_jit_suitability() {
let suitable = Expr::Lambda {
params: vec![],
body: Box::new(Expr::Literal(Literal::ExactInteger(1))),
};
assert!(utils::is_jit_suitable(&suitable));
let unsuitable = Expr::Literal(Literal::ExactInteger(42));
assert!(!utils::is_jit_suitable(&unsuitable));
}
}