1use crate::{Bytecode, Opcode, Value};
10use alloc::string::String;
11use alloc::vec;
12use alloc::vec::Vec;
13
14#[derive(Debug, Clone, Copy, PartialEq)]
17pub enum RegOp {
18 Mov { dst: u16, src: u16 },
20 LoadConst { dst: u16, k: u32 },
22 LoadGlobal { dst: u16, slot: u32 },
24 StoreGlobal { slot: u32, src: u16 },
26 LoadUpvalue { dst: u16, idx: u32 },
28 StoreUpvalue { idx: u32, src: u16 },
30 Bin {
32 op: BinKind,
33 dst: u16,
34 lhs: u16,
35 rhs: u16,
36 },
37 Un { op: UnKind, dst: u16, src: u16 },
39 Cmp {
41 op: CmpKind,
42 dst: u16,
43 lhs: u16,
44 rhs: u16,
45 },
46 CmpImm {
48 op: CmpKind,
49 dst: u16,
50 lhs: u16,
51 imm: i64,
52 },
53 BinImm {
55 op: BinKind,
56 dst: u16,
57 lhs: u16,
58 imm: i64,
59 },
60 Jump { target: u32 },
62 JumpIfFalse { target: u32, cond: u16 },
64 JumpIfTrue { target: u32, cond: u16 },
66 Call {
68 dst: u16,
69 func: u16,
70 args: u16,
71 argc: u8,
72 },
73 TailCall { func: u16, args: u16, argc: u8 },
75 Ret { src: u16 },
77 GetProp { dst: u16, obj: u16, name: u32 },
79 SetProp { obj: u16, name: u32, src: u16 },
81 Invoke {
83 dst: u16,
84 obj: u16,
85 name: u32,
86 args: u16,
87 argc: u8,
88 },
89 BuildArr { dst: u16, start: u16, count: u16 },
91 BuildMap { dst: u16, start: u16, count: u16 },
93 IndexGet { dst: u16, obj: u16, idx: u16 },
95 IndexSet { obj: u16, idx: u16, src: u16 },
97 CreateClass { dst: u16, k: u32 },
99 CreateInstance {
100 dst: u16,
101 class: u16,
102 args: u16,
103 argc: u8,
104 },
105 MakeClosure {
106 dst: u16,
107 func: u16,
108 upvals: u16,
109 count: u8,
110 },
111 Wrap { kind: WrapKind, dst: u16, src: u16 },
113 Unwrap {
115 kind: WrapKind,
116 dst: u16,
117 flag: u16,
118 src: u16,
119 },
120 TryQ { dst: u16, src: u16 },
122 Argc { dst: u16 },
124 CallChunk {
126 dst: u16,
127 chunk: u32,
128 args: u16,
129 argc: u8,
130 },
131 Nop,
133 Halt { src: u16 },
135}
136
137#[derive(Debug, Clone, Copy, PartialEq)]
138pub enum BinKind {
139 Add,
140 Sub,
141 Mul,
142 Div,
143 Mod,
144 Pow,
145 BitAnd,
146 BitOr,
147 BitXor,
148 Shl,
149 Shr,
150 And,
151 Or,
152}
153
154#[derive(Debug, Clone, Copy, PartialEq)]
155pub enum UnKind {
156 Neg,
157 Not,
158 BitNot,
159}
160
161#[derive(Debug, Clone, Copy, PartialEq)]
162pub enum CmpKind {
163 Eq,
164 Ne,
165 Lt,
166 Le,
167 Gt,
168 Ge,
169}
170
171#[derive(Debug, Clone, Copy, PartialEq)]
172pub enum WrapKind {
173 Ok,
174 Err,
175 Some,
176}
177
178pub fn parameter_slots(bytecode: &Bytecode, chunk_idx: usize) -> u16 {
187 if chunk_idx == bytecode.main_chunk {
188 return 0;
189 }
190 let mut best: Option<usize> = None;
191 for ch in &bytecode.chunks {
192 for c in &ch.constants {
193 if let Value::Function(f) = c {
194 if f.chunk_index == chunk_idx {
195 best = Some(best.map_or(f.arity, |b: usize| b.max(f.arity)));
196 }
197 }
198 if let Value::Class(cls) = c {
199 for (_, ci) in cls.methods.iter() {
200 if *ci == chunk_idx {
201 if best.is_none() {
204 best = Some(1);
205 }
206 }
207 }
208 }
209 }
210 }
211 best.unwrap_or(0) as u16
212}
213
214#[derive(Debug, Clone)]
227pub struct RegChunk {
228 pub name: String,
229 pub code: Vec<RegOp>,
230 pub constants: Vec<Value>,
231 pub n_regs: u16,
234 pub param_slots: u16,
243 pub arity: usize,
247 pub required: usize,
249}
250
251impl RegChunk {
252 pub fn new(name: impl Into<String>) -> Self {
253 Self {
254 name: name.into(),
255 code: Vec::new(),
256 constants: Vec::new(),
257 n_regs: 0,
258 param_slots: 0,
259 arity: 0,
260 required: 0,
261 }
262 }
263}
264
265pub struct StackToRegister<'a> {
272 src_code: &'a [crate::Instruction],
273 #[allow(dead_code)]
274 src_consts: &'a [Value],
275 out: RegChunk,
276 ip_map: Vec<u32>,
278 jumps: Vec<(usize, usize)>,
280 param_slots: u16,
282 heights: Vec<u16>,
284}
285
286impl<'a> StackToRegister<'a> {
287 pub fn new(name: &str, code: &'a [crate::Instruction], consts: &'a [Value]) -> Self {
288 let mut out = RegChunk::new(name);
289 out.constants = consts.to_vec();
290 out.arity = 0;
291 out.required = 0;
292 Self {
293 src_code: code,
294 src_consts: consts,
295 out,
296 ip_map: Vec::new(),
297 jumps: Vec::new(),
298 param_slots: 0,
299 heights: Vec::new(),
300 }
301 }
302
303 pub fn with_param_slots(mut self, param_slots: u16) -> Self {
306 self.param_slots = param_slots;
307 self
308 }
309
310 fn emit(&mut self, op: RegOp) -> usize {
311 self.out.code.push(op);
312 self.out.code.len() - 1
313 }
314
315 fn use_reg(&mut self, r: u16) {
316 if r + 1 > self.out.n_regs {
317 self.out.n_regs = r + 1;
318 }
319 }
320
321 #[allow(unused_assignments)]
322 pub fn translate(mut self) -> RegChunk {
323 let param_slots = self.param_slots;
324 self.heights = compute_heights(self.src_code, param_slots);
325 self.out.n_regs = self.out.n_regs.max(param_slots);
326 self.out.param_slots = param_slots;
327 for (sip, ins) in self.src_code.iter().enumerate() {
328 self.ip_map.push(self.out.code.len() as u32);
329 let mut sp: u16 = self.heights[sip];
330 match ins.opcode {
331 Opcode::Push => {
332 let k = ins.operand.unwrap_or(0);
333 let dst = sp;
334 self.use_reg(dst);
335 self.emit(RegOp::LoadConst { dst, k });
336 sp += 1;
337 }
338 Opcode::Pop => {
339 sp = sp.saturating_sub(1);
340 }
341 Opcode::Dup => {
342 let src = sp.saturating_sub(1);
343 let dst = sp;
344 self.use_reg(dst);
345 self.emit(RegOp::Mov { dst, src });
346 sp += 1;
347 }
348 Opcode::LoadLocal => {
349 let idx = ins.operand.unwrap_or(0) as u16;
350 let dst = sp;
351 self.use_reg(idx.max(dst));
352 self.emit(RegOp::Mov { dst, src: idx });
353 sp += 1;
354 }
355 Opcode::StoreLocal => {
356 let idx = ins.operand.unwrap_or(0) as u16;
357 let src = sp.saturating_sub(1);
358 self.use_reg(idx.max(src));
359 self.emit(RegOp::Mov { dst: idx, src });
360 sp = sp.saturating_sub(1);
361 }
362 Opcode::LoadGlobal => {
363 let slot = ins.operand.unwrap_or(0);
364 let dst = sp;
365 self.use_reg(dst);
366 self.emit(RegOp::LoadGlobal { dst, slot });
367 sp += 1;
368 }
369 Opcode::StoreGlobal => {
370 let slot = ins.operand.unwrap_or(0);
371 let src = sp.saturating_sub(1);
372 self.use_reg(src);
373 self.emit(RegOp::StoreGlobal { slot, src });
374 sp = sp.saturating_sub(1);
375 }
376 Opcode::LoadUpvalue => {
377 let idx = ins.operand.unwrap_or(0);
378 let dst = sp;
379 self.use_reg(dst);
380 self.emit(RegOp::LoadUpvalue { dst, idx });
381 sp += 1;
382 }
383 Opcode::StoreUpvalue => {
384 let idx = ins.operand.unwrap_or(0);
385 let src = sp.saturating_sub(1);
386 self.use_reg(src);
387 self.emit(RegOp::StoreUpvalue { idx, src });
388 sp = sp.saturating_sub(1);
389 }
390 Opcode::Add
391 | Opcode::Sub
392 | Opcode::Mul
393 | Opcode::Div
394 | Opcode::Mod
395 | Opcode::Pow
396 | Opcode::BitAnd
397 | Opcode::BitOr
398 | Opcode::BitXor
399 | Opcode::Shl
400 | Opcode::Shr
401 | Opcode::And
402 | Opcode::Or => {
403 let kind = bin_kind(ins.opcode);
404 let rhs = sp.saturating_sub(1);
405 let lhs = sp.saturating_sub(2);
406 let dst = lhs;
407 self.use_reg(rhs.max(dst));
408 self.emit(RegOp::Bin {
409 op: kind,
410 dst,
411 lhs,
412 rhs,
413 });
414 sp = sp.saturating_sub(1);
415 }
416 Opcode::AddImm
417 | Opcode::SubImm
418 | Opcode::MulImm
419 | Opcode::EqImm
420 | Opcode::NeImm
421 | Opcode::LtImm
422 | Opcode::LeImm
423 | Opcode::GtImm
424 | Opcode::GeImm => {
425 let imm = ins.operand.unwrap_or(0) as i32 as i64;
431 let lhs = sp.saturating_sub(1);
432 let dst = lhs;
433 self.use_reg(dst);
434 let op = ins.opcode;
435 if matches!(op, Opcode::AddImm | Opcode::SubImm | Opcode::MulImm) {
436 self.emit(RegOp::BinImm {
437 op: bin_kind_imm(op),
438 dst,
439 lhs,
440 imm,
441 });
442 } else {
443 self.emit(RegOp::CmpImm {
444 op: cmp_kind_imm(op),
445 dst,
446 lhs,
447 imm,
448 });
449 }
450 }
451 Opcode::Neg | Opcode::Not | Opcode::BitNot => {
452 let src = sp.saturating_sub(1);
453 let dst = src;
454 self.use_reg(dst);
455 self.emit(RegOp::Un {
456 op: un_kind(ins.opcode),
457 dst,
458 src,
459 });
460 }
461 Opcode::Eq | Opcode::Ne | Opcode::Lt | Opcode::Le | Opcode::Gt | Opcode::Ge => {
462 let rhs = sp.saturating_sub(1);
463 let lhs = sp.saturating_sub(2);
464 let dst = lhs;
465 self.use_reg(rhs.max(dst));
466 self.emit(RegOp::Cmp {
467 op: cmp_kind(ins.opcode),
468 dst,
469 lhs,
470 rhs,
471 });
472 sp = sp.saturating_sub(1);
473 }
474 Opcode::Jump => {
475 let t = ins.operand.unwrap_or(0) as usize;
476 let site = self.emit(RegOp::Jump { target: 0 });
477 self.jumps.push((t, site));
478 }
479 Opcode::JumpIfFalse => {
480 let t = ins.operand.unwrap_or(0) as usize;
481 let cond = sp.saturating_sub(1);
482 self.use_reg(cond);
483 let site = self.emit(RegOp::JumpIfFalse { target: 0, cond });
484 self.jumps.push((t, site));
485 }
486 Opcode::JumpIfTrue => {
487 let t = ins.operand.unwrap_or(0) as usize;
488 let cond = sp.saturating_sub(1);
489 self.use_reg(cond);
490 let site = self.emit(RegOp::JumpIfTrue { target: 0, cond });
491 self.jumps.push((t, site));
492 }
493 Opcode::Call => {
494 let argc = ins.operand.unwrap_or(0) as u8;
495 let argc_us = argc as u16;
496 let func = sp.saturating_sub(argc_us + 1);
498 let args = func + 1;
499 let dst = func;
500 self.use_reg(sp.saturating_sub(1).max(dst));
501 self.emit(RegOp::Call {
502 dst,
503 func,
504 args,
505 argc,
506 });
507 sp = func + 1;
508 }
509 Opcode::TailCall => {
510 let argc = ins.operand.unwrap_or(0) as u8;
511 let argc_us = argc as u16;
512 let func = sp.saturating_sub(argc_us + 1);
513 let args = func + 1;
514 self.use_reg(sp.saturating_sub(1));
515 self.emit(RegOp::TailCall { func, args, argc });
516 sp = 0;
517 }
518 Opcode::Return => {
519 let src = sp.saturating_sub(1);
520 self.use_reg(src);
521 self.emit(RegOp::Ret { src });
522 sp = 0;
523 }
524 Opcode::GetProperty => {
525 let name = ins.operand.unwrap_or(0);
526 let obj = sp.saturating_sub(1);
527 let dst = obj;
528 self.use_reg(dst);
529 self.emit(RegOp::GetProp { dst, obj, name });
530 }
531 Opcode::SetProperty => {
532 let name = ins.operand.unwrap_or(0);
533 let src = sp.saturating_sub(1);
534 let obj = sp.saturating_sub(2);
535 self.use_reg(src.max(obj));
536 self.emit(RegOp::SetProp { obj, name, src });
537 sp = obj + 1;
542 }
543 Opcode::Invoke => {
544 let packed = ins.operand.unwrap_or(0);
545 let argc = (packed >> 16) as u8;
546 let name = packed & 0xFFFF;
547 let argc_us = argc as u16;
548 let obj = sp.saturating_sub(argc_us + 1);
549 let args = obj + 1;
550 let dst = obj;
551 self.use_reg(sp.saturating_sub(1).max(dst));
552 self.emit(RegOp::Invoke {
553 dst,
554 obj,
555 name,
556 args,
557 argc,
558 });
559 sp = obj + 1;
560 }
561 Opcode::BuildArray => {
562 let count = ins.operand.unwrap_or(0) as u16;
563 let start = sp.saturating_sub(count);
564 let dst = start;
565 self.use_reg(sp.saturating_sub(1).max(dst));
566 self.emit(RegOp::BuildArr { dst, start, count });
567 sp = start + 1;
568 }
569 Opcode::BuildMap => {
570 let count = ins.operand.unwrap_or(0) as u16; let slots = count * 2;
572 let start = sp.saturating_sub(slots);
573 let dst = start;
574 self.use_reg(sp.saturating_sub(1).max(dst));
575 self.emit(RegOp::BuildMap {
576 dst,
577 start,
578 count: slots,
579 });
580 sp = start + 1;
581 }
582 Opcode::IndexGet => {
583 let idx = sp.saturating_sub(1);
584 let obj = sp.saturating_sub(2);
585 let dst = obj;
586 self.use_reg(idx.max(dst));
587 self.emit(RegOp::IndexGet { dst, obj, idx });
588 sp = obj + 1;
589 }
590 Opcode::IndexSet => {
591 let src = sp.saturating_sub(1);
592 let idx = sp.saturating_sub(2);
593 let obj = sp.saturating_sub(3);
594 self.use_reg(src.max(obj));
595 self.emit(RegOp::IndexSet { obj, idx, src });
596 sp = obj;
598 }
599 Opcode::CreateClass => {
600 let k = ins.operand.unwrap_or(0);
601 let dst = sp;
602 self.use_reg(dst);
603 self.emit(RegOp::CreateClass { dst, k });
604 sp += 1;
605 }
606 Opcode::CreateInstance => {
607 let argc = 0u8; let _ = argc;
610 let class = sp.saturating_sub(1);
611 let dst = class;
612 self.use_reg(dst);
613 self.emit(RegOp::CreateInstance {
614 dst,
615 class,
616 args: class,
617 argc: 0,
618 });
619 }
620 Opcode::GetMethod => {
621 let name = ins.operand.unwrap_or(0);
622 let obj = sp.saturating_sub(1);
623 let dst = obj;
624 self.use_reg(dst);
625 self.emit(RegOp::GetProp { dst, obj, name });
626 }
627 Opcode::MatchPattern => {
628 let dst = sp;
630 self.use_reg(dst);
631 let k = {
632 let v = Value::Bool(true);
634 if let Some(i) = self.out.constants.iter().position(|c| c == &v) {
635 i as u32
636 } else {
637 self.out.constants.push(v);
638 (self.out.constants.len() - 1) as u32
639 }
640 };
641 self.emit(RegOp::LoadConst { dst, k });
642 }
643 Opcode::MakeClosure => {
644 let count = ins.operand.unwrap_or(0) as u8;
645 let count_us = count as u16;
646 let upvals = sp.saturating_sub(count_us + 1);
647 let func = sp.saturating_sub(count_us + 1);
648 let func_r = sp - count_us - 1;
650 let upvals_r = func_r + 1;
651 let dst = func_r;
652 self.use_reg(sp.saturating_sub(1).max(dst));
653 self.emit(RegOp::MakeClosure {
654 dst,
655 func: func_r,
656 upvals: upvals_r,
657 count,
658 });
659 sp = func_r + 1;
660 let _ = (upvals, func);
661 }
662 Opcode::Throw => {
663 let src = sp.saturating_sub(1);
664 self.use_reg(src);
665 self.emit(RegOp::Ret { src });
666 sp = 0;
667 }
668 Opcode::TryBegin | Opcode::TryEnd | Opcode::Nop => {
669 self.emit(RegOp::Nop);
670 }
671 Opcode::WrapOk | Opcode::WrapErr | Opcode::WrapSome => {
672 let kind = match ins.opcode {
673 Opcode::WrapOk => WrapKind::Ok,
674 Opcode::WrapErr => WrapKind::Err,
675 _ => WrapKind::Some,
676 };
677 let src = sp.saturating_sub(1);
678 let dst = src;
679 self.use_reg(dst);
680 self.emit(RegOp::Wrap { kind, dst, src });
681 }
682 Opcode::UnwrapOk | Opcode::UnwrapErr | Opcode::UnwrapSome => {
683 let kind = match ins.opcode {
684 Opcode::UnwrapOk => WrapKind::Ok,
685 Opcode::UnwrapErr => WrapKind::Err,
686 _ => WrapKind::Some,
687 };
688 let src = sp.saturating_sub(1);
692 let dst = src;
693 let flag = src + 1;
694 self.use_reg(flag);
695 self.emit(RegOp::Unwrap {
696 kind,
697 dst,
698 flag,
699 src,
700 });
701 let _ = sp;
703 }
704 Opcode::Try => {
705 let src = sp.saturating_sub(1);
706 let dst = src;
707 self.use_reg(dst);
708 self.emit(RegOp::TryQ { dst, src });
709 }
710 Opcode::Argc => {
711 let dst = sp;
712 self.use_reg(dst);
713 self.emit(RegOp::Argc { dst });
714 sp += 1;
715 }
716 Opcode::Slide => {
717 let n = ins.operand.unwrap_or(0) as u16;
721 if n > 0 {
722 let src = sp.saturating_sub(1);
723 let dst = src.saturating_sub(n);
724 self.use_reg(src.max(dst));
725 self.emit(RegOp::Mov { dst, src });
726 sp = sp.saturating_sub(n);
727 }
728 }
729 Opcode::Halt => {
730 let src = sp.saturating_sub(1);
731 self.use_reg(src);
732 self.emit(RegOp::Halt { src });
733 sp = 0;
734 }
735 Opcode::CallDirect => {
736 let packed = ins.operand.unwrap_or(0);
737 let chunk = packed >> 16;
738 let argc = (packed & 0xFFFF) as u8;
739 let argc_us = argc as u16;
740 let args = sp.saturating_sub(argc_us);
742 let dst = args;
743 self.use_reg(sp.saturating_sub(1).max(dst));
744 self.emit(RegOp::CallChunk {
745 dst,
746 chunk,
747 args,
748 argc,
749 });
750 sp = args + 1;
751 }
752 }
753 }
754 let mut code = core::mem::take(&mut self.out.code);
756 let ip_map = self.ip_map.clone();
757 let jumps = self.jumps.clone();
758 for (stack_target, site) in jumps {
759 let reg_target = if stack_target < ip_map.len() {
760 ip_map[stack_target]
761 } else {
762 code.len() as u32
763 };
764 match &mut code[site] {
765 RegOp::Jump { ref mut target } => *target = reg_target,
766 RegOp::JumpIfFalse { ref mut target, .. } => *target = reg_target,
767 RegOp::JumpIfTrue { ref mut target, .. } => *target = reg_target,
768 _ => {}
769 }
770 }
771 self.out.code = code;
772 self.out
773 }
774
775 pub fn with_arity(self, arity: usize, required: usize) -> RegChunk {
777 let mut c = self.translate();
778 c.arity = arity;
779 c.required = required;
780 c
781 }
782
783 pub fn translate_named(self) -> RegChunk {
784 self.translate()
785 }
786}
787
788fn bin_kind(op: Opcode) -> BinKind {
789 match op {
790 Opcode::Add | Opcode::AddImm => BinKind::Add,
791 Opcode::Sub | Opcode::SubImm => BinKind::Sub,
792 Opcode::Mul | Opcode::MulImm => BinKind::Mul,
793 Opcode::Div => BinKind::Div,
794 Opcode::Mod => BinKind::Mod,
795 Opcode::Pow => BinKind::Pow,
796 Opcode::BitAnd => BinKind::BitAnd,
797 Opcode::BitOr => BinKind::BitOr,
798 Opcode::BitXor => BinKind::BitXor,
799 Opcode::Shl => BinKind::Shl,
800 Opcode::Shr => BinKind::Shr,
801 Opcode::And => BinKind::And,
802 Opcode::Or => BinKind::Or,
803 _ => BinKind::Add,
804 }
805}
806
807fn bin_kind_imm(op: Opcode) -> BinKind {
808 match op {
809 Opcode::SubImm => BinKind::Sub,
810 Opcode::MulImm => BinKind::Mul,
811 _ => BinKind::Add,
812 }
813}
814
815fn cmp_kind(op: Opcode) -> CmpKind {
816 match op {
817 Opcode::Eq | Opcode::EqImm => CmpKind::Eq,
818 Opcode::Ne | Opcode::NeImm => CmpKind::Ne,
819 Opcode::Lt | Opcode::LtImm => CmpKind::Lt,
820 Opcode::Le | Opcode::LeImm => CmpKind::Le,
821 Opcode::Gt | Opcode::GtImm => CmpKind::Gt,
822 Opcode::Ge | Opcode::GeImm => CmpKind::Ge,
823 _ => CmpKind::Eq,
824 }
825}
826
827fn cmp_kind_imm(op: Opcode) -> CmpKind {
828 cmp_kind(op)
829}
830
831fn un_kind(op: Opcode) -> UnKind {
832 match op {
833 Opcode::Not => UnKind::Not,
834 Opcode::BitNot => UnKind::BitNot,
835 _ => UnKind::Neg,
836 }
837}
838
839fn is_unwrap(op: Opcode) -> bool {
840 matches!(
841 op,
842 Opcode::UnwrapOk | Opcode::UnwrapErr | Opcode::UnwrapSome
843 )
844}
845
846fn stack_out(op: Opcode, h: u16, operand: Option<u32>) -> u16 {
848 match op {
849 Opcode::Push
850 | Opcode::Dup
851 | Opcode::LoadLocal
852 | Opcode::LoadGlobal
853 | Opcode::LoadUpvalue
854 | Opcode::Argc
855 | Opcode::CreateClass
856 | Opcode::CreateInstance
857 | Opcode::GetMethod
858 | Opcode::MatchPattern => h.saturating_add(1),
859 Opcode::Pop | Opcode::StoreGlobal | Opcode::StoreUpvalue | Opcode::Throw => {
860 h.saturating_sub(1)
861 }
862 Opcode::Slide => h.saturating_sub(operand.unwrap_or(0) as u16),
863 Opcode::StoreLocal => {
864 let idx = operand.unwrap_or(0) as u16;
865 let after = h.saturating_sub(1);
866 if idx >= after {
867 idx + 1
868 } else {
869 after
870 }
871 }
872 Opcode::Add
873 | Opcode::Sub
874 | Opcode::Mul
875 | Opcode::Div
876 | Opcode::Mod
877 | Opcode::Pow
878 | Opcode::BitAnd
879 | Opcode::BitOr
880 | Opcode::BitXor
881 | Opcode::Shl
882 | Opcode::Shr
883 | Opcode::And
884 | Opcode::Or
885 | Opcode::Eq
886 | Opcode::Ne
887 | Opcode::Lt
888 | Opcode::Le
889 | Opcode::Gt
890 | Opcode::Ge
891 | Opcode::IndexGet => h.saturating_sub(1),
892 Opcode::IndexSet => h.saturating_sub(2),
893 Opcode::Call => {
894 let argc = operand.unwrap_or(0) as u16;
895 h.saturating_sub(argc + 1).saturating_add(1)
897 }
898 Opcode::CallDirect => {
899 let argc = operand.unwrap_or(0) as u16;
900 h.saturating_sub(argc).saturating_add(1)
901 }
902 Opcode::Invoke => {
903 let packed = operand.unwrap_or(0);
904 let argc = (packed >> 16) as u16;
905 h.saturating_sub(argc + 1).saturating_add(1)
906 }
907 Opcode::BuildArray => {
908 let count = operand.unwrap_or(0) as u16;
909 h.saturating_sub(count).saturating_add(1)
910 }
911 Opcode::BuildMap => {
912 let pairs = operand.unwrap_or(0) as u16;
913 h.saturating_sub(pairs * 2).saturating_add(1)
914 }
915 Opcode::SetProperty => h.saturating_sub(1),
916 Opcode::GetProperty => h,
918 Opcode::MakeClosure => {
919 let count = operand.unwrap_or(0) as u16;
920 h.saturating_sub(count + 1).saturating_add(1)
921 }
922 Opcode::WrapOk | Opcode::WrapErr | Opcode::WrapSome => h,
923 Opcode::UnwrapOk | Opcode::UnwrapErr | Opcode::UnwrapSome => h.saturating_add(1),
926 Opcode::Try => h,
927 Opcode::AddImm
928 | Opcode::SubImm
929 | Opcode::MulImm
930 | Opcode::EqImm
931 | Opcode::NeImm
932 | Opcode::LtImm
933 | Opcode::LeImm
934 | Opcode::GtImm
935 | Opcode::GeImm
936 | Opcode::Neg
937 | Opcode::Not
938 | Opcode::BitNot
939 | Opcode::Jump
940 | Opcode::JumpIfFalse
941 | Opcode::JumpIfTrue
942 | Opcode::TryBegin
943 | Opcode::TryEnd
944 | Opcode::Nop => h,
945 Opcode::Return | Opcode::Halt | Opcode::TailCall => h,
946 }
947}
948
949fn compute_heights(code: &[crate::Instruction], param_slots: u16) -> Vec<u16> {
951 let n = code.len();
952 if n == 0 {
953 return Vec::new();
954 }
955 let mut height = vec![u16::MAX; n];
956 let mut work = alloc::collections::VecDeque::new();
957 height[0] = param_slots;
958 work.push_back(0usize);
959
960 while let Some(ip) = work.pop_front() {
961 if ip >= n {
962 continue;
963 }
964 let h = height[ip];
965 let ins = &code[ip];
966 let out = stack_out(ins.opcode, h, ins.operand);
967 match ins.opcode {
968 Opcode::Jump => {
969 let t = ins.operand.unwrap_or(0) as usize;
970 if t < n {
971 let cur = height[t];
972 if cur == u16::MAX {
973 height[t] = out;
974 work.push_back(t);
975 } else if cur != out {
976 let m = cur.max(out);
977 if m != cur {
978 height[t] = m;
979 work.push_back(t);
980 }
981 }
982 }
983 }
984 Opcode::JumpIfFalse | Opcode::JumpIfTrue => {
985 let taken = if ip > 0 && is_unwrap(code[ip - 1].opcode) {
988 out.saturating_sub(1)
989 } else {
990 out
991 };
992 let ft = ip + 1;
994 if ft < n {
995 let cur = height[ft];
996 if cur == u16::MAX {
997 height[ft] = out;
998 work.push_back(ft);
999 } else if cur != out {
1000 let m = cur.max(out);
1001 if m != cur {
1002 height[ft] = m;
1003 work.push_back(ft);
1004 }
1005 }
1006 }
1007 let t = ins.operand.unwrap_or(0) as usize;
1008 if t < n {
1009 let cur = height[t];
1010 if cur == u16::MAX {
1011 height[t] = taken;
1012 work.push_back(t);
1013 } else if cur != taken {
1014 let m = cur.max(taken);
1015 if m != cur {
1016 height[t] = m;
1017 work.push_back(t);
1018 }
1019 }
1020 }
1021 }
1022 Opcode::Return | Opcode::Halt | Opcode::TailCall | Opcode::Throw => {}
1033 _ => {
1034 let ft = ip + 1;
1035 if ft < n {
1036 let cur = height[ft];
1037 if cur == u16::MAX {
1038 height[ft] = out;
1039 work.push_back(ft);
1040 } else if cur != out {
1041 let m = cur.max(out);
1042 if m != cur {
1043 height[ft] = m;
1044 work.push_back(ft);
1045 }
1046 }
1047 }
1048 }
1049 }
1050 }
1051
1052 for h in height.iter_mut() {
1053 if *h == u16::MAX {
1054 *h = param_slots;
1055 }
1056 }
1057 height
1058}