use crate::class_file::{ClassFile, MethodInfo};
use crate::cranelift_jit::CraneliftJitBackend;
use crate::memory::{Memory, StackFrame};
use crate::native::NativeRegistry;
use log::{debug, info, warn};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::time::Instant;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum CompilationLevel {
Interpreter = 0,
Baseline = 1,
Optimized = 2,
}
#[derive(Debug, Clone)]
pub struct MethodProfile {
pub invocation_count: u64,
pub instruction_count: u64,
pub total_time_ns: u64,
pub level: CompilationLevel,
pub last_compiled: Option<Instant>,
}
impl MethodProfile {
pub fn new() -> Self {
Self {
invocation_count: 0,
instruction_count: 0,
total_time_ns: 0,
level: CompilationLevel::Interpreter,
last_compiled: None,
}
}
pub fn record_invocation(&mut self) {
self.invocation_count += 1;
}
pub fn record_instructions(&mut self, count: u64) {
self.instruction_count += count;
}
pub fn record_time(&mut self, time_ns: u64) {
self.total_time_ns += time_ns;
}
pub fn should_upgrade(&self, threshold: u64) -> bool {
match self.level {
CompilationLevel::Interpreter => self.invocation_count >= threshold,
CompilationLevel::Baseline => {
self.invocation_count >= threshold * 10
}
CompilationLevel::Optimized => false,
}
}
}
#[derive(Debug, Clone)]
pub struct TieredCompilationConfig {
pub baseline_threshold: u64,
pub optimized_threshold: u64,
pub enabled: bool,
pub max_method_size: usize,
}
impl Default for TieredCompilationConfig {
fn default() -> Self {
Self {
baseline_threshold: 100, optimized_threshold: 1000, enabled: true,
max_method_size: 10000, }
}
}
pub type JitResult<T> = Result<T, JitError>;
#[derive(Debug, Clone)]
pub enum JitError {
CompilationFailed(String),
UnsupportedInstruction(String),
InvalidMethod(String),
IrGenerationError(String),
LinkingError(String),
}
impl std::fmt::Display for JitError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
JitError::CompilationFailed(msg) => write!(f, "Compilation failed: {}", msg),
JitError::UnsupportedInstruction(msg) => write!(f, "Unsupported instruction: {}", msg),
JitError::InvalidMethod(msg) => write!(f, "Invalid method: {}", msg),
JitError::IrGenerationError(msg) => write!(f, "IR generation error: {}", msg),
JitError::LinkingError(msg) => write!(f, "Linking error: {}", msg),
}
}
}
impl std::error::Error for JitError {}
pub type CompiledFunction = unsafe extern "C" fn(*mut Memory, *mut StackFrame) -> i32;
#[derive(Clone)]
pub struct CompiledCode {
pub name: String,
pub func: CompiledFunction,
pub level: CompilationLevel,
pub code_size: usize,
pub compile_time_ms: u64,
}
unsafe extern "C" fn placeholder_compiled_function(
_memory: *mut Memory,
_frame: *mut StackFrame,
) -> i32 {
0 }
pub struct CraneliftJitCompiler {
jit_backend: Option<CraneliftJitBackend>,
compiled_functions: HashMap<String, CompiledCode>,
profiles: HashMap<String, MethodProfile>,
config: TieredCompilationConfig,
}
impl CraneliftJitCompiler {
pub fn new() -> JitResult<Self> {
let jit_backend = match CraneliftJitBackend::new() {
Ok(b) => Some(b),
Err(e) => {
warn!(
"Cranelift JIT backend unavailable: {} - using placeholder",
e
);
None
}
};
Ok(Self {
jit_backend,
compiled_functions: HashMap::new(),
profiles: HashMap::new(),
config: TieredCompilationConfig::default(),
})
}
pub fn with_config(config: TieredCompilationConfig) -> JitResult<Self> {
let mut compiler = Self::new()?;
compiler.config = config;
Ok(compiler)
}
pub fn compile_method(
&mut self,
class: &ClassFile,
method: &MethodInfo,
level: CompilationLevel,
) -> JitResult<CompiledCode> {
let method_name = class
.get_string(method.name_index)
.unwrap_or_else(|| "unknown".to_string());
let class_name = class
.get_class_name()
.unwrap_or_else(|| "Unknown".to_string());
let full_name = format!("{}.{}", class_name, method_name);
info!("Compiling method '{}' at level {:?}", full_name, level);
let start = Instant::now();
let _code_attr = method
.attributes
.iter()
.find(|attr| attr.info.len() >= 8)
.ok_or_else(|| JitError::InvalidMethod("No Code attribute found".to_string()))?;
let compiled_code = if let Some(ref mut backend) = self.jit_backend {
match backend.compile(class, method, &full_name) {
Ok((code_ptr, code_size)) => {
let func =
unsafe { std::mem::transmute::<*const u8, CompiledFunction>(code_ptr) };
CompiledCode {
name: full_name.clone(),
func,
level,
code_size,
compile_time_ms: start.elapsed().as_millis() as u64,
}
}
Err(JitError::UnsupportedInstruction(_)) => {
debug!(
"Method '{}' has unsupported bytecode - using placeholder",
full_name
);
CompiledCode {
name: full_name.clone(),
func: placeholder_compiled_function,
level,
code_size: 0,
compile_time_ms: start.elapsed().as_millis() as u64,
}
}
Err(e) => {
warn!(
"JIT compile failed for '{}': {} - using placeholder",
full_name, e
);
CompiledCode {
name: full_name.clone(),
func: placeholder_compiled_function,
level,
code_size: 0,
compile_time_ms: start.elapsed().as_millis() as u64,
}
}
}
} else {
CompiledCode {
name: full_name.clone(),
func: placeholder_compiled_function,
level,
code_size: 0,
compile_time_ms: start.elapsed().as_millis() as u64,
}
};
self.compiled_functions
.insert(full_name, compiled_code.clone());
Ok(compiled_code)
}
pub fn get_compiled_function(&self, name: &str) -> Option<&CompiledCode> {
self.compiled_functions.get(name)
}
pub fn record_invocation(&mut self, class_name: &str, method_name: &str) {
let full_name = format!("{}.{}", class_name, method_name);
let profile = self
.profiles
.entry(full_name)
.or_insert_with(MethodProfile::new);
profile.record_invocation();
}
pub fn should_compile(&self, class_name: &str, method_name: &str) -> Option<CompilationLevel> {
let full_name = format!("{}.{}", class_name, method_name);
if let Some(profile) = self.profiles.get(&full_name) {
if profile.level == CompilationLevel::Interpreter
&& profile.should_upgrade(self.config.baseline_threshold)
{
return Some(CompilationLevel::Baseline);
} else if profile.level == CompilationLevel::Baseline
&& profile.should_upgrade(self.config.optimized_threshold)
{
return Some(CompilationLevel::Optimized);
}
}
None
}
pub fn config(&self) -> &TieredCompilationConfig {
&self.config
}
pub fn set_config(&mut self, config: TieredCompilationConfig) {
self.config = config;
}
}
impl Default for CraneliftJitCompiler {
fn default() -> Self {
Self::new().expect("Failed to create JIT compiler")
}
}
pub struct AotCompiler {}
impl AotCompiler {
pub fn new() -> JitResult<Self> {
Ok(Self {})
}
pub fn compile_class(&mut self, class: &ClassFile, output_path: &Path) -> JitResult<()> {
crate::aot_compiler::compile_class_to_object(class, output_path)
}
pub fn link_executable(objects: &[PathBuf], output_path: &Path) -> JitResult<()> {
use std::process::Command;
info!("Linking executable to '{}'", output_path.display());
let mut cmd = Command::new("cc");
for obj in objects {
cmd.arg(obj);
}
cmd.arg("-o").arg(output_path);
let result = cmd
.output()
.map_err(|e| JitError::LinkingError(format!("Failed to execute linker: {}", e)))?;
if !result.status.success() {
return Err(JitError::LinkingError(format!(
"Linker failed: {}",
String::from_utf8_lossy(&result.stderr)
)));
}
Ok(())
}
}
impl Default for AotCompiler {
fn default() -> Self {
Self::new().expect("Failed to create AOT compiler")
}
}
#[cfg(feature = "llvm")]
pub mod llvm_backend {
use super::*;
use inkwell::context::Context;
use inkwell::module::Module;
use inkwell::values::BasicValueEnum;
pub struct LlvmIrGenerator {
context: Context,
module: Module,
}
impl LlvmIrGenerator {
pub fn new(module_name: &str) -> Self {
let context = Context::create();
let module = context.create_module(module_name);
Self { context, module }
}
pub fn method_to_llvm_ir(
&mut self,
class: &ClassFile,
method: &MethodInfo,
) -> JitResult<String> {
let method_name = class
.get_string(method.name_index)
.unwrap_or_else(|| "unknown".to_string());
let class_name = class
.get_class_name()
.unwrap_or_else(|| "Unknown".to_string());
let func_name = format!("{}_{}", class_name.replace("/", "_"), method_name);
let i32_type = self.context.i32_type();
let fn_type = i32_type.fn_type(
&[
i32_type.into(),
i32_type.into(),
i32_type.into(),
i32_type.into(),
],
false,
);
let function = self.module.add_function(&func_name, fn_type, None);
let entry = self.context.append_basic_block(function, "entry");
let builder = self.context.create_builder();
builder.position_at_end(entry);
let code_attr = method.attributes.iter().find(|a| a.info.len() >= 8);
let mut stack: Vec<inkwell::values::IntValue> = vec![];
if let Some(attr) = code_attr {
let code_len = ((attr.info[4] as usize) << 24)
| ((attr.info[5] as usize) << 16)
| ((attr.info[6] as usize) << 8)
| (attr.info[7] as usize);
let bytecode = attr.info.get(8..8 + code_len).unwrap_or(&[]);
let mut pc = 0usize;
while pc < bytecode.len() {
let opcode = bytecode[pc];
pc += 1;
match opcode {
0x10 => {
if pc < bytecode.len() {
let byte_val = bytecode[pc] as i8 as i32;
pc += 1;
stack.push(builder.build_int_const(
i32_type,
byte_val as i64,
"const",
));
}
}
0x1a..=0x1d => {
if let Ok(param) = function.get_nth_param((opcode - 0x1a) as u32) {
stack.push(param.into_int_value());
}
}
0x60 => {
if stack.len() >= 2 {
if let (Ok(b), Ok(a)) = (stack.pop(), stack.pop()) {
let sum = builder.build_int_add(a, b, "add");
stack.push(sum);
}
}
}
0xac => {
if let Some(ret) = stack.pop() {
builder.build_return(Some(&ret.into()));
} else {
builder.build_return(Some(
&builder.build_int_const(i32_type, 0, "").into(),
));
}
break;
}
_ => {}
}
}
if stack.is_empty()
&& !builder
.get_insert_block()
.and_then(|b| b.get_terminal())
.is_some()
{
builder.build_return(Some(&builder.build_int_const(i32_type, 0, "").into()));
}
} else {
builder.build_return(Some(&builder.build_int_const(i32_type, 0, "").into()));
}
Ok(self.module.print_to_string().to_string())
}
pub fn write_to_file(&self, path: &Path) -> JitResult<()> {
use std::io::Write;
let ir_string = self.module.print_to_string().to_string();
let mut file = std::fs::File::create(path)
.map_err(|e| JitError::LinkingError(format!("Failed to create file: {}", e)))?;
file.write_all(ir_string.as_bytes())
.map_err(|e| JitError::LinkingError(format!("Failed to write file: {}", e)))?;
Ok(())
}
}
}
#[cfg(not(feature = "llvm"))]
pub mod llvm_backend {
pub struct LlvmIrGenerator {}
impl LlvmIrGenerator {
pub fn new(_module_name: &str) -> Result<Self, String> {
Err("LLVM feature is not enabled. Add --features llvm to enable.".to_string())
}
}
}
pub struct JitManager {
pub compiler: CraneliftJitCompiler,
compiled_functions: HashMap<String, CompiledCode>,
native_registry: NativeRegistry,
}
impl JitManager {
pub fn new() -> JitResult<Self> {
Ok(Self {
compiler: CraneliftJitCompiler::new()?,
compiled_functions: HashMap::new(),
native_registry: NativeRegistry::new(),
})
}
pub fn with_config(config: TieredCompilationConfig) -> JitResult<Self> {
Ok(Self {
compiler: CraneliftJitCompiler::with_config(config)?,
compiled_functions: HashMap::new(),
native_registry: NativeRegistry::new(),
})
}
pub fn get_or_compile_method(
&mut self,
class: &ClassFile,
method: &MethodInfo,
) -> JitResult<CompiledCode> {
self.get_or_compile_method_at(class, method, None)
}
pub fn get_or_compile_method_at(
&mut self,
class: &ClassFile,
method: &MethodInfo,
level_hint: Option<CompilationLevel>,
) -> JitResult<CompiledCode> {
let method_name = class
.get_string(method.name_index)
.unwrap_or_else(|| "unknown".to_string());
let class_name = class
.get_class_name()
.unwrap_or_else(|| "Unknown".to_string());
let full_name = format!("{}.{}", class_name, method_name);
if let Some(code) = self.compiled_functions.get(&full_name) {
return Ok(code.clone());
}
let level = level_hint
.or_else(|| self.compiler.should_compile(&class_name, &method_name))
.unwrap_or(CompilationLevel::Baseline);
let code = self.compiler.compile_method(class, method, level)?;
self.compiled_functions
.insert(full_name.clone(), code.clone());
Ok(code)
}
pub fn is_compiled(&self, class_name: &str, method_name: &str) -> bool {
let full_name = format!("{}.{}", class_name, method_name);
self.compiled_functions.contains_key(&full_name)
}
pub fn record_and_check_compilation(
&mut self,
class_name: &str,
method_name: &str,
) -> Option<CompilationLevel> {
self.compiler.record_invocation(class_name, method_name);
self.compiler.should_compile(class_name, method_name)
}
}
impl Default for JitManager {
fn default() -> Self {
Self::new().expect("Failed to create JIT manager")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compilation_level_ordering() {
assert!(CompilationLevel::Interpreter < CompilationLevel::Baseline);
assert!(CompilationLevel::Baseline < CompilationLevel::Optimized);
}
#[test]
fn test_method_profile() {
let mut profile = MethodProfile::new();
profile.record_invocation();
assert_eq!(profile.invocation_count, 1);
profile.record_instructions(100);
assert_eq!(profile.instruction_count, 100);
profile.record_time(1_000_000);
assert_eq!(profile.total_time_ns, 1_000_000);
assert!(!profile.should_upgrade(1000));
for _ in 0..1000 {
profile.record_invocation();
}
assert!(profile.should_upgrade(1000));
}
#[test]
fn test_tiered_config_default() {
let config = TieredCompilationConfig::default();
assert!(config.enabled);
assert_eq!(config.baseline_threshold, 100);
assert_eq!(config.optimized_threshold, 1000);
}
#[test]
fn test_jit_compiler_creation() {
let compiler = CraneliftJitCompiler::new();
assert!(compiler.is_ok());
}
#[test]
fn test_jit_manager_creation() {
let manager = JitManager::new();
assert!(manager.is_ok());
}
#[test]
fn test_aot_compiler_creation() {
let compiler = AotCompiler::new();
assert!(compiler.is_ok());
}
}