1use crate::{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
178#[derive(Debug, Clone)]
180pub struct RegChunk {
181 pub name: String,
182 pub code: Vec<RegOp>,
183 pub constants: Vec<Value>,
184 pub n_regs: u16,
186 pub arity: usize,
188 pub required: usize,
189}
190
191impl RegChunk {
192 pub fn new(name: impl Into<String>) -> Self {
193 Self {
194 name: name.into(),
195 code: Vec::new(),
196 constants: Vec::new(),
197 n_regs: 0,
198 arity: 0,
199 required: 0,
200 }
201 }
202}
203
204pub struct StackToRegister<'a> {
211 src_code: &'a [crate::Instruction],
212 #[allow(dead_code)]
213 src_consts: &'a [Value],
214 out: RegChunk,
215 ip_map: Vec<u32>,
217 jumps: Vec<(usize, usize)>,
219 start_sp: u16,
221 heights: Vec<u16>,
223}
224
225impl<'a> StackToRegister<'a> {
226 pub fn new(name: &str, code: &'a [crate::Instruction], consts: &'a [Value]) -> Self {
227 let mut out = RegChunk::new(name);
228 out.constants = consts.to_vec();
229 out.arity = 0;
230 out.required = 0;
231 Self {
232 src_code: code,
233 src_consts: consts,
234 out,
235 ip_map: Vec::new(),
236 jumps: Vec::new(),
237 start_sp: 0,
238 heights: Vec::new(),
239 }
240 }
241
242 pub fn with_start_sp(mut self, start_sp: u16) -> Self {
244 self.start_sp = start_sp;
245 self
246 }
247
248 fn emit(&mut self, op: RegOp) -> usize {
249 self.out.code.push(op);
250 self.out.code.len() - 1
251 }
252
253 fn use_reg(&mut self, r: u16) {
254 if r + 1 > self.out.n_regs {
255 self.out.n_regs = r + 1;
256 }
257 }
258
259 pub fn translate(mut self) -> RegChunk {
260 let start_sp = self.start_sp;
261 self.heights = compute_heights(self.src_code, start_sp);
262 self.out.n_regs = self.out.n_regs.max(start_sp);
263 for (sip, ins) in self.src_code.iter().enumerate() {
264 self.ip_map.push(self.out.code.len() as u32);
265 let mut sp: u16 = self.heights[sip];
266 match ins.opcode {
267 Opcode::Push => {
268 let k = ins.operand.unwrap_or(0);
269 let dst = sp;
270 self.use_reg(dst);
271 self.emit(RegOp::LoadConst { dst, k });
272 sp += 1;
273 }
274 Opcode::Pop => {
275 sp = sp.saturating_sub(1);
276 }
277 Opcode::Dup => {
278 let src = sp.saturating_sub(1);
279 let dst = sp;
280 self.use_reg(dst);
281 self.emit(RegOp::Mov { dst, src });
282 sp += 1;
283 }
284 Opcode::LoadLocal => {
285 let idx = ins.operand.unwrap_or(0) as u16;
286 let dst = sp;
287 self.use_reg(idx.max(dst));
288 self.emit(RegOp::Mov { dst, src: idx });
289 sp += 1;
290 }
291 Opcode::StoreLocal => {
292 let idx = ins.operand.unwrap_or(0) as u16;
293 let src = sp.saturating_sub(1);
294 self.use_reg(idx.max(src));
295 self.emit(RegOp::Mov { dst: idx, src });
296 sp = sp.saturating_sub(1);
297 }
298 Opcode::LoadGlobal => {
299 let slot = ins.operand.unwrap_or(0);
300 let dst = sp;
301 self.use_reg(dst);
302 self.emit(RegOp::LoadGlobal { dst, slot });
303 sp += 1;
304 }
305 Opcode::StoreGlobal => {
306 let slot = ins.operand.unwrap_or(0);
307 let src = sp.saturating_sub(1);
308 self.use_reg(src);
309 self.emit(RegOp::StoreGlobal { slot, src });
310 sp = sp.saturating_sub(1);
311 }
312 Opcode::LoadUpvalue => {
313 let idx = ins.operand.unwrap_or(0);
314 let dst = sp;
315 self.use_reg(dst);
316 self.emit(RegOp::LoadUpvalue { dst, idx });
317 sp += 1;
318 }
319 Opcode::StoreUpvalue => {
320 let idx = ins.operand.unwrap_or(0);
321 let src = sp.saturating_sub(1);
322 self.use_reg(src);
323 self.emit(RegOp::StoreUpvalue { idx, src });
324 sp = sp.saturating_sub(1);
325 }
326 Opcode::Add
327 | Opcode::Sub
328 | Opcode::Mul
329 | Opcode::Div
330 | Opcode::Mod
331 | Opcode::Pow
332 | Opcode::BitAnd
333 | Opcode::BitOr
334 | Opcode::BitXor
335 | Opcode::Shl
336 | Opcode::Shr
337 | Opcode::And
338 | Opcode::Or => {
339 let kind = bin_kind(ins.opcode);
340 let rhs = sp.saturating_sub(1);
341 let lhs = sp.saturating_sub(2);
342 let dst = lhs;
343 self.use_reg(rhs.max(dst));
344 self.emit(RegOp::Bin {
345 op: kind,
346 dst,
347 lhs,
348 rhs,
349 });
350 sp = sp.saturating_sub(1);
351 }
352 Opcode::AddImm
353 | Opcode::SubImm
354 | Opcode::MulImm
355 | Opcode::EqImm
356 | Opcode::NeImm
357 | Opcode::LtImm
358 | Opcode::LeImm
359 | Opcode::GtImm
360 | Opcode::GeImm => {
361 let imm = ins.operand.unwrap_or(0) as i64;
362 let lhs = sp.saturating_sub(1);
363 let dst = lhs;
364 self.use_reg(dst);
365 let op = ins.opcode;
366 if matches!(op, Opcode::AddImm | Opcode::SubImm | Opcode::MulImm) {
367 self.emit(RegOp::BinImm {
368 op: bin_kind_imm(op),
369 dst,
370 lhs,
371 imm,
372 });
373 } else {
374 self.emit(RegOp::CmpImm {
375 op: cmp_kind_imm(op),
376 dst,
377 lhs,
378 imm,
379 });
380 }
381 }
382 Opcode::Neg | Opcode::Not | Opcode::BitNot => {
383 let src = sp.saturating_sub(1);
384 let dst = src;
385 self.use_reg(dst);
386 self.emit(RegOp::Un {
387 op: un_kind(ins.opcode),
388 dst,
389 src,
390 });
391 }
392 Opcode::Eq | Opcode::Ne | Opcode::Lt | Opcode::Le | Opcode::Gt | Opcode::Ge => {
393 let rhs = sp.saturating_sub(1);
394 let lhs = sp.saturating_sub(2);
395 let dst = lhs;
396 self.use_reg(rhs.max(dst));
397 self.emit(RegOp::Cmp {
398 op: cmp_kind(ins.opcode),
399 dst,
400 lhs,
401 rhs,
402 });
403 sp = sp.saturating_sub(1);
404 }
405 Opcode::Jump => {
406 let t = ins.operand.unwrap_or(0) as usize;
407 let site = self.emit(RegOp::Jump { target: 0 });
408 self.jumps.push((t, site));
409 }
410 Opcode::JumpIfFalse => {
411 let t = ins.operand.unwrap_or(0) as usize;
412 let cond = sp.saturating_sub(1);
413 self.use_reg(cond);
414 let site = self.emit(RegOp::JumpIfFalse { target: 0, cond });
415 self.jumps.push((t, site));
416 }
417 Opcode::JumpIfTrue => {
418 let t = ins.operand.unwrap_or(0) as usize;
419 let cond = sp.saturating_sub(1);
420 self.use_reg(cond);
421 let site = self.emit(RegOp::JumpIfTrue { target: 0, cond });
422 self.jumps.push((t, site));
423 }
424 Opcode::Call => {
425 let argc = ins.operand.unwrap_or(0) as u8;
426 let argc_us = argc as u16;
427 let func = sp.saturating_sub(argc_us + 1);
429 let args = func + 1;
430 let dst = func;
431 self.use_reg(sp.saturating_sub(1).max(dst));
432 self.emit(RegOp::Call {
433 dst,
434 func,
435 args,
436 argc,
437 });
438 sp = func + 1;
439 }
440 Opcode::TailCall => {
441 let argc = ins.operand.unwrap_or(0) as u8;
442 let argc_us = argc as u16;
443 let func = sp.saturating_sub(argc_us + 1);
444 let args = func + 1;
445 self.use_reg(sp.saturating_sub(1));
446 self.emit(RegOp::TailCall { func, args, argc });
447 sp = 0;
448 }
449 Opcode::Return => {
450 let src = sp.saturating_sub(1);
451 self.use_reg(src);
452 self.emit(RegOp::Ret { src });
453 sp = 0;
454 }
455 Opcode::GetProperty => {
456 let name = ins.operand.unwrap_or(0);
457 let obj = sp.saturating_sub(1);
458 let dst = obj;
459 self.use_reg(dst);
460 self.emit(RegOp::GetProp { dst, obj, name });
461 }
462 Opcode::SetProperty => {
463 let name = ins.operand.unwrap_or(0);
464 let src = sp.saturating_sub(1);
465 let obj = sp.saturating_sub(2);
466 self.use_reg(src.max(obj));
467 self.emit(RegOp::SetProp { obj, name, src });
468 sp = obj + 1;
473 }
474 Opcode::Invoke => {
475 let packed = ins.operand.unwrap_or(0);
476 let argc = (packed >> 16) as u8;
477 let name = packed & 0xFFFF;
478 let argc_us = argc as u16;
479 let obj = sp.saturating_sub(argc_us + 1);
480 let args = obj + 1;
481 let dst = obj;
482 self.use_reg(sp.saturating_sub(1).max(dst));
483 self.emit(RegOp::Invoke {
484 dst,
485 obj,
486 name,
487 args,
488 argc,
489 });
490 sp = obj + 1;
491 }
492 Opcode::BuildArray => {
493 let count = ins.operand.unwrap_or(0) as u16;
494 let start = sp.saturating_sub(count);
495 let dst = start;
496 self.use_reg(sp.saturating_sub(1).max(dst));
497 self.emit(RegOp::BuildArr { dst, start, count });
498 sp = start + 1;
499 }
500 Opcode::BuildMap => {
501 let count = ins.operand.unwrap_or(0) as u16; let slots = count * 2;
503 let start = sp.saturating_sub(slots);
504 let dst = start;
505 self.use_reg(sp.saturating_sub(1).max(dst));
506 self.emit(RegOp::BuildMap {
507 dst,
508 start,
509 count: slots,
510 });
511 sp = start + 1;
512 }
513 Opcode::IndexGet => {
514 let idx = sp.saturating_sub(1);
515 let obj = sp.saturating_sub(2);
516 let dst = obj;
517 self.use_reg(idx.max(dst));
518 self.emit(RegOp::IndexGet { dst, obj, idx });
519 sp = obj + 1;
520 }
521 Opcode::IndexSet => {
522 let src = sp.saturating_sub(1);
523 let idx = sp.saturating_sub(2);
524 let obj = sp.saturating_sub(3);
525 self.use_reg(src.max(obj));
526 self.emit(RegOp::IndexSet { obj, idx, src });
527 sp = obj;
529 }
530 Opcode::CreateClass => {
531 let k = ins.operand.unwrap_or(0);
532 let dst = sp;
533 self.use_reg(dst);
534 self.emit(RegOp::CreateClass { dst, k });
535 sp += 1;
536 }
537 Opcode::CreateInstance => {
538 let argc = 0u8; let _ = argc;
541 let class = sp.saturating_sub(1);
542 let dst = class;
543 self.use_reg(dst);
544 self.emit(RegOp::CreateInstance {
545 dst,
546 class,
547 args: class,
548 argc: 0,
549 });
550 }
551 Opcode::GetMethod => {
552 let name = ins.operand.unwrap_or(0);
553 let obj = sp.saturating_sub(1);
554 let dst = obj;
555 self.use_reg(dst);
556 self.emit(RegOp::GetProp { dst, obj, name });
557 }
558 Opcode::MatchPattern => {
559 let dst = sp;
561 self.use_reg(dst);
562 let k = {
563 let v = Value::Bool(true);
565 if let Some(i) = self.out.constants.iter().position(|c| c == &v) {
566 i as u32
567 } else {
568 self.out.constants.push(v);
569 (self.out.constants.len() - 1) as u32
570 }
571 };
572 self.emit(RegOp::LoadConst { dst, k });
573 }
574 Opcode::MakeClosure => {
575 let count = ins.operand.unwrap_or(0) as u8;
576 let count_us = count as u16;
577 let upvals = sp.saturating_sub(count_us + 1);
578 let func = sp.saturating_sub(count_us + 1);
579 let func_r = sp - count_us - 1;
581 let upvals_r = func_r + 1;
582 let dst = func_r;
583 self.use_reg(sp.saturating_sub(1).max(dst));
584 self.emit(RegOp::MakeClosure {
585 dst,
586 func: func_r,
587 upvals: upvals_r,
588 count,
589 });
590 sp = func_r + 1;
591 let _ = (upvals, func);
592 }
593 Opcode::Throw => {
594 let src = sp.saturating_sub(1);
595 self.use_reg(src);
596 self.emit(RegOp::Ret { src });
597 sp = 0;
598 }
599 Opcode::TryBegin | Opcode::TryEnd | Opcode::Nop => {
600 self.emit(RegOp::Nop);
601 }
602 Opcode::WrapOk | Opcode::WrapErr | Opcode::WrapSome => {
603 let kind = match ins.opcode {
604 Opcode::WrapOk => WrapKind::Ok,
605 Opcode::WrapErr => WrapKind::Err,
606 _ => WrapKind::Some,
607 };
608 let src = sp.saturating_sub(1);
609 let dst = src;
610 self.use_reg(dst);
611 self.emit(RegOp::Wrap { kind, dst, src });
612 }
613 Opcode::UnwrapOk | Opcode::UnwrapErr | Opcode::UnwrapSome => {
614 let kind = match ins.opcode {
615 Opcode::UnwrapOk => WrapKind::Ok,
616 Opcode::UnwrapErr => WrapKind::Err,
617 _ => WrapKind::Some,
618 };
619 let src = sp.saturating_sub(1);
623 let dst = src;
624 let flag = src + 1;
625 self.use_reg(flag);
626 self.emit(RegOp::Unwrap {
627 kind,
628 dst,
629 flag,
630 src,
631 });
632 let _ = sp;
634 }
635 Opcode::Try => {
636 let src = sp.saturating_sub(1);
637 let dst = src;
638 self.use_reg(dst);
639 self.emit(RegOp::TryQ { dst, src });
640 }
641 Opcode::Argc => {
642 let dst = sp;
643 self.use_reg(dst);
644 self.emit(RegOp::Argc { dst });
645 sp += 1;
646 }
647 Opcode::Halt => {
648 let src = sp.saturating_sub(1);
649 self.use_reg(src);
650 self.emit(RegOp::Halt { src });
651 sp = 0;
652 }
653 Opcode::CallDirect => {
654 let packed = ins.operand.unwrap_or(0);
655 let chunk = packed >> 16;
656 let argc = (packed & 0xFFFF) as u8;
657 let argc_us = argc as u16;
658 let args = sp.saturating_sub(argc_us);
660 let dst = args;
661 self.use_reg(sp.saturating_sub(1).max(dst));
662 self.emit(RegOp::CallChunk {
663 dst,
664 chunk,
665 args,
666 argc,
667 });
668 sp = args + 1;
669 }
670 }
671 }
672 let mut code = core::mem::take(&mut self.out.code);
674 let ip_map = self.ip_map.clone();
675 let jumps = self.jumps.clone();
676 for (stack_target, site) in jumps {
677 let reg_target = if stack_target < ip_map.len() {
678 ip_map[stack_target]
679 } else {
680 code.len() as u32
681 };
682 match &mut code[site] {
683 RegOp::Jump { ref mut target } => *target = reg_target,
684 RegOp::JumpIfFalse { ref mut target, .. } => *target = reg_target,
685 RegOp::JumpIfTrue { ref mut target, .. } => *target = reg_target,
686 _ => {}
687 }
688 }
689 self.out.code = code;
690 self.out
691 }
692
693 pub fn with_arity(self, arity: usize, required: usize) -> RegChunk {
695 let mut c = self.translate();
696 c.arity = arity;
697 c.required = required;
698 c
699 }
700
701 pub fn translate_named(self) -> RegChunk {
702 self.translate()
703 }
704}
705
706fn bin_kind(op: Opcode) -> BinKind {
707 match op {
708 Opcode::Add | Opcode::AddImm => BinKind::Add,
709 Opcode::Sub | Opcode::SubImm => BinKind::Sub,
710 Opcode::Mul | Opcode::MulImm => BinKind::Mul,
711 Opcode::Div => BinKind::Div,
712 Opcode::Mod => BinKind::Mod,
713 Opcode::Pow => BinKind::Pow,
714 Opcode::BitAnd => BinKind::BitAnd,
715 Opcode::BitOr => BinKind::BitOr,
716 Opcode::BitXor => BinKind::BitXor,
717 Opcode::Shl => BinKind::Shl,
718 Opcode::Shr => BinKind::Shr,
719 Opcode::And => BinKind::And,
720 Opcode::Or => BinKind::Or,
721 _ => BinKind::Add,
722 }
723}
724
725fn bin_kind_imm(op: Opcode) -> BinKind {
726 match op {
727 Opcode::SubImm => BinKind::Sub,
728 Opcode::MulImm => BinKind::Mul,
729 _ => BinKind::Add,
730 }
731}
732
733fn cmp_kind(op: Opcode) -> CmpKind {
734 match op {
735 Opcode::Eq | Opcode::EqImm => CmpKind::Eq,
736 Opcode::Ne | Opcode::NeImm => CmpKind::Ne,
737 Opcode::Lt | Opcode::LtImm => CmpKind::Lt,
738 Opcode::Le | Opcode::LeImm => CmpKind::Le,
739 Opcode::Gt | Opcode::GtImm => CmpKind::Gt,
740 Opcode::Ge | Opcode::GeImm => CmpKind::Ge,
741 _ => CmpKind::Eq,
742 }
743}
744
745fn cmp_kind_imm(op: Opcode) -> CmpKind {
746 cmp_kind(op)
747}
748
749fn un_kind(op: Opcode) -> UnKind {
750 match op {
751 Opcode::Not => UnKind::Not,
752 Opcode::BitNot => UnKind::BitNot,
753 _ => UnKind::Neg,
754 }
755}
756
757fn is_unwrap(op: Opcode) -> bool {
758 matches!(
759 op,
760 Opcode::UnwrapOk | Opcode::UnwrapErr | Opcode::UnwrapSome
761 )
762}
763
764fn stack_out(op: Opcode, h: u16, operand: Option<u32>) -> u16 {
766 match op {
767 Opcode::Push
768 | Opcode::Dup
769 | Opcode::LoadLocal
770 | Opcode::LoadGlobal
771 | Opcode::LoadUpvalue
772 | Opcode::Argc
773 | Opcode::CreateClass
774 | Opcode::CreateInstance
775 | Opcode::GetMethod
776 | Opcode::MatchPattern => h.saturating_add(1),
777 Opcode::Pop
778 | Opcode::StoreGlobal
779 | Opcode::StoreUpvalue
780 | Opcode::Throw => h.saturating_sub(1),
781 Opcode::StoreLocal => {
782 let idx = operand.unwrap_or(0) as u16;
783 let after = h.saturating_sub(1);
784 if idx >= after {
785 idx + 1
786 } else {
787 after
788 }
789 }
790 Opcode::Add
791 | Opcode::Sub
792 | Opcode::Mul
793 | Opcode::Div
794 | Opcode::Mod
795 | Opcode::Pow
796 | Opcode::BitAnd
797 | Opcode::BitOr
798 | Opcode::BitXor
799 | Opcode::Shl
800 | Opcode::Shr
801 | Opcode::And
802 | Opcode::Or
803 | Opcode::Eq
804 | Opcode::Ne
805 | Opcode::Lt
806 | Opcode::Le
807 | Opcode::Gt
808 | Opcode::Ge
809 | Opcode::IndexGet => h.saturating_sub(1),
810 Opcode::IndexSet => h.saturating_sub(2),
811 Opcode::Call => {
812 let argc = operand.unwrap_or(0) as u16;
813 h.saturating_sub(argc + 1).saturating_add(1)
815 }
816 Opcode::CallDirect => {
817 let argc = operand.unwrap_or(0) as u16;
818 h.saturating_sub(argc).saturating_add(1)
819 }
820 Opcode::Invoke => {
821 let packed = operand.unwrap_or(0);
822 let argc = (packed >> 16) as u16;
823 h.saturating_sub(argc + 1).saturating_add(1)
824 }
825 Opcode::BuildArray => {
826 let count = operand.unwrap_or(0) as u16;
827 h.saturating_sub(count).saturating_add(1)
828 }
829 Opcode::BuildMap => {
830 let pairs = operand.unwrap_or(0) as u16;
831 h.saturating_sub(pairs * 2).saturating_add(1)
832 }
833 Opcode::SetProperty => h.saturating_sub(1),
834 Opcode::GetProperty => h,
836 Opcode::MakeClosure => {
837 let count = operand.unwrap_or(0) as u16;
838 h.saturating_sub(count + 1).saturating_add(1)
839 }
840 Opcode::WrapOk | Opcode::WrapErr | Opcode::WrapSome => h,
841 Opcode::UnwrapOk | Opcode::UnwrapErr | Opcode::UnwrapSome => h.saturating_add(1),
844 Opcode::Try => h,
845 Opcode::AddImm
846 | Opcode::SubImm
847 | Opcode::MulImm
848 | Opcode::EqImm
849 | Opcode::NeImm
850 | Opcode::LtImm
851 | Opcode::LeImm
852 | Opcode::GtImm
853 | Opcode::GeImm
854 | Opcode::Neg
855 | Opcode::Not
856 | Opcode::BitNot
857 | Opcode::Jump
858 | Opcode::JumpIfFalse
859 | Opcode::JumpIfTrue
860 | Opcode::TryBegin
861 | Opcode::TryEnd
862 | Opcode::Nop => h,
863 Opcode::Return | Opcode::Halt | Opcode::TailCall => h,
864 }
865}
866
867fn compute_heights(code: &[crate::Instruction], start_sp: u16) -> Vec<u16> {
869 let n = code.len();
870 if n == 0 {
871 return Vec::new();
872 }
873 let mut height = vec![u16::MAX; n];
874 let mut work = alloc::collections::VecDeque::new();
875 height[0] = start_sp;
876 work.push_back(0usize);
877
878 while let Some(ip) = work.pop_front() {
879 if ip >= n {
880 continue;
881 }
882 let h = height[ip];
883 let ins = &code[ip];
884 let out = stack_out(ins.opcode, h, ins.operand);
885 match ins.opcode {
886 Opcode::Jump => {
887 let t = ins.operand.unwrap_or(0) as usize;
888 if t < n {
889 let cur = height[t];
890 if cur == u16::MAX {
891 height[t] = out;
892 work.push_back(t);
893 } else if cur != out {
894 let m = cur.max(out);
895 if m != cur {
896 height[t] = m;
897 work.push_back(t);
898 }
899 }
900 }
901 }
902 Opcode::JumpIfFalse | Opcode::JumpIfTrue => {
903 let taken = if ip > 0 && is_unwrap(code[ip - 1].opcode) {
906 out.saturating_sub(1)
907 } else {
908 out
909 };
910 let ft = ip + 1;
912 if ft < n {
913 let cur = height[ft];
914 if cur == u16::MAX {
915 height[ft] = out;
916 work.push_back(ft);
917 } else if cur != out {
918 let m = cur.max(out);
919 if m != cur {
920 height[ft] = m;
921 work.push_back(ft);
922 }
923 }
924 }
925 let t = ins.operand.unwrap_or(0) as usize;
926 if t < n {
927 let cur = height[t];
928 if cur == u16::MAX {
929 height[t] = taken;
930 work.push_back(t);
931 } else if cur != taken {
932 let m = cur.max(taken);
933 if m != cur {
934 height[t] = m;
935 work.push_back(t);
936 }
937 }
938 }
939 }
940 Opcode::Return | Opcode::Halt | Opcode::TailCall | Opcode::Throw => {
941 if ip + 1 < n {
942 let cur = height[ip + 1];
943 if cur == u16::MAX {
944 height[ip + 1] = out;
945 work.push_back(ip + 1);
946 }
947 }
948 }
949 _ => {
950 let ft = ip + 1;
951 if ft < n {
952 let cur = height[ft];
953 if cur == u16::MAX {
954 height[ft] = out;
955 work.push_back(ft);
956 } else if cur != out {
957 let m = cur.max(out);
958 if m != cur {
959 height[ft] = m;
960 work.push_back(ft);
961 }
962 }
963 }
964 }
965 }
966 }
967
968 for h in height.iter_mut() {
969 if *h == u16::MAX {
970 *h = start_sp;
971 }
972 }
973 height
974}