1use std::collections::{HashMap, HashSet};
21
22use cranelift::prelude::*;
23use cranelift_module::{Linkage, Module};
24
25use shape_vm::bytecode::{DeoptInfo, Instruction, OpCode, Operand, OsrEntryPoint};
26use shape_vm::type_tracking::{FrameDescriptor, NativeKind};
27
28use crate::loop_analysis::LoopInfo;
29
30#[derive(Debug)]
32pub struct OsrCompilationResult {
33 pub native_code: *const u8,
35 pub entry_point: OsrEntryPoint,
37 pub deopt_points: Vec<DeoptInfo>,
39}
40
41unsafe impl Send for OsrCompilationResult {}
44
45const JIT_LOCALS_CAP: usize = 256;
48
49const LOCALS_BYTE_OFFSET: i32 = 64; fn is_osr_supported_opcode(opcode: OpCode, operand: &Option<Operand>) -> bool {
54 use shape_vm::bytecode::BuiltinFunction as BF;
55 match opcode {
56 OpCode::PushConst | OpCode::PushNull | OpCode::Pop | OpCode::Dup | OpCode::Swap => true,
58 OpCode::LoadLocal
60 | OpCode::LoadLocalTrusted
61 | OpCode::StoreLocal
62 | OpCode::StoreLocalTyped => true,
63 OpCode::LoadModuleBinding
64 | OpCode::StoreModuleBinding
65 | OpCode::StoreModuleBindingTyped => true,
66 OpCode::AddInt
68 | OpCode::SubInt
69 | OpCode::MulInt
70 | OpCode::DivInt
71 | OpCode::ModInt
72 | OpCode::PowInt => true,
73 OpCode::AddNumber
75 | OpCode::SubNumber
76 | OpCode::MulNumber
77 | OpCode::DivNumber
78 | OpCode::ModNumber
79 | OpCode::PowNumber => true,
80 OpCode::NegInt | OpCode::NegNumber => true,
82 OpCode::GtInt
84 | OpCode::LtInt
85 | OpCode::GteInt
86 | OpCode::LteInt
87 | OpCode::EqInt
88 | OpCode::NeqInt => true,
89 OpCode::GtNumber
91 | OpCode::LtNumber
92 | OpCode::GteNumber
93 | OpCode::LteNumber
94 | OpCode::EqNumber
95 | OpCode::NeqNumber => true,
96 OpCode::EqString | OpCode::GtString | OpCode::LtString | OpCode::GteString
98 | OpCode::LteString | OpCode::EqDecimal | OpCode::IsNull => true,
99 OpCode::And | OpCode::Or | OpCode::Not => true,
101 OpCode::Jump
103 | OpCode::JumpIfFalse
104 | OpCode::JumpIfFalseTrusted
105 | OpCode::JumpIfTrue
106 | OpCode::LoopStart
107 | OpCode::LoopEnd
108 | OpCode::Break
109 | OpCode::Continue => true,
110 OpCode::IntToNumber | OpCode::NumberToInt | OpCode::CastWidth => true,
112 OpCode::Return | OpCode::ReturnValue => true,
114 OpCode::Nop | OpCode::Halt => true,
116 OpCode::BuiltinCall => {
118 if let Some(Operand::Builtin(bf)) = operand {
119 matches!(
120 bf,
121 BF::Abs
122 | BF::Sqrt
123 | BF::Min
124 | BF::Max
125 | BF::Floor
126 | BF::Ceil
127 | BF::Round
128 | BF::Pow
129 )
130 } else {
131 false
132 }
133 }
134 _ => false,
135 }
136}
137
138pub fn compile_osr_loop(
153 jit: &mut crate::compiler::JITCompiler,
154 function: &shape_vm::bytecode::Function,
155 instructions: &[Instruction],
156 loop_info: &LoopInfo,
157 frame_descriptor: &FrameDescriptor,
158) -> Result<OsrCompilationResult, String> {
159 if loop_info.header_idx >= instructions.len() {
161 return Err(format!(
162 "OSR loop header {} is out of bounds (instruction count: {})",
163 loop_info.header_idx,
164 instructions.len()
165 ));
166 }
167 if loop_info.end_idx >= instructions.len() {
168 return Err(format!(
169 "OSR loop end {} is out of bounds (instruction count: {})",
170 loop_info.end_idx,
171 instructions.len()
172 ));
173 }
174
175 for idx in loop_info.header_idx..=loop_info.end_idx {
177 let instr = &instructions[idx];
178 if !is_osr_supported_opcode(instr.opcode, &instr.operand) {
179 return Err(format!(
180 "OSR unsupported opcode {:?} at instruction {}",
181 instr.opcode, idx
182 ));
183 }
184 }
185
186 let mut live_locals: Vec<u16> = loop_info
188 .body_locals_read
189 .union(&loop_info.body_locals_written)
190 .copied()
191 .collect();
192 live_locals.sort_unstable();
193
194 for &local_idx in &live_locals {
196 if local_idx as usize >= JIT_LOCALS_CAP {
197 return Err(format!(
198 "OSR local index {} exceeds JIT_LOCALS_CAP ({})",
199 local_idx, JIT_LOCALS_CAP
200 ));
201 }
202 }
203
204 let local_kinds: Vec<NativeKind> = live_locals
211 .iter()
212 .map(|&slot| {
213 frame_descriptor
214 .slots
215 .get(slot as usize)
216 .copied()
217 .unwrap_or(NativeKind::Int64)
218 })
219 .collect();
220
221 let entry_point = OsrEntryPoint {
222 bytecode_ip: loop_info.header_idx,
223 live_locals: live_locals.clone(),
224 local_kinds: local_kinds.clone(),
225 exit_ip: loop_info.end_idx + 1,
226 };
227
228 let body_locals_written: HashSet<u16> = loop_info.body_locals_written.clone();
230
231 let func_name = format!("osr_loop_f{}_ip{}", function.arity, loop_info.header_idx);
235 let mut sig = jit.module_mut().make_signature();
236 sig.params.push(AbiParam::new(types::I64)); sig.params.push(AbiParam::new(types::I64)); sig.returns.push(AbiParam::new(types::I64)); let func_id = jit
241 .module_mut()
242 .declare_function(&func_name, Linkage::Export, &sig)
243 .map_err(|e| format!("Failed to declare OSR function: {}", e))?;
244
245 let mut ctx = cranelift::codegen::Context::new();
246 ctx.func.signature = sig;
247
248 {
249 let mut builder = FunctionBuilder::new(&mut ctx.func, jit.builder_context_mut());
250
251 let entry_block = builder.create_block();
253 let exit_block = builder.create_block();
254 let deopt_block = builder.create_block();
255
256 let mut block_map: HashMap<usize, Block> = HashMap::new();
258 let header_block = builder.create_block();
260 block_map.insert(loop_info.header_idx, header_block);
261
262 for idx in loop_info.header_idx..=loop_info.end_idx {
263 let instr = &instructions[idx];
264 match instr.opcode {
265 OpCode::Jump
266 | OpCode::JumpIfFalse
267 | OpCode::JumpIfFalseTrusted
268 | OpCode::JumpIfTrue => {
269 if let Some(Operand::Offset(off)) = instr.operand {
270 let target = (idx as i64 + off as i64 + 1) as usize;
271 if target >= loop_info.header_idx
272 && target <= loop_info.end_idx + 1
273 && !block_map.contains_key(&target)
274 {
275 let blk = builder.create_block();
276 block_map.insert(target, blk);
277 }
278 }
279 }
280 _ => {}
281 }
282 match instr.opcode {
285 OpCode::JumpIfFalse | OpCode::JumpIfFalseTrusted | OpCode::JumpIfTrue => {
286 let fall_through = idx + 1;
287 if fall_through >= loop_info.header_idx
288 && fall_through <= loop_info.end_idx
289 && !block_map.contains_key(&fall_through)
290 {
291 let blk = builder.create_block();
292 block_map.insert(fall_through, blk);
293 }
294 }
295 _ => {}
296 }
297 }
298
299 let max_local = live_locals.iter().copied().max().unwrap_or(0) as usize;
301 for local_idx in 0..=max_local {
302 builder.declare_var(Variable::new(local_idx), types::I64);
303 }
304 let stack_var_base = JIT_LOCALS_CAP;
306 let max_stack_depth = 32usize;
307 for s in 0..max_stack_depth {
308 builder.declare_var(Variable::new(stack_var_base + s), types::I64);
309 }
310
311 builder.append_block_params_for_function_params(entry_block);
313 builder.switch_to_block(entry_block);
314 builder.seal_block(entry_block);
315
316 let ctx_ptr = builder.block_params(entry_block)[0];
317
318 for &local_idx in &live_locals {
320 let offset = LOCALS_BYTE_OFFSET + (local_idx as i32) * 8;
321 let val = builder
322 .ins()
323 .load(types::I64, MemFlags::trusted(), ctx_ptr, offset);
324 builder.def_var(Variable::new(local_idx as usize), val);
325 }
326
327 builder.ins().jump(header_block, &[]);
329
330 let mut stack_depth: usize = 0;
333 let mut block_terminated: bool = false;
335
336 macro_rules! stack_push {
337 ($builder:expr, $val:expr, $depth:expr) => {{
338 let var = Variable::new(stack_var_base + $depth);
339 $builder.def_var(var, $val);
340 $depth += 1;
341 }};
342 }
343 macro_rules! stack_pop {
344 ($builder:expr, $depth:expr) => {{
345 $depth -= 1;
346 let var = Variable::new(stack_var_base + $depth);
347 $builder.use_var(var)
348 }};
349 }
350
351 for idx in loop_info.header_idx..=loop_info.end_idx {
352 if let Some(&blk) = block_map.get(&idx) {
354 if idx != loop_info.header_idx || block_terminated {
355 if !block_terminated {
356 builder.ins().jump(blk, &[]);
357 }
358 }
359 builder.switch_to_block(blk);
360 block_terminated = false;
361 if idx != loop_info.header_idx {
363 builder.seal_block(blk);
364 }
365 }
366
367 if block_terminated {
369 continue;
370 }
371
372 let instr = &instructions[idx];
373 match instr.opcode {
374 OpCode::Nop | OpCode::LoopStart => {
375 }
377
378 OpCode::LoopEnd => {
379 builder.ins().jump(header_block, &[]);
381 block_terminated = true;
382 }
383
384 OpCode::PushNull => {
385 let null = builder
386 .ins()
387 .iconst(types::I64, crate::ffi::value_ffi::TAG_NULL as i64);
388 stack_push!(builder, null, stack_depth);
389 }
390
391 OpCode::PushConst => {
392 if let Some(Operand::Const(_const_idx)) = instr.operand {
393 let null = builder
397 .ins()
398 .iconst(types::I64, crate::ffi::value_ffi::TAG_NULL as i64);
399 stack_push!(builder, null, stack_depth);
400 }
401 }
402
403 OpCode::Pop => {
404 if stack_depth > 0 {
405 let _ = stack_pop!(builder, stack_depth);
406 }
407 }
408
409 OpCode::Dup => {
410 if stack_depth > 0 {
411 let var = Variable::new(stack_var_base + stack_depth - 1);
412 let val = builder.use_var(var);
413 stack_push!(builder, val, stack_depth);
414 }
415 }
416
417 OpCode::Swap => {
418 if stack_depth >= 2 {
419 let var_a = Variable::new(stack_var_base + stack_depth - 1);
420 let var_b = Variable::new(stack_var_base + stack_depth - 2);
421 let a = builder.use_var(var_a);
422 let b = builder.use_var(var_b);
423 builder.def_var(var_a, b);
424 builder.def_var(var_b, a);
425 }
426 }
427
428 OpCode::LoadLocal | OpCode::LoadLocalTrusted => {
429 if let Some(Operand::Local(local_idx)) = instr.operand {
430 let val = builder.use_var(Variable::new(local_idx as usize));
431 stack_push!(builder, val, stack_depth);
432 }
433 }
434
435 OpCode::StoreLocal => {
436 if let Some(Operand::Local(local_idx)) = instr.operand {
437 if stack_depth > 0 {
438 let val = stack_pop!(builder, stack_depth);
439 builder.def_var(Variable::new(local_idx as usize), val);
440 }
441 }
442 }
443
444 OpCode::StoreLocalTyped => {
445 if let Some(Operand::TypedLocal(local_idx, _width)) = instr.operand {
446 if stack_depth > 0 {
447 let val = stack_pop!(builder, stack_depth);
448 builder.def_var(Variable::new(local_idx as usize), val);
451 }
452 }
453 }
454
455 OpCode::AddInt => {
457 if stack_depth >= 2 {
458 let b = stack_pop!(builder, stack_depth);
459 let a = stack_pop!(builder, stack_depth);
460 let result = builder.ins().iadd(a, b);
461 stack_push!(builder, result, stack_depth);
462 }
463 }
464 OpCode::SubInt => {
465 if stack_depth >= 2 {
466 let b = stack_pop!(builder, stack_depth);
467 let a = stack_pop!(builder, stack_depth);
468 let result = builder.ins().isub(a, b);
469 stack_push!(builder, result, stack_depth);
470 }
471 }
472 OpCode::MulInt => {
473 if stack_depth >= 2 {
474 let b = stack_pop!(builder, stack_depth);
475 let a = stack_pop!(builder, stack_depth);
476 let result = builder.ins().imul(a, b);
477 stack_push!(builder, result, stack_depth);
478 }
479 }
480 OpCode::DivInt => {
481 if stack_depth >= 2 {
482 let b = stack_pop!(builder, stack_depth);
483 let a = stack_pop!(builder, stack_depth);
484 let result = builder.ins().sdiv(a, b);
485 stack_push!(builder, result, stack_depth);
486 }
487 }
488 OpCode::ModInt => {
489 if stack_depth >= 2 {
490 let b = stack_pop!(builder, stack_depth);
491 let a = stack_pop!(builder, stack_depth);
492 let result = builder.ins().srem(a, b);
493 stack_push!(builder, result, stack_depth);
494 }
495 }
496 OpCode::PowInt => {
497 builder.ins().jump(deopt_block, &[]);
499 block_terminated = true;
500 }
501
502 OpCode::AddNumber => {
505 if stack_depth >= 2 {
506 let b = stack_pop!(builder, stack_depth);
507 let a = stack_pop!(builder, stack_depth);
508 let a_f = builder.ins().bitcast(types::F64, MemFlags::new(), a);
509 let b_f = builder.ins().bitcast(types::F64, MemFlags::new(), b);
510 let r_f = builder.ins().fadd(a_f, b_f);
511 let result = builder.ins().bitcast(types::I64, MemFlags::new(), r_f);
512 stack_push!(builder, result, stack_depth);
513 }
514 }
515 OpCode::SubNumber => {
516 if stack_depth >= 2 {
517 let b = stack_pop!(builder, stack_depth);
518 let a = stack_pop!(builder, stack_depth);
519 let a_f = builder.ins().bitcast(types::F64, MemFlags::new(), a);
520 let b_f = builder.ins().bitcast(types::F64, MemFlags::new(), b);
521 let r_f = builder.ins().fsub(a_f, b_f);
522 let result = builder.ins().bitcast(types::I64, MemFlags::new(), r_f);
523 stack_push!(builder, result, stack_depth);
524 }
525 }
526 OpCode::MulNumber => {
527 if stack_depth >= 2 {
528 let b = stack_pop!(builder, stack_depth);
529 let a = stack_pop!(builder, stack_depth);
530 let a_f = builder.ins().bitcast(types::F64, MemFlags::new(), a);
531 let b_f = builder.ins().bitcast(types::F64, MemFlags::new(), b);
532 let r_f = builder.ins().fmul(a_f, b_f);
533 let result = builder.ins().bitcast(types::I64, MemFlags::new(), r_f);
534 stack_push!(builder, result, stack_depth);
535 }
536 }
537 OpCode::DivNumber => {
538 if stack_depth >= 2 {
539 let b = stack_pop!(builder, stack_depth);
540 let a = stack_pop!(builder, stack_depth);
541 let a_f = builder.ins().bitcast(types::F64, MemFlags::new(), a);
542 let b_f = builder.ins().bitcast(types::F64, MemFlags::new(), b);
543 let r_f = builder.ins().fdiv(a_f, b_f);
544 let result = builder.ins().bitcast(types::I64, MemFlags::new(), r_f);
545 stack_push!(builder, result, stack_depth);
546 }
547 }
548 OpCode::ModNumber => {
549 if stack_depth >= 2 {
550 let b = stack_pop!(builder, stack_depth);
551 let a = stack_pop!(builder, stack_depth);
552 let a_f = builder.ins().bitcast(types::F64, MemFlags::new(), a);
553 let b_f = builder.ins().bitcast(types::F64, MemFlags::new(), b);
554 let div = builder.ins().fdiv(a_f, b_f);
556 let trunced = builder.ins().trunc(div);
557 let prod = builder.ins().fmul(trunced, b_f);
558 let r_f = builder.ins().fsub(a_f, prod);
559 let result = builder.ins().bitcast(types::I64, MemFlags::new(), r_f);
560 stack_push!(builder, result, stack_depth);
561 }
562 }
563 OpCode::PowNumber => {
564 builder.ins().jump(deopt_block, &[]);
566 block_terminated = true;
567 }
568
569 OpCode::NegInt => {
570 if stack_depth >= 1 {
571 let val = stack_pop!(builder, stack_depth);
572 let result = builder.ins().ineg(val);
573 stack_push!(builder, result, stack_depth);
574 }
575 }
576 OpCode::NegNumber => {
577 if stack_depth >= 1 {
578 let val = stack_pop!(builder, stack_depth);
579 let f = builder.ins().bitcast(types::F64, MemFlags::new(), val);
580 let neg_f = builder.ins().fneg(f);
581 let result = builder.ins().bitcast(types::I64, MemFlags::new(), neg_f);
582 stack_push!(builder, result, stack_depth);
583 }
584 }
585
586 OpCode::LtInt => {
588 if stack_depth >= 2 {
589 let b = stack_pop!(builder, stack_depth);
590 let a = stack_pop!(builder, stack_depth);
591 let cmp = builder.ins().icmp(IntCC::SignedLessThan, a, b);
592 let result = builder.ins().uextend(types::I64, cmp);
593 stack_push!(builder, result, stack_depth);
594 }
595 }
596 OpCode::GtInt => {
597 if stack_depth >= 2 {
598 let b = stack_pop!(builder, stack_depth);
599 let a = stack_pop!(builder, stack_depth);
600 let cmp = builder.ins().icmp(IntCC::SignedGreaterThan, a, b);
601 let result = builder.ins().uextend(types::I64, cmp);
602 stack_push!(builder, result, stack_depth);
603 }
604 }
605 OpCode::LteInt => {
606 if stack_depth >= 2 {
607 let b = stack_pop!(builder, stack_depth);
608 let a = stack_pop!(builder, stack_depth);
609 let cmp = builder.ins().icmp(IntCC::SignedLessThanOrEqual, a, b);
610 let result = builder.ins().uextend(types::I64, cmp);
611 stack_push!(builder, result, stack_depth);
612 }
613 }
614 OpCode::GteInt => {
615 if stack_depth >= 2 {
616 let b = stack_pop!(builder, stack_depth);
617 let a = stack_pop!(builder, stack_depth);
618 let cmp = builder.ins().icmp(IntCC::SignedGreaterThanOrEqual, a, b);
619 let result = builder.ins().uextend(types::I64, cmp);
620 stack_push!(builder, result, stack_depth);
621 }
622 }
623 OpCode::EqInt => {
624 if stack_depth >= 2 {
625 let b = stack_pop!(builder, stack_depth);
626 let a = stack_pop!(builder, stack_depth);
627 let cmp = builder.ins().icmp(IntCC::Equal, a, b);
628 let result = builder.ins().uextend(types::I64, cmp);
629 stack_push!(builder, result, stack_depth);
630 }
631 }
632 OpCode::NeqInt => {
633 if stack_depth >= 2 {
634 let b = stack_pop!(builder, stack_depth);
635 let a = stack_pop!(builder, stack_depth);
636 let cmp = builder.ins().icmp(IntCC::NotEqual, a, b);
637 let result = builder.ins().uextend(types::I64, cmp);
638 stack_push!(builder, result, stack_depth);
639 }
640 }
641
642 OpCode::LtNumber => {
644 if stack_depth >= 2 {
645 let b = stack_pop!(builder, stack_depth);
646 let a = stack_pop!(builder, stack_depth);
647 let a_f = builder.ins().bitcast(types::F64, MemFlags::new(), a);
648 let b_f = builder.ins().bitcast(types::F64, MemFlags::new(), b);
649 let cmp = builder.ins().fcmp(FloatCC::LessThan, a_f, b_f);
650 let result = builder.ins().uextend(types::I64, cmp);
651 stack_push!(builder, result, stack_depth);
652 }
653 }
654 OpCode::GtNumber => {
655 if stack_depth >= 2 {
656 let b = stack_pop!(builder, stack_depth);
657 let a = stack_pop!(builder, stack_depth);
658 let a_f = builder.ins().bitcast(types::F64, MemFlags::new(), a);
659 let b_f = builder.ins().bitcast(types::F64, MemFlags::new(), b);
660 let cmp = builder.ins().fcmp(FloatCC::GreaterThan, a_f, b_f);
661 let result = builder.ins().uextend(types::I64, cmp);
662 stack_push!(builder, result, stack_depth);
663 }
664 }
665 OpCode::LteNumber => {
666 if stack_depth >= 2 {
667 let b = stack_pop!(builder, stack_depth);
668 let a = stack_pop!(builder, stack_depth);
669 let a_f = builder.ins().bitcast(types::F64, MemFlags::new(), a);
670 let b_f = builder.ins().bitcast(types::F64, MemFlags::new(), b);
671 let cmp = builder.ins().fcmp(FloatCC::LessThanOrEqual, a_f, b_f);
672 let result = builder.ins().uextend(types::I64, cmp);
673 stack_push!(builder, result, stack_depth);
674 }
675 }
676 OpCode::GteNumber => {
677 if stack_depth >= 2 {
678 let b = stack_pop!(builder, stack_depth);
679 let a = stack_pop!(builder, stack_depth);
680 let a_f = builder.ins().bitcast(types::F64, MemFlags::new(), a);
681 let b_f = builder.ins().bitcast(types::F64, MemFlags::new(), b);
682 let cmp = builder.ins().fcmp(FloatCC::GreaterThanOrEqual, a_f, b_f);
683 let result = builder.ins().uextend(types::I64, cmp);
684 stack_push!(builder, result, stack_depth);
685 }
686 }
687 OpCode::EqNumber => {
688 if stack_depth >= 2 {
689 let b = stack_pop!(builder, stack_depth);
690 let a = stack_pop!(builder, stack_depth);
691 let a_f = builder.ins().bitcast(types::F64, MemFlags::new(), a);
692 let b_f = builder.ins().bitcast(types::F64, MemFlags::new(), b);
693 let cmp = builder.ins().fcmp(FloatCC::Equal, a_f, b_f);
694 let result = builder.ins().uextend(types::I64, cmp);
695 stack_push!(builder, result, stack_depth);
696 }
697 }
698 OpCode::NeqNumber => {
699 if stack_depth >= 2 {
700 let b = stack_pop!(builder, stack_depth);
701 let a = stack_pop!(builder, stack_depth);
702 let a_f = builder.ins().bitcast(types::F64, MemFlags::new(), a);
703 let b_f = builder.ins().bitcast(types::F64, MemFlags::new(), b);
704 let cmp = builder.ins().fcmp(FloatCC::NotEqual, a_f, b_f);
705 let result = builder.ins().uextend(types::I64, cmp);
706 stack_push!(builder, result, stack_depth);
707 }
708 }
709
710 OpCode::EqString => {
712 builder.ins().jump(deopt_block, &[]);
714 block_terminated = true;
715 }
716 OpCode::GtString | OpCode::LtString | OpCode::GteString | OpCode::LteString => {
718 builder.ins().jump(deopt_block, &[]);
720 block_terminated = true;
721 }
722 OpCode::EqDecimal => {
724 builder.ins().jump(deopt_block, &[]);
726 block_terminated = true;
727 }
728 OpCode::IsNull => {
730 if stack_depth >= 1 {
731 let val = stack_pop!(builder, stack_depth);
732 let zero = builder.ins().iconst(types::I64, 0);
733 let cmp = builder.ins().icmp(IntCC::Equal, val, zero);
734 let result = builder.ins().uextend(types::I64, cmp);
735 stack_push!(builder, result, stack_depth);
736 }
737 }
738
739 OpCode::And => {
741 if stack_depth >= 2 {
742 let b = stack_pop!(builder, stack_depth);
743 let a = stack_pop!(builder, stack_depth);
744 let result = builder.ins().band(a, b);
745 stack_push!(builder, result, stack_depth);
746 }
747 }
748 OpCode::Or => {
749 if stack_depth >= 2 {
750 let b = stack_pop!(builder, stack_depth);
751 let a = stack_pop!(builder, stack_depth);
752 let result = builder.ins().bor(a, b);
753 stack_push!(builder, result, stack_depth);
754 }
755 }
756 OpCode::Not => {
757 if stack_depth >= 1 {
758 let val = stack_pop!(builder, stack_depth);
759 let zero = builder.ins().iconst(types::I64, 0);
760 let cmp = builder.ins().icmp(IntCC::Equal, val, zero);
761 let result = builder.ins().uextend(types::I64, cmp);
762 stack_push!(builder, result, stack_depth);
763 }
764 }
765
766 OpCode::IntToNumber => {
768 if stack_depth >= 1 {
769 let val = stack_pop!(builder, stack_depth);
770 let f = builder.ins().fcvt_from_sint(types::F64, val);
772 let result = builder.ins().bitcast(types::I64, MemFlags::new(), f);
773 stack_push!(builder, result, stack_depth);
774 }
775 }
776 OpCode::NumberToInt => {
777 if stack_depth >= 1 {
778 let val = stack_pop!(builder, stack_depth);
779 let f = builder.ins().bitcast(types::F64, MemFlags::new(), val);
781 let result = builder.ins().fcvt_to_sint_sat(types::I64, f);
782 stack_push!(builder, result, stack_depth);
783 }
784 }
785
786 OpCode::CastWidth => {
787 if stack_depth >= 1 {
788 if let Some(Operand::Width(width)) = &instr.operand {
789 if let Some(int_w) = width.to_int_width() {
790 let val = stack_pop!(builder, stack_depth);
791 let mask = int_w.mask() as i64;
792 let mask_val = builder.ins().iconst(types::I64, mask);
793 let truncated = builder.ins().band(val, mask_val);
794 let result = if int_w.is_signed() {
795 let bits = int_w.bits() as i64;
796 let shift = 64 - bits;
797 let shift_val = builder.ins().iconst(types::I64, shift);
798 let shifted = builder.ins().ishl(truncated, shift_val);
799 builder.ins().sshr(shifted, shift_val)
800 } else {
801 truncated
802 };
803 stack_push!(builder, result, stack_depth);
804 }
805 }
806 }
807 }
808
809 OpCode::Jump => {
811 if let Some(Operand::Offset(off)) = instr.operand {
812 let target = (idx as i64 + off as i64 + 1) as usize;
813 if target > loop_info.end_idx {
814 builder.ins().jump(exit_block, &[]);
815 } else if let Some(&blk) = block_map.get(&target) {
816 builder.ins().jump(blk, &[]);
817 } else {
818 builder.ins().jump(deopt_block, &[]);
819 }
820 block_terminated = true;
821 }
822 }
823
824 OpCode::JumpIfFalse | OpCode::JumpIfFalseTrusted => {
825 if let Some(Operand::Offset(off)) = instr.operand {
826 let target = (idx as i64 + off as i64 + 1) as usize;
827 if stack_depth > 0 {
828 let cond = stack_pop!(builder, stack_depth);
829 let zero = builder.ins().iconst(types::I64, 0);
830 let is_false = builder.ins().icmp(IntCC::Equal, cond, zero);
831
832 let target_block = if target > loop_info.end_idx {
833 exit_block
834 } else {
835 block_map.get(&target).copied().unwrap_or(deopt_block)
836 };
837 let fall_through =
838 block_map.get(&(idx + 1)).copied().unwrap_or(deopt_block);
839
840 builder
841 .ins()
842 .brif(is_false, target_block, &[], fall_through, &[]);
843 block_terminated = true;
844 }
845 }
846 }
847
848 OpCode::JumpIfTrue => {
849 if let Some(Operand::Offset(off)) = instr.operand {
850 let target = (idx as i64 + off as i64 + 1) as usize;
851 if stack_depth > 0 {
852 let cond = stack_pop!(builder, stack_depth);
853 let zero = builder.ins().iconst(types::I64, 0);
854 let is_true = builder.ins().icmp(IntCC::NotEqual, cond, zero);
855
856 let target_block = if target > loop_info.end_idx {
857 exit_block
858 } else {
859 block_map.get(&target).copied().unwrap_or(deopt_block)
860 };
861 let fall_through =
862 block_map.get(&(idx + 1)).copied().unwrap_or(deopt_block);
863
864 builder
865 .ins()
866 .brif(is_true, target_block, &[], fall_through, &[]);
867 block_terminated = true;
868 }
869 }
870 }
871
872 OpCode::Break => {
873 builder.ins().jump(exit_block, &[]);
874 block_terminated = true;
875 }
876
877 OpCode::Continue => {
878 builder.ins().jump(header_block, &[]);
879 block_terminated = true;
880 }
881
882 OpCode::Return | OpCode::ReturnValue => {
883 builder.ins().jump(exit_block, &[]);
884 block_terminated = true;
885 }
886
887 OpCode::Halt => {
888 builder.ins().jump(exit_block, &[]);
889 block_terminated = true;
890 }
891
892 OpCode::LoadModuleBinding
894 | OpCode::StoreModuleBinding
895 | OpCode::StoreModuleBindingTyped => {
896 builder.ins().jump(deopt_block, &[]);
897 block_terminated = true;
898 }
899
900 OpCode::BuiltinCall => {
902 builder.ins().jump(deopt_block, &[]);
903 block_terminated = true;
904 }
905
906 _ => {
907 builder.ins().jump(deopt_block, &[]);
909 block_terminated = true;
910 }
911 }
912 }
913
914 builder.seal_block(header_block);
916
917 builder.switch_to_block(exit_block);
919 builder.seal_block(exit_block);
920
921 for &local_idx in &live_locals {
922 if body_locals_written.contains(&local_idx) {
923 let val = builder.use_var(Variable::new(local_idx as usize));
924 let offset = LOCALS_BYTE_OFFSET + (local_idx as i32) * 8;
925 builder
926 .ins()
927 .store(MemFlags::trusted(), val, ctx_ptr, offset);
928 }
929 }
930 let zero_ret = builder.ins().iconst(types::I64, 0);
931 builder.ins().return_(&[zero_ret]);
932
933 builder.switch_to_block(deopt_block);
935 builder.seal_block(deopt_block);
936
937 for &local_idx in &live_locals {
938 let val = builder.use_var(Variable::new(local_idx as usize));
939 let offset = LOCALS_BYTE_OFFSET + (local_idx as i32) * 8;
940 builder
941 .ins()
942 .store(MemFlags::trusted(), val, ctx_ptr, offset);
943 }
944 let deopt_sentinel = builder.ins().iconst(types::I64, u64::MAX as i64);
945 builder.ins().return_(&[deopt_sentinel]);
946
947 builder.finalize();
948 }
949
950 jit.module_mut()
952 .define_function(func_id, &mut ctx)
953 .map_err(|e| format!("Failed to define OSR function: {}", e))?;
954 jit.module_mut().clear_context(&mut ctx);
955 jit.module_mut()
956 .finalize_definitions()
957 .map_err(|e| format!("Failed to finalize OSR function: {}", e))?;
958
959 let code_ptr = jit.module_mut().get_finalized_function(func_id);
960
961 Ok(OsrCompilationResult {
962 native_code: code_ptr,
963 entry_point,
964 deopt_points: Vec::new(),
965 })
966}