1use cranelift::codegen::ir::FuncRef;
4use cranelift::prelude::*;
5use cranelift_module::{Linkage, Module};
6use std::collections::{BTreeMap, HashMap};
7
8use super::setup::JITCompiler;
9use crate::context::{JittedFn, JittedStrategyFn};
10use crate::mixed_table::{FunctionEntry, MixedFunctionTable};
11use crate::numeric_compiler::compile_numeric_program;
12use crate::translator::BytecodeToIR;
13use crate::translator::types::InlineCandidate;
14use shape_vm::bytecode::{BytecodeProgram, OpCode, Operand};
15
16#[derive(Default)]
17struct NumericOpcodeStats {
18 typed: usize,
19 generic: usize,
20 typed_breakdown: BTreeMap<String, usize>,
21 generic_breakdown: BTreeMap<String, usize>,
22}
23
24fn bump_breakdown(map: &mut BTreeMap<String, usize>, opcode: OpCode) {
25 let key = format!("{:?}", opcode);
26 *map.entry(key).or_insert(0) += 1;
27}
28
29fn collect_numeric_opcode_stats(program: &BytecodeProgram) -> NumericOpcodeStats {
30 let mut stats = NumericOpcodeStats::default();
31 for instr in &program.instructions {
32 match instr.opcode {
33 OpCode::AddInt
35 | OpCode::SubInt
36 | OpCode::MulInt
37 | OpCode::DivInt
38 | OpCode::ModInt
39 | OpCode::PowInt
40 | OpCode::AddNumber
41 | OpCode::SubNumber
42 | OpCode::MulNumber
43 | OpCode::DivNumber
44 | OpCode::ModNumber
45 | OpCode::PowNumber
46 | OpCode::GtInt
48 | OpCode::LtInt
49 | OpCode::GteInt
50 | OpCode::LteInt
51 | OpCode::GtNumber
52 | OpCode::LtNumber
53 | OpCode::GteNumber
54 | OpCode::LteNumber
55 | OpCode::EqInt
56 | OpCode::EqNumber
57 | OpCode::NeqInt
58 | OpCode::NeqNumber => {
59 stats.typed += 1;
60 bump_breakdown(&mut stats.typed_breakdown, instr.opcode);
61 }
62 OpCode::Add
64 | OpCode::Sub
65 | OpCode::Mul
66 | OpCode::Div
67 | OpCode::Mod
68 | OpCode::Pow
69 | OpCode::Gt
70 | OpCode::Lt
71 | OpCode::Gte
72 | OpCode::Lte
73 | OpCode::Eq
74 | OpCode::Neq => {
75 stats.generic += 1;
76 bump_breakdown(&mut stats.generic_breakdown, instr.opcode);
77 }
78 _ => {}
79 }
80 }
81 stats
82}
83
84fn maybe_emit_numeric_metrics(program: &BytecodeProgram) {
85 if std::env::var_os("SHAPE_JIT_METRICS").is_none() {
86 return;
87 }
88 let static_stats = collect_numeric_opcode_stats(program);
89 let static_total = static_stats.typed + static_stats.generic;
90 let static_coverage_pct = if static_total == 0 {
91 100.0
92 } else {
93 (static_stats.typed as f64 * 100.0) / (static_total as f64)
94 };
95 let effective_typed = static_stats.typed;
98 let effective_generic = static_stats.generic;
99 let effective_coverage_pct = static_coverage_pct;
100 eprintln!(
101 "[shape-jit-metrics] typed_numeric_ops={} generic_numeric_ops={} typed_numeric_coverage_pct={:.2} static_typed_numeric_ops={} static_generic_numeric_ops={} static_typed_numeric_coverage_pct={:.2}",
102 effective_typed,
103 effective_generic,
104 effective_coverage_pct,
105 static_stats.typed,
106 static_stats.generic,
107 static_coverage_pct
108 );
109 if std::env::var_os("SHAPE_JIT_METRICS_DETAIL").is_some() {
110 let fmt_breakdown = |breakdown: &BTreeMap<String, usize>| -> String {
111 breakdown
112 .iter()
113 .map(|(name, count)| format!("{}:{}", name, count))
114 .collect::<Vec<_>>()
115 .join(",")
116 };
117 eprintln!(
118 "[shape-jit-metrics-detail] typed_breakdown={} generic_breakdown={}",
119 fmt_breakdown(&static_stats.typed_breakdown),
120 fmt_breakdown(&static_stats.generic_breakdown)
121 );
122 }
123}
124
125fn stack_effect_for_param_analysis(op: OpCode) -> Option<(i32, i32)> {
126 let effect = match op {
127 OpCode::LoadLocal
128 | OpCode::LoadModuleBinding
129 | OpCode::LoadClosure
130 | OpCode::PushConst
131 | OpCode::PushNull
132 | OpCode::DerefLoad => (0, 1),
133 OpCode::IntToNumber | OpCode::NumberToInt | OpCode::Neg | OpCode::Not => (1, 1),
134 OpCode::Add
135 | OpCode::Sub
136 | OpCode::Mul
137 | OpCode::Div
138 | OpCode::Mod
139 | OpCode::Pow
140 | OpCode::AddInt
141 | OpCode::SubInt
142 | OpCode::MulInt
143 | OpCode::DivInt
144 | OpCode::ModInt
145 | OpCode::PowInt
146 | OpCode::AddNumber
147 | OpCode::SubNumber
148 | OpCode::MulNumber
149 | OpCode::DivNumber
150 | OpCode::ModNumber
151 | OpCode::PowNumber
152 | OpCode::Gt
153 | OpCode::Lt
154 | OpCode::Gte
155 | OpCode::Lte
156 | OpCode::Eq
157 | OpCode::Neq
158 | OpCode::GtInt
159 | OpCode::LtInt
160 | OpCode::GteInt
161 | OpCode::LteInt
162 | OpCode::GtNumber
163 | OpCode::LtNumber
164 | OpCode::GteNumber
165 | OpCode::LteNumber
166 | OpCode::EqInt
167 | OpCode::EqNumber
168 | OpCode::NeqInt
169 | OpCode::NeqNumber
170 | OpCode::GetProp => (2, 1),
171 OpCode::Dup => (1, 2),
172 OpCode::Swap => (2, 2),
173 OpCode::StoreLocal | OpCode::StoreLocalTyped | OpCode::Pop => (1, 0),
174 _ => return None,
175 };
176 Some(effect)
177}
178
179fn source_local_for_stack_pos(
180 program: &BytecodeProgram,
181 before_idx: usize,
182 mut pos_from_top: i32,
183) -> Option<u16> {
184 for j in (0..before_idx).rev() {
185 let instr = &program.instructions[j];
186 let (pops, pushes) = stack_effect_for_param_analysis(instr.opcode)?;
187 if pos_from_top < pushes {
188 return match instr.opcode {
189 OpCode::LoadLocal => match &instr.operand {
190 Some(Operand::Local(idx)) => Some(*idx),
191 _ => None,
192 },
193 _ => None,
194 };
195 }
196 pos_from_top = pos_from_top - pushes + pops;
197 if pos_from_top < 0 {
198 return None;
199 }
200 }
201 None
202}
203
204fn collect_numeric_param_hints(
205 program: &BytecodeProgram,
206 arity: u16,
207 ref_params: &[bool],
208) -> std::collections::HashSet<u16> {
209 let mut params = std::collections::HashSet::new();
210 for (i, instr) in program.instructions.iter().enumerate() {
211 let is_numeric_consumer = matches!(
212 instr.opcode,
213 OpCode::Add
214 | OpCode::Sub
215 | OpCode::Mul
216 | OpCode::Div
217 | OpCode::Mod
218 | OpCode::Pow
219 | OpCode::AddInt
220 | OpCode::SubInt
221 | OpCode::MulInt
222 | OpCode::DivInt
223 | OpCode::ModInt
224 | OpCode::PowInt
225 | OpCode::AddNumber
226 | OpCode::SubNumber
227 | OpCode::MulNumber
228 | OpCode::DivNumber
229 | OpCode::ModNumber
230 | OpCode::PowNumber
231 | OpCode::Gt
232 | OpCode::Lt
233 | OpCode::Gte
234 | OpCode::Lte
235 | OpCode::Eq
236 | OpCode::Neq
237 | OpCode::GtInt
238 | OpCode::LtInt
239 | OpCode::GteInt
240 | OpCode::LteInt
241 | OpCode::GtNumber
242 | OpCode::LtNumber
243 | OpCode::GteNumber
244 | OpCode::LteNumber
245 | OpCode::EqInt
246 | OpCode::EqNumber
247 | OpCode::NeqInt
248 | OpCode::NeqNumber
249 );
250 if !is_numeric_consumer {
251 continue;
252 }
253 for pos in 0..2 {
254 if let Some(local_idx) = source_local_for_stack_pos(program, i, pos) {
255 if local_idx >= arity {
256 continue;
257 }
258 let is_ref = ref_params.get(local_idx as usize).copied().unwrap_or(false);
259 if !is_ref {
260 params.insert(local_idx);
261 }
262 }
263 }
264 }
265 params
266}
267
268impl JITCompiler {
269 #[inline(always)]
270 pub fn compile(&mut self, name: &str, program: &BytecodeProgram) -> Result<JittedFn, String> {
271 let mut sig = self.module.make_signature();
272 sig.params.push(AbiParam::new(types::I64));
273 sig.params.push(AbiParam::new(types::I64));
274 sig.params.push(AbiParam::new(types::I64));
275 sig.returns.push(AbiParam::new(types::F64));
276
277 let func_id = self
278 .module
279 .declare_function(name, Linkage::Export, &sig)
280 .map_err(|e| format!("Failed to declare function: {}", e))?;
281
282 let mut ctx = self.module.make_context();
283 ctx.func.signature = sig;
284
285 {
286 let mut builder = FunctionBuilder::new(&mut ctx.func, &mut self.builder_context);
287 let entry_block = builder.create_block();
288 builder.append_block_params_for_function_params(entry_block);
289 builder.switch_to_block(entry_block);
290 builder.seal_block(entry_block);
291
292 let stack_ptr = builder.block_params(entry_block)[0];
293 let constants_ptr = builder.block_params(entry_block)[1];
294
295 let result = compile_numeric_program(&mut builder, program, stack_ptr, constants_ptr)?;
296
297 builder.ins().return_(&[result]);
298 builder.finalize();
299 }
300
301 self.module
302 .define_function(func_id, &mut ctx)
303 .map_err(|e| format!("Failed to define function: {}", e))?;
304
305 self.module.clear_context(&mut ctx);
306 self.module
307 .finalize_definitions()
308 .map_err(|e| format!("Failed to finalize: {}", e))?;
309
310 let code_ptr = self.module.get_finalized_function(func_id);
311 self.compiled_functions.insert(name.to_string(), code_ptr);
312
313 Ok(unsafe { std::mem::transmute(code_ptr) })
314 }
315
316 #[inline(always)]
317 pub fn compile_program(
318 &mut self,
319 name: &str,
320 program: &BytecodeProgram,
321 ) -> Result<JittedStrategyFn, String> {
322 maybe_emit_numeric_metrics(program);
323
324 let mut user_func_arities: HashMap<u16, u16> = HashMap::new();
325 let mut user_func_ids: HashMap<u16, cranelift_module::FuncId> = HashMap::new();
326
327 for (idx, func) in program.functions.iter().enumerate() {
328 let func_name = format!("{}_{}", name, func.name);
329 let mut user_sig = self.module.make_signature();
330 user_sig.params.push(AbiParam::new(types::I64)); for _ in 0..func.arity {
332 user_sig.params.push(AbiParam::new(types::I64));
333 }
334 user_sig.returns.push(AbiParam::new(types::I32));
335 let func_id = self
336 .module
337 .declare_function(&func_name, Linkage::Local, &user_sig)
338 .map_err(|e| format!("Failed to pre-declare function {}: {}", func.name, e))?;
339 user_func_ids.insert(idx as u16, func_id);
340 user_func_arities.insert(idx as u16, func.arity);
341 }
342
343 let main_func_id = self.compile_strategy_with_user_funcs(
344 name,
345 program,
346 &user_func_ids,
347 &user_func_arities,
348 )?;
349
350 for (idx, func) in program.functions.iter().enumerate() {
351 let func_name = format!("{}_{}", name, func.name);
352 self.compile_function_with_user_funcs(
353 &func_name,
354 program,
355 idx,
356 &user_func_ids,
357 &user_func_arities,
358 )?;
359 }
360
361 self.module
362 .finalize_definitions()
363 .map_err(|e| format!("Failed to finalize definitions: {:?}", e))?;
364
365 let main_code_ptr = self.module.get_finalized_function(main_func_id);
366 self.compiled_functions
367 .insert(name.to_string(), main_code_ptr);
368
369 self.function_table.clear();
370 for (idx, func) in program.functions.iter().enumerate() {
371 let func_name = format!("{}_{}", name, func.name);
372 if let Some(&func_id) = user_func_ids.get(&(idx as u16)) {
373 let ptr = self.module.get_finalized_function(func_id);
374 while self.function_table.len() <= idx {
375 self.function_table.push(std::ptr::null());
376 }
377 self.function_table[idx] = ptr;
378 self.compiled_functions.insert(func_name, ptr);
379 }
380 }
381
382 Ok(unsafe { std::mem::transmute(main_code_ptr) })
383 }
384
385 fn compile_function_with_user_funcs(
386 &mut self,
387 name: &str,
388 program: &BytecodeProgram,
389 func_idx: usize,
390 user_func_ids: &HashMap<u16, cranelift_module::FuncId>,
391 user_func_arities: &HashMap<u16, u16>,
392 ) -> Result<(), String> {
393 let func = &program.functions[func_idx];
394 let func_id = *user_func_ids
395 .get(&(func_idx as u16))
396 .ok_or_else(|| format!("Function {} not pre-declared", name))?;
397
398 let mut sig = self.module.make_signature();
399 sig.params.push(AbiParam::new(types::I64)); for _ in 0..func.arity {
401 sig.params.push(AbiParam::new(types::I64));
402 }
403 sig.returns.push(AbiParam::new(types::I32));
404
405 let mut ctx = self.module.make_context();
406 ctx.func.signature = sig;
407
408 let mut func_builder_ctx = FunctionBuilderContext::new();
409 {
410 let mut builder = FunctionBuilder::new(&mut ctx.func, &mut func_builder_ctx);
411 let entry_block = builder.create_block();
412 builder.append_block_params_for_function_params(entry_block);
413 builder.switch_to_block(entry_block);
414 builder.seal_block(entry_block);
415
416 let ctx_ptr = builder.block_params(entry_block)[0];
417 let mut user_func_refs: HashMap<u16, FuncRef> = HashMap::new();
418 for (&fn_idx, &fn_id) in user_func_ids {
419 let func_ref = self.module.declare_func_in_func(fn_id, builder.func);
420 user_func_refs.insert(fn_idx, func_ref);
421 }
422
423 let ffi = self.build_ffi_refs(&mut builder);
424
425 let func_end = func.entry_point + func.body_length;
426 let sub_instructions = &program.instructions[func.entry_point..func_end];
427 let sub_program = BytecodeProgram {
428 instructions: sub_instructions.to_vec(),
429 constants: program.constants.clone(),
430 strings: program.strings.clone(),
431 functions: Vec::new(),
436 debug_info: Default::default(),
437 data_schema: program.data_schema.clone(),
438 module_binding_names: program.module_binding_names.clone(),
439 top_level_locals_count: program.top_level_locals_count,
440 top_level_local_storage_hints: program
441 .function_local_storage_hints
442 .get(func_idx)
443 .cloned()
444 .unwrap_or_default(),
445 type_schema_registry: program.type_schema_registry.clone(),
446 module_binding_storage_hints: program.module_binding_storage_hints.clone(),
447 function_local_storage_hints: Vec::new(),
448 top_level_frame: None,
449 compiled_annotations: program.compiled_annotations.clone(),
450 trait_method_symbols: program.trait_method_symbols.clone(),
451 expanded_function_defs: program.expanded_function_defs.clone(),
452 string_index: Default::default(),
453 foreign_functions: program.foreign_functions.clone(),
454 native_struct_layouts: program.native_struct_layouts.clone(),
455 content_addressed: None,
456 function_blob_hashes: Vec::new(),
457 };
458
459 let mut compiler = BytecodeToIR::new(
460 &mut builder,
461 &sub_program,
462 ctx_ptr,
463 ffi,
464 user_func_refs,
465 user_func_arities.clone(),
466 );
467 compiler.numeric_param_hints =
468 collect_numeric_param_hints(&sub_program, func.arity, &func.ref_params);
469
470 let entry_params = compiler.builder.block_params(entry_block).to_vec();
471 for arg_idx in 0..func.arity {
472 let arg_val = entry_params[(arg_idx as usize) + 1];
473 let var = compiler.get_or_create_local(arg_idx);
474 compiler.builder.def_var(var, arg_val);
475 }
476
477 let result = compiler.compile()?;
478 builder.ins().return_(&[result]);
479 builder.finalize();
480 }
481
482 self.module
483 .define_function(func_id, &mut ctx)
484 .map_err(|e| format!("Failed to define function: {:?}", e))?;
485
486 self.module.clear_context(&mut ctx);
487
488 Ok(())
489 }
490
491 pub fn compile_single_function(
503 &mut self,
504 program: &BytecodeProgram,
505 func_index: usize,
506 feedback: Option<shape_vm::feedback::FeedbackVector>,
507 ) -> Result<
508 (
509 *const u8,
510 Vec<shape_vm::bytecode::DeoptInfo>,
511 Vec<shape_value::shape_graph::ShapeId>,
512 ),
513 String,
514 > {
515 use cranelift_module::{Linkage, Module};
516
517 let func = program
518 .functions
519 .get(func_index)
520 .ok_or_else(|| format!("Function {} not found in program", func_index))?;
521
522 let func_name = format!("tier1_fn_{}", func.name);
523
524 let mut sig = self.module.make_signature();
526 sig.params.push(AbiParam::new(types::I64)); sig.params.push(AbiParam::new(types::I64)); sig.returns.push(AbiParam::new(types::I64)); let cr_func_id = self
531 .module
532 .declare_function(&func_name, Linkage::Export, &sig)
533 .map_err(|e| format!("Failed to declare function: {}", e))?;
534
535 let mut ctx = self.module.make_context();
536 ctx.func.signature = sig;
537
538 let mut deopt_points;
539 let shape_guards;
540
541 let mut func_builder_ctx = FunctionBuilderContext::new();
542 {
543 let mut builder = FunctionBuilder::new(&mut ctx.func, &mut func_builder_ctx);
544 let entry_block = builder.create_block();
545 builder.append_block_params_for_function_params(entry_block);
546 builder.switch_to_block(entry_block);
547 builder.seal_block(entry_block);
548
549 let ctx_ptr = builder.block_params(entry_block)[0];
550 let ffi = self.build_ffi_refs(&mut builder);
553
554 let func_end = program
557 .functions
558 .iter()
559 .enumerate()
560 .filter(|(i, _)| *i != func_index)
561 .filter_map(|(_, f)| {
562 if f.entry_point > func.entry_point {
563 Some(f.entry_point)
564 } else {
565 None
566 }
567 })
568 .min()
569 .unwrap_or(program.instructions.len());
570
571 let sub_instructions = &program.instructions[func.entry_point..func_end];
572 let sub_program = BytecodeProgram {
573 instructions: sub_instructions.to_vec(),
574 constants: program.constants.clone(),
575 strings: program.strings.clone(),
576 functions: Vec::new(),
577 debug_info: Default::default(),
578 data_schema: program.data_schema.clone(),
579 module_binding_names: program.module_binding_names.clone(),
580 top_level_locals_count: program.top_level_locals_count,
581 top_level_local_storage_hints: program
582 .function_local_storage_hints
583 .get(func_index)
584 .cloned()
585 .unwrap_or_default(),
586 type_schema_registry: program.type_schema_registry.clone(),
587 module_binding_storage_hints: program.module_binding_storage_hints.clone(),
588 function_local_storage_hints: Vec::new(),
589 top_level_frame: None,
590 compiled_annotations: program.compiled_annotations.clone(),
591 trait_method_symbols: program.trait_method_symbols.clone(),
592 expanded_function_defs: program.expanded_function_defs.clone(),
593 string_index: Default::default(),
594 foreign_functions: program.foreign_functions.clone(),
595 native_struct_layouts: program.native_struct_layouts.clone(),
596 content_addressed: None,
597 function_blob_hashes: Vec::new(),
598 };
599
600 let user_func_arities: HashMap<u16, u16> = program
607 .functions
608 .iter()
609 .enumerate()
610 .map(|(i, f)| (i as u16, f.arity))
611 .collect();
612 let mut compiler = BytecodeToIR::new(
613 &mut builder,
614 &sub_program,
615 ctx_ptr,
616 ffi,
617 HashMap::new(),
618 user_func_arities,
619 );
620 compiler.numeric_param_hints =
621 collect_numeric_param_hints(&sub_program, func.arity, &func.ref_params);
622 compiler.func_locals_count = func.locals_count;
623
624 if let Some(mut fv) = feedback {
628 fv.rebase(func.entry_point);
629 compiler.set_feedback(fv);
630 }
631
632 const LOCALS_BYTE_OFFSET: i32 = 64; for arg_idx in 0..func.arity {
636 let offset = LOCALS_BYTE_OFFSET + (arg_idx as i32) * 8;
637 let arg_val =
638 compiler
639 .builder
640 .ins()
641 .load(types::I64, MemFlags::trusted(), ctx_ptr, offset);
642 let var = compiler.get_or_create_local(arg_idx);
643 compiler.builder.def_var(var, arg_val);
644 }
645
646 let signal = compiler.compile()?;
647 deopt_points = compiler.take_deopt_points();
648 for dp in &mut deopt_points {
650 dp.resume_ip += func.entry_point;
651 }
652 shape_guards = compiler.take_shape_guards();
653 drop(compiler);
655
656 use crate::context::STACK_OFFSET;
662 let zero = builder.ins().iconst(types::I32, 0);
663 let is_deopt = builder.ins().icmp(IntCC::SignedLessThan, signal, zero);
664
665 let deopt_block = builder.create_block();
666 let success_block = builder.create_block();
667 let merge_block = builder.create_block();
668 builder.append_block_param(merge_block, types::I64);
669
670 builder
671 .ins()
672 .brif(is_deopt, deopt_block, &[], success_block, &[]);
673
674 builder.switch_to_block(deopt_block);
676 builder.seal_block(deopt_block);
677 let deopt_sentinel = builder.ins().iconst(types::I64, -1i64); builder.ins().jump(merge_block, &[deopt_sentinel]);
679
680 builder.switch_to_block(success_block);
682 builder.seal_block(success_block);
683 let result_val =
684 builder
685 .ins()
686 .load(types::I64, MemFlags::trusted(), ctx_ptr, STACK_OFFSET);
687 builder.ins().jump(merge_block, &[result_val]);
688
689 builder.switch_to_block(merge_block);
691 builder.seal_block(merge_block);
692 let ret_val = builder.block_params(merge_block)[0];
693 builder.ins().return_(&[ret_val]);
694 builder.finalize();
695 }
696
697 self.module
698 .define_function(cr_func_id, &mut ctx)
699 .map_err(|e| format!("Failed to define function: {:?}", e))?;
700
701 self.module.clear_context(&mut ctx);
702 self.module
703 .finalize_definitions()
704 .map_err(|e| format!("Failed to finalize: {:?}", e))?;
705
706 let code_ptr = self.module.get_finalized_function(cr_func_id);
707 self.compiled_functions.insert(func_name, code_ptr);
708
709 Ok((code_ptr, deopt_points, shape_guards))
710 }
711
712 pub fn compile_optimizing_function(
726 &mut self,
727 program: &BytecodeProgram,
728 func_index: usize,
729 feedback: shape_vm::feedback::FeedbackVector,
730 callee_feedback: &HashMap<u16, shape_vm::feedback::FeedbackVector>,
731 ) -> Result<
732 (
733 *const u8,
734 Vec<shape_vm::bytecode::DeoptInfo>,
735 Vec<shape_value::shape_graph::ShapeId>,
736 ),
737 String,
738 > {
739 use cranelift_module::{Linkage, Module};
740
741 let func = program
742 .functions
743 .get(func_index)
744 .ok_or_else(|| format!("Function {} not found in program", func_index))?;
745
746 let mut user_func_arities: HashMap<u16, u16> = HashMap::new();
753 for (idx, f) in program.functions.iter().enumerate() {
754 user_func_arities.insert(idx as u16, f.arity);
755 }
756
757 let dc_name = format!("opt_dc_{}_{}", func_index, func.name);
758 let mut dc_sig = self.module.make_signature();
759 dc_sig.params.push(AbiParam::new(types::I64)); for _ in 0..func.arity {
761 dc_sig.params.push(AbiParam::new(types::I64));
762 }
763 dc_sig.returns.push(AbiParam::new(types::I32)); let target_dc_func_id = self
765 .module
766 .declare_function(&dc_name, Linkage::Local, &dc_sig)
767 .map_err(|e| format!("Failed to declare function {}: {}", func.name, e))?;
768
769 let mut deopt_points;
770 let shape_guards;
771 {
772 let mut dc_sig = self.module.make_signature();
773 dc_sig.params.push(AbiParam::new(types::I64)); for _ in 0..func.arity {
775 dc_sig.params.push(AbiParam::new(types::I64));
776 }
777 dc_sig.returns.push(AbiParam::new(types::I32));
778
779 let mut ctx = self.module.make_context();
780 ctx.func.signature = dc_sig;
781
782 let mut func_builder_ctx = FunctionBuilderContext::new();
783 {
784 let mut builder = FunctionBuilder::new(&mut ctx.func, &mut func_builder_ctx);
785 let entry_block = builder.create_block();
786 builder.append_block_params_for_function_params(entry_block);
787 builder.switch_to_block(entry_block);
788 builder.seal_block(entry_block);
789
790 let ctx_ptr = builder.block_params(entry_block)[0];
791
792 let mut user_func_refs: HashMap<u16, FuncRef> = HashMap::new();
794 {
795 let func_ref = self
796 .module
797 .declare_func_in_func(target_dc_func_id, builder.func);
798 user_func_refs.insert(func_index as u16, func_ref);
799 }
800
801 for (_slot_offset, slot) in feedback.slots.iter() {
806 if let shape_vm::feedback::FeedbackSlot::Call(fb) = slot {
807 if fb.state == shape_vm::feedback::ICState::Monomorphic {
808 if let Some(target) = fb.targets.first() {
809 let target_id = target.function_id;
810 if target_id != func_index as u16
811 && !user_func_refs.contains_key(&target_id)
812 {
813 if let Some(&(callee_func_id, _callee_arity)) =
815 self.compiled_dc_funcs.get(&target_id)
816 {
817 let callee_ref = self
818 .module
819 .declare_func_in_func(callee_func_id, builder.func);
820 user_func_refs.insert(target_id, callee_ref);
821 }
822 }
823 }
824 }
825 }
826 }
827
828 let ffi = self.build_ffi_refs(&mut builder);
829
830 let func_end = program
832 .functions
833 .iter()
834 .enumerate()
835 .filter(|(i, _)| *i != func_index)
836 .filter_map(|(_, f)| {
837 if f.entry_point > func.entry_point {
838 Some(f.entry_point)
839 } else {
840 None
841 }
842 })
843 .min()
844 .unwrap_or(program.instructions.len());
845
846 let target_instructions = &program.instructions[func.entry_point..func_end];
847 let target_instr_count = target_instructions.len();
848
849 let full_candidates = BytecodeToIR::analyze_inline_candidates(program);
858 let mut sub_instructions_vec = target_instructions.to_vec();
859 let mut callee_inline_map: HashMap<u16, InlineCandidate> = HashMap::new();
861 let mut callee_skip_ranges: Vec<(usize, usize)> = Vec::new();
863 let mut callee_feedback_offsets: Vec<(u16, usize)> = Vec::new();
865
866 for instr in target_instructions {
867 if let shape_vm::bytecode::OpCode::Call = instr.opcode {
868 if let Some(shape_vm::bytecode::Operand::Function(fn_id)) = &instr.operand {
869 let callee_id = fn_id.0;
870 if callee_id == func_index as u16
872 || callee_inline_map.contains_key(&callee_id)
873 {
874 continue;
875 }
876 if let Some(candidate) = full_candidates.get(&callee_id) {
877 let callee_start = candidate.entry_point;
878 let callee_end = callee_start + candidate.instruction_count;
879 if callee_end <= program.instructions.len() {
880 let callee_instrs =
881 &program.instructions[callee_start..callee_end];
882 let rebased_entry = sub_instructions_vec.len();
883 sub_instructions_vec.extend_from_slice(callee_instrs);
884 let rebased_end = sub_instructions_vec.len();
885
886 callee_inline_map.insert(
887 callee_id,
888 InlineCandidate {
889 entry_point: rebased_entry,
890 instruction_count: candidate.instruction_count,
891 arity: candidate.arity,
892 locals_count: candidate.locals_count,
893 },
894 );
895 callee_skip_ranges.push((rebased_entry, rebased_end));
896 callee_feedback_offsets.push((callee_id, rebased_entry));
897 }
898 }
899 }
900 }
901 }
902
903 let sub_program = BytecodeProgram {
904 instructions: sub_instructions_vec,
905 constants: program.constants.clone(),
906 strings: program.strings.clone(),
907 functions: Vec::new(),
908 debug_info: Default::default(),
909 data_schema: program.data_schema.clone(),
910 module_binding_names: program.module_binding_names.clone(),
911 top_level_locals_count: program.top_level_locals_count,
912 top_level_local_storage_hints: program
913 .function_local_storage_hints
914 .get(func_index)
915 .cloned()
916 .unwrap_or_default(),
917 type_schema_registry: program.type_schema_registry.clone(),
918 module_binding_storage_hints: program.module_binding_storage_hints.clone(),
919 function_local_storage_hints: Vec::new(),
920 top_level_frame: None,
921 compiled_annotations: program.compiled_annotations.clone(),
922 trait_method_symbols: program.trait_method_symbols.clone(),
923 expanded_function_defs: program.expanded_function_defs.clone(),
924 string_index: Default::default(),
925 foreign_functions: program.foreign_functions.clone(),
926 native_struct_layouts: program.native_struct_layouts.clone(),
927 content_addressed: None,
928 function_blob_hashes: Vec::new(),
929 };
930
931 let mut compiler = BytecodeToIR::new(
932 &mut builder,
933 &sub_program,
934 ctx_ptr,
935 ffi,
936 user_func_refs,
937 user_func_arities.clone(),
938 );
939 {
943 let mut outer_only = sub_program.clone();
944 outer_only.instructions.truncate(target_instr_count);
945 compiler.numeric_param_hints =
946 collect_numeric_param_hints(&outer_only, func.arity, &func.ref_params);
947 }
948 compiler.func_locals_count = func.locals_count;
949 compiler.compiling_function_id = func_index as u16;
950
951 for (callee_id, candidate) in &callee_inline_map {
954 compiler
955 .inline_candidates
956 .insert(*callee_id, candidate.clone());
957 }
958 compiler.skip_ranges = callee_skip_ranges;
961
962 let mut rebased_feedback = feedback;
964 rebased_feedback.rebase(func.entry_point);
965 for (callee_id, sub_offset) in &callee_feedback_offsets {
968 if let Some(callee_fv) = callee_feedback.get(callee_id) {
969 let mut callee_rebased = callee_fv.clone();
970 let callee_func = &program.functions[*callee_id as usize];
971 callee_rebased.rebase(callee_func.entry_point);
972 rebased_feedback.merge_at_offset(&callee_rebased, *sub_offset);
973 }
974 }
975 compiler.set_feedback(rebased_feedback);
976
977 let entry_params = compiler.builder.block_params(entry_block).to_vec();
978 for arg_idx in 0..func.arity {
979 let arg_val = entry_params[(arg_idx as usize) + 1];
980 let var = compiler.get_or_create_local(arg_idx);
981 compiler.builder.def_var(var, arg_val);
982 }
983
984 let result = compiler.compile()?;
985 deopt_points = compiler.take_deopt_points();
986
987 BytecodeToIR::verify_deopt_points(
989 &deopt_points,
990 &compiler.unboxed_int_locals,
991 &compiler.unboxed_f64_locals,
992 )?;
993
994 let rebase_ip = |sub_ip: usize| -> usize {
1003 for (callee_id, candidate) in &callee_inline_map {
1004 let re = candidate.entry_point;
1005 let re_end = re + candidate.instruction_count;
1006 if sub_ip >= re && sub_ip < re_end {
1007 let callee_entry = program.functions[*callee_id as usize].entry_point;
1008 return callee_entry + (sub_ip - re);
1009 }
1010 }
1011 func.entry_point + sub_ip
1013 };
1014
1015 for dp in &mut deopt_points {
1016 dp.resume_ip = rebase_ip(dp.resume_ip);
1017 for iframe in &mut dp.inline_frames {
1018 iframe.resume_ip = rebase_ip(iframe.resume_ip);
1019 }
1020 }
1021 shape_guards = compiler.take_shape_guards();
1022 drop(compiler);
1023
1024 builder.ins().return_(&[result]);
1025 builder.finalize();
1026 }
1027
1028 self.module
1029 .define_function(target_dc_func_id, &mut ctx)
1030 .map_err(|e| format!("Failed to define target function: {:?}", e))?;
1031 self.module.clear_context(&mut ctx);
1032
1033 self.compiled_dc_funcs
1036 .insert(func_index as u16, (target_dc_func_id, func.arity));
1037 }
1038
1039 let wrapper_name = format!("opt_wrapper_{}", func.name);
1042 let mut wrapper_sig = self.module.make_signature();
1043 wrapper_sig.params.push(AbiParam::new(types::I64)); wrapper_sig.params.push(AbiParam::new(types::I64)); wrapper_sig.returns.push(AbiParam::new(types::I64)); let wrapper_func_id = self
1048 .module
1049 .declare_function(&wrapper_name, Linkage::Export, &wrapper_sig)
1050 .map_err(|e| format!("Failed to declare wrapper: {}", e))?;
1051
1052 {
1053 let mut ctx = self.module.make_context();
1054 ctx.func.signature = wrapper_sig;
1055
1056 let mut func_builder_ctx = FunctionBuilderContext::new();
1057 {
1058 let mut builder = FunctionBuilder::new(&mut ctx.func, &mut func_builder_ctx);
1059 let entry_block = builder.create_block();
1060 builder.append_block_params_for_function_params(entry_block);
1061 builder.switch_to_block(entry_block);
1062 builder.seal_block(entry_block);
1063
1064 let ctx_ptr = builder.block_params(entry_block)[0];
1065
1066 let target_ref = self
1068 .module
1069 .declare_func_in_func(target_dc_func_id, builder.func);
1070
1071 const LOCALS_BYTE_OFFSET: i32 = 64;
1073 let mut call_args = vec![ctx_ptr];
1074 for arg_idx in 0..func.arity {
1075 let offset = LOCALS_BYTE_OFFSET + (arg_idx as i32) * 8;
1076 let arg_val =
1077 builder
1078 .ins()
1079 .load(types::I64, MemFlags::trusted(), ctx_ptr, offset);
1080 call_args.push(arg_val);
1081 }
1082
1083 let inst = builder.ins().call(target_ref, &call_args);
1084 let signal = builder.inst_results(inst)[0]; use crate::context::STACK_OFFSET;
1089 let zero = builder.ins().iconst(types::I32, 0);
1090 let is_deopt = builder.ins().icmp(IntCC::SignedLessThan, signal, zero);
1091
1092 let deopt_block = builder.create_block();
1093 let success_block = builder.create_block();
1094 let merge_block = builder.create_block();
1095 builder.append_block_param(merge_block, types::I64);
1096
1097 builder
1098 .ins()
1099 .brif(is_deopt, deopt_block, &[], success_block, &[]);
1100
1101 builder.switch_to_block(deopt_block);
1102 builder.seal_block(deopt_block);
1103 let deopt_sentinel = builder.ins().iconst(types::I64, -1i64);
1104 builder.ins().jump(merge_block, &[deopt_sentinel]);
1105
1106 builder.switch_to_block(success_block);
1107 builder.seal_block(success_block);
1108 let result_val =
1109 builder
1110 .ins()
1111 .load(types::I64, MemFlags::trusted(), ctx_ptr, STACK_OFFSET);
1112 builder.ins().jump(merge_block, &[result_val]);
1113
1114 builder.switch_to_block(merge_block);
1115 builder.seal_block(merge_block);
1116 let ret_val = builder.block_params(merge_block)[0];
1117 builder.ins().return_(&[ret_val]);
1118 builder.finalize();
1119 }
1120
1121 self.module
1122 .define_function(wrapper_func_id, &mut ctx)
1123 .map_err(|e| format!("Failed to define wrapper: {:?}", e))?;
1124 self.module.clear_context(&mut ctx);
1125 }
1126
1127 self.module
1129 .finalize_definitions()
1130 .map_err(|e| format!("Failed to finalize: {:?}", e))?;
1131
1132 let code_ptr = self.module.get_finalized_function(wrapper_func_id);
1133 self.compiled_functions.insert(wrapper_name, code_ptr);
1134
1135 Ok((code_ptr, deopt_points, shape_guards))
1136 }
1137
1138 pub fn compile_program_selective(
1147 &mut self,
1148 name: &str,
1149 program: &BytecodeProgram,
1150 ) -> Result<(JittedStrategyFn, MixedFunctionTable), String> {
1151 use super::accessors::preflight_instructions;
1152
1153 maybe_emit_numeric_metrics(program);
1154
1155 let mut jit_compatible: Vec<bool> = Vec::with_capacity(program.functions.len());
1157
1158 for (_idx, func) in program.functions.iter().enumerate() {
1159 if func.body_length == 0 {
1160 jit_compatible.push(false);
1161 continue;
1162 }
1163 let func_end = func.entry_point + func.body_length;
1164 let instructions = &program.instructions[func.entry_point..func_end];
1165 let report = preflight_instructions(instructions);
1166 jit_compatible.push(report.can_jit());
1167 }
1168
1169 {
1172 let skip_ranges = Self::compute_skip_ranges(program);
1173 let main_instructions: Vec<_> = program
1174 .instructions
1175 .iter()
1176 .enumerate()
1177 .filter(|(i, _)| !skip_ranges.iter().any(|(s, e)| *i >= *s && *i < *e))
1178 .map(|(_, instr)| instr.clone())
1179 .collect();
1180 let main_report = preflight_instructions(&main_instructions);
1181 if !main_report.can_jit() {
1182 return Err(format!(
1183 "Main code contains unsupported constructs: {:?}",
1184 main_report
1185 ));
1186 }
1187 }
1188
1189 let mut user_func_arities: HashMap<u16, u16> = HashMap::new();
1194 let mut user_func_ids: HashMap<u16, cranelift_module::FuncId> = HashMap::new();
1195
1196 for (idx, func) in program.functions.iter().enumerate() {
1197 if !jit_compatible[idx] {
1198 user_func_arities.insert(idx as u16, func.arity);
1199 continue;
1200 }
1201 let func_name = format!("{}_f{}_{}", name, idx, func.name);
1205 let mut user_sig = self.module.make_signature();
1206 user_sig.params.push(AbiParam::new(types::I64)); for _ in 0..func.arity {
1208 user_sig.params.push(AbiParam::new(types::I64));
1209 }
1210 user_sig.returns.push(AbiParam::new(types::I32));
1211 let func_id = self
1212 .module
1213 .declare_function(&func_name, Linkage::Local, &user_sig)
1214 .map_err(|e| format!("Failed to pre-declare function {}: {}", func.name, e))?;
1215 user_func_ids.insert(idx as u16, func_id);
1216 user_func_arities.insert(idx as u16, func.arity);
1217 }
1218
1219 let main_func_id = self.compile_strategy_with_user_funcs(
1221 name,
1222 program,
1223 &user_func_ids,
1224 &user_func_arities,
1225 )?;
1226
1227 for (idx, func) in program.functions.iter().enumerate() {
1229 if !jit_compatible[idx] {
1230 continue;
1231 }
1232 let func_name = format!("{}_f{}_{}", name, idx, func.name);
1233 self.compile_function_with_user_funcs(
1234 &func_name,
1235 program,
1236 idx,
1237 &user_func_ids,
1238 &user_func_arities,
1239 )?;
1240 }
1241
1242 self.module
1243 .finalize_definitions()
1244 .map_err(|e| format!("Failed to finalize definitions: {:?}", e))?;
1245
1246 let main_code_ptr = self.module.get_finalized_function(main_func_id);
1247 self.compiled_functions
1248 .insert(name.to_string(), main_code_ptr);
1249
1250 let mut mixed_table = MixedFunctionTable::with_capacity(program.functions.len());
1252
1253 self.function_table.clear();
1254 for (idx, func) in program.functions.iter().enumerate() {
1255 if jit_compatible[idx] {
1256 if let Some(&func_id) = user_func_ids.get(&(idx as u16)) {
1257 let ptr = self.module.get_finalized_function(func_id);
1258 while self.function_table.len() <= idx {
1259 self.function_table.push(std::ptr::null());
1260 }
1261 self.function_table[idx] = ptr;
1262 let func_name = format!("{}_f{}_{}", name, idx, func.name);
1263 self.compiled_functions.insert(func_name, ptr);
1264 mixed_table.insert(idx, FunctionEntry::Native(ptr));
1265 }
1266 } else {
1267 while self.function_table.len() <= idx {
1268 self.function_table.push(std::ptr::null());
1269 }
1270 mixed_table.insert(idx, FunctionEntry::Interpreted(idx as u16));
1272 }
1273 }
1274
1275 let jit_fn = unsafe { std::mem::transmute(main_code_ptr) };
1276 Ok((jit_fn, mixed_table))
1277 }
1278}