1use std::collections::HashMap;
8
9use cranelift_codegen::settings::{self, Configurable};
10use cranelift_frontend::FunctionBuilderContext;
11use cranelift_jit::{JITBuilder, JITModule};
12use cranelift_module::Module;
13
14use crate::chunk::Prototype;
15use crate::jit_runtime;
16use crate::value::VmValue;
17
18pub type JitFn = unsafe extern "C" fn(*const VmValue, usize) -> VmValue;
21
22pub struct JitCompiler {
24 module: JITModule,
25 ctx: cranelift_codegen::Context,
26 builder_ctx: FunctionBuilderContext,
27 compiled: HashMap<String, *const u8>,
29}
30
31impl JitCompiler {
32 pub fn new() -> Result<Self, String> {
33 let mut flag_builder = settings::builder();
34 flag_builder
35 .set("use_colocated_libcalls", "false")
36 .map_err(|e| format!("JIT settings error: {e}"))?;
37 flag_builder
38 .set("is_pic", "false")
39 .map_err(|e| format!("JIT settings error: {e}"))?;
40
41 let isa_builder = cranelift_codegen::isa::lookup(target_lexicon::Triple::host())
42 .map_err(|e| format!("JIT ISA error: {e}"))?;
43
44 let isa = isa_builder
45 .finish(settings::Flags::new(flag_builder))
46 .map_err(|e| format!("JIT ISA finish error: {e}"))?;
47
48 let mut builder = JITBuilder::with_isa(isa, cranelift_module::default_libcall_names());
49
50 builder.symbol("tl_rt_add", jit_runtime::tl_rt_add as *const u8);
52 builder.symbol("tl_rt_sub", jit_runtime::tl_rt_sub as *const u8);
53 builder.symbol("tl_rt_mul", jit_runtime::tl_rt_mul as *const u8);
54 builder.symbol("tl_rt_cmp", jit_runtime::tl_rt_cmp as *const u8);
55 builder.symbol("tl_rt_is_truthy", jit_runtime::tl_rt_is_truthy as *const u8);
56
57 let module = JITModule::new(builder);
58 let ctx = module.make_context();
59
60 Ok(JitCompiler {
61 module,
62 ctx,
63 builder_ctx: FunctionBuilderContext::new(),
64 compiled: HashMap::new(),
65 })
66 }
67
68 pub fn get_compiled(&self, name: &str) -> Option<*const u8> {
70 self.compiled.get(name).copied()
71 }
72
73 pub fn compile_function(&mut self, proto: &Prototype) -> Result<Option<*const u8>, String> {
76 if !proto.upvalue_defs.is_empty() {
78 return Ok(None); }
80
81 for &inst in &proto.code {
83 let op = crate::opcode::decode_op(inst);
84 match op {
85 crate::opcode::Op::TablePipe
86 | crate::opcode::Op::Interpolate
87 | crate::opcode::Op::NewMap
88 | crate::opcode::Op::GetMember => {
89 return Ok(None); }
91 _ => {}
92 }
93 }
94
95 Ok(None)
98 }
99}
100
101pub const JIT_THRESHOLD: u32 = 100;
103
104#[derive(Debug, Default)]
106pub struct TieringState {
107 pub call_counts: HashMap<String, u32>,
109}
110
111impl TieringState {
112 pub fn new() -> Self {
113 Self::default()
114 }
115
116 pub fn record_call(&mut self, name: &str) -> bool {
118 let count = self.call_counts.entry(name.to_string()).or_insert(0);
119 *count += 1;
120 *count == JIT_THRESHOLD
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn test_jit_compiler_creation() {
130 let jit = JitCompiler::new();
131 assert!(jit.is_ok(), "JIT compiler should initialize");
132 }
133
134 #[test]
135 fn test_tiering_state() {
136 let mut state = TieringState::new();
137 for _ in 0..JIT_THRESHOLD - 1 {
138 assert!(!state.record_call("test_fn"));
139 }
140 assert!(state.record_call("test_fn"));
142 assert!(!state.record_call("test_fn"));
144 }
145}