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