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 #[allow(unused_assignments)]
260 pub fn translate(mut self) -> RegChunk {
261 let start_sp = self.start_sp;
262 self.heights = compute_heights(self.src_code, start_sp);
263 self.out.n_regs = self.out.n_regs.max(start_sp);
264 for (sip, ins) in self.src_code.iter().enumerate() {
265 self.ip_map.push(self.out.code.len() as u32);
266 let mut sp: u16 = self.heights[sip];
267 match ins.opcode {
268 Opcode::Push => {
269 let k = ins.operand.unwrap_or(0);
270 let dst = sp;
271 self.use_reg(dst);
272 self.emit(RegOp::LoadConst { dst, k });
273 sp += 1;
274 }
275 Opcode::Pop => {
276 sp = sp.saturating_sub(1);
277 }
278 Opcode::Dup => {
279 let src = sp.saturating_sub(1);
280 let dst = sp;
281 self.use_reg(dst);
282 self.emit(RegOp::Mov { dst, src });
283 sp += 1;
284 }
285 Opcode::LoadLocal => {
286 let idx = ins.operand.unwrap_or(0) as u16;
287 let dst = sp;
288 self.use_reg(idx.max(dst));
289 self.emit(RegOp::Mov { dst, src: idx });
290 sp += 1;
291 }
292 Opcode::StoreLocal => {
293 let idx = ins.operand.unwrap_or(0) as u16;
294 let src = sp.saturating_sub(1);
295 self.use_reg(idx.max(src));
296 self.emit(RegOp::Mov { dst: idx, src });
297 sp = sp.saturating_sub(1);
298 }
299 Opcode::LoadGlobal => {
300 let slot = ins.operand.unwrap_or(0);
301 let dst = sp;
302 self.use_reg(dst);
303 self.emit(RegOp::LoadGlobal { dst, slot });
304 sp += 1;
305 }
306 Opcode::StoreGlobal => {
307 let slot = ins.operand.unwrap_or(0);
308 let src = sp.saturating_sub(1);
309 self.use_reg(src);
310 self.emit(RegOp::StoreGlobal { slot, src });
311 sp = sp.saturating_sub(1);
312 }
313 Opcode::LoadUpvalue => {
314 let idx = ins.operand.unwrap_or(0);
315 let dst = sp;
316 self.use_reg(dst);
317 self.emit(RegOp::LoadUpvalue { dst, idx });
318 sp += 1;
319 }
320 Opcode::StoreUpvalue => {
321 let idx = ins.operand.unwrap_or(0);
322 let src = sp.saturating_sub(1);
323 self.use_reg(src);
324 self.emit(RegOp::StoreUpvalue { idx, src });
325 sp = sp.saturating_sub(1);
326 }
327 Opcode::Add
328 | Opcode::Sub
329 | Opcode::Mul
330 | Opcode::Div
331 | Opcode::Mod
332 | Opcode::Pow
333 | Opcode::BitAnd
334 | Opcode::BitOr
335 | Opcode::BitXor
336 | Opcode::Shl
337 | Opcode::Shr
338 | Opcode::And
339 | Opcode::Or => {
340 let kind = bin_kind(ins.opcode);
341 let rhs = sp.saturating_sub(1);
342 let lhs = sp.saturating_sub(2);
343 let dst = lhs;
344 self.use_reg(rhs.max(dst));
345 self.emit(RegOp::Bin {
346 op: kind,
347 dst,
348 lhs,
349 rhs,
350 });
351 sp = sp.saturating_sub(1);
352 }
353 Opcode::AddImm
354 | Opcode::SubImm
355 | Opcode::MulImm
356 | Opcode::EqImm
357 | Opcode::NeImm
358 | Opcode::LtImm
359 | Opcode::LeImm
360 | Opcode::GtImm
361 | Opcode::GeImm => {
362 let imm = ins.operand.unwrap_or(0) as i64;
363 let lhs = sp.saturating_sub(1);
364 let dst = lhs;
365 self.use_reg(dst);
366 let op = ins.opcode;
367 if matches!(op, Opcode::AddImm | Opcode::SubImm | Opcode::MulImm) {
368 self.emit(RegOp::BinImm {
369 op: bin_kind_imm(op),
370 dst,
371 lhs,
372 imm,
373 });
374 } else {
375 self.emit(RegOp::CmpImm {
376 op: cmp_kind_imm(op),
377 dst,
378 lhs,
379 imm,
380 });
381 }
382 }
383 Opcode::Neg | Opcode::Not | Opcode::BitNot => {
384 let src = sp.saturating_sub(1);
385 let dst = src;
386 self.use_reg(dst);
387 self.emit(RegOp::Un {
388 op: un_kind(ins.opcode),
389 dst,
390 src,
391 });
392 }
393 Opcode::Eq | Opcode::Ne | Opcode::Lt | Opcode::Le | Opcode::Gt | Opcode::Ge => {
394 let rhs = sp.saturating_sub(1);
395 let lhs = sp.saturating_sub(2);
396 let dst = lhs;
397 self.use_reg(rhs.max(dst));
398 self.emit(RegOp::Cmp {
399 op: cmp_kind(ins.opcode),
400 dst,
401 lhs,
402 rhs,
403 });
404 sp = sp.saturating_sub(1);
405 }
406 Opcode::Jump => {
407 let t = ins.operand.unwrap_or(0) as usize;
408 let site = self.emit(RegOp::Jump { target: 0 });
409 self.jumps.push((t, site));
410 }
411 Opcode::JumpIfFalse => {
412 let t = ins.operand.unwrap_or(0) as usize;
413 let cond = sp.saturating_sub(1);
414 self.use_reg(cond);
415 let site = self.emit(RegOp::JumpIfFalse { target: 0, cond });
416 self.jumps.push((t, site));
417 }
418 Opcode::JumpIfTrue => {
419 let t = ins.operand.unwrap_or(0) as usize;
420 let cond = sp.saturating_sub(1);
421 self.use_reg(cond);
422 let site = self.emit(RegOp::JumpIfTrue { target: 0, cond });
423 self.jumps.push((t, site));
424 }
425 Opcode::Call => {
426 let argc = ins.operand.unwrap_or(0) as u8;
427 let argc_us = argc as u16;
428 let func = sp.saturating_sub(argc_us + 1);
430 let args = func + 1;
431 let dst = func;
432 self.use_reg(sp.saturating_sub(1).max(dst));
433 self.emit(RegOp::Call {
434 dst,
435 func,
436 args,
437 argc,
438 });
439 sp = func + 1;
440 }
441 Opcode::TailCall => {
442 let argc = ins.operand.unwrap_or(0) as u8;
443 let argc_us = argc as u16;
444 let func = sp.saturating_sub(argc_us + 1);
445 let args = func + 1;
446 self.use_reg(sp.saturating_sub(1));
447 self.emit(RegOp::TailCall { func, args, argc });
448 sp = 0;
449 }
450 Opcode::Return => {
451 let src = sp.saturating_sub(1);
452 self.use_reg(src);
453 self.emit(RegOp::Ret { src });
454 sp = 0;
455 }
456 Opcode::GetProperty => {
457 let name = ins.operand.unwrap_or(0);
458 let obj = sp.saturating_sub(1);
459 let dst = obj;
460 self.use_reg(dst);
461 self.emit(RegOp::GetProp { dst, obj, name });
462 }
463 Opcode::SetProperty => {
464 let name = ins.operand.unwrap_or(0);
465 let src = sp.saturating_sub(1);
466 let obj = sp.saturating_sub(2);
467 self.use_reg(src.max(obj));
468 self.emit(RegOp::SetProp { obj, name, src });
469 sp = obj + 1;
474 }
475 Opcode::Invoke => {
476 let packed = ins.operand.unwrap_or(0);
477 let argc = (packed >> 16) as u8;
478 let name = packed & 0xFFFF;
479 let argc_us = argc as u16;
480 let obj = sp.saturating_sub(argc_us + 1);
481 let args = obj + 1;
482 let dst = obj;
483 self.use_reg(sp.saturating_sub(1).max(dst));
484 self.emit(RegOp::Invoke {
485 dst,
486 obj,
487 name,
488 args,
489 argc,
490 });
491 sp = obj + 1;
492 }
493 Opcode::BuildArray => {
494 let count = ins.operand.unwrap_or(0) as u16;
495 let start = sp.saturating_sub(count);
496 let dst = start;
497 self.use_reg(sp.saturating_sub(1).max(dst));
498 self.emit(RegOp::BuildArr { dst, start, count });
499 sp = start + 1;
500 }
501 Opcode::BuildMap => {
502 let count = ins.operand.unwrap_or(0) as u16; let slots = count * 2;
504 let start = sp.saturating_sub(slots);
505 let dst = start;
506 self.use_reg(sp.saturating_sub(1).max(dst));
507 self.emit(RegOp::BuildMap {
508 dst,
509 start,
510 count: slots,
511 });
512 sp = start + 1;
513 }
514 Opcode::IndexGet => {
515 let idx = sp.saturating_sub(1);
516 let obj = sp.saturating_sub(2);
517 let dst = obj;
518 self.use_reg(idx.max(dst));
519 self.emit(RegOp::IndexGet { dst, obj, idx });
520 sp = obj + 1;
521 }
522 Opcode::IndexSet => {
523 let src = sp.saturating_sub(1);
524 let idx = sp.saturating_sub(2);
525 let obj = sp.saturating_sub(3);
526 self.use_reg(src.max(obj));
527 self.emit(RegOp::IndexSet { obj, idx, src });
528 sp = obj;
530 }
531 Opcode::CreateClass => {
532 let k = ins.operand.unwrap_or(0);
533 let dst = sp;
534 self.use_reg(dst);
535 self.emit(RegOp::CreateClass { dst, k });
536 sp += 1;
537 }
538 Opcode::CreateInstance => {
539 let argc = 0u8; let _ = argc;
542 let class = sp.saturating_sub(1);
543 let dst = class;
544 self.use_reg(dst);
545 self.emit(RegOp::CreateInstance {
546 dst,
547 class,
548 args: class,
549 argc: 0,
550 });
551 }
552 Opcode::GetMethod => {
553 let name = ins.operand.unwrap_or(0);
554 let obj = sp.saturating_sub(1);
555 let dst = obj;
556 self.use_reg(dst);
557 self.emit(RegOp::GetProp { dst, obj, name });
558 }
559 Opcode::MatchPattern => {
560 let dst = sp;
562 self.use_reg(dst);
563 let k = {
564 let v = Value::Bool(true);
566 if let Some(i) = self.out.constants.iter().position(|c| c == &v) {
567 i as u32
568 } else {
569 self.out.constants.push(v);
570 (self.out.constants.len() - 1) as u32
571 }
572 };
573 self.emit(RegOp::LoadConst { dst, k });
574 }
575 Opcode::MakeClosure => {
576 let count = ins.operand.unwrap_or(0) as u8;
577 let count_us = count as u16;
578 let upvals = sp.saturating_sub(count_us + 1);
579 let func = sp.saturating_sub(count_us + 1);
580 let func_r = sp - count_us - 1;
582 let upvals_r = func_r + 1;
583 let dst = func_r;
584 self.use_reg(sp.saturating_sub(1).max(dst));
585 self.emit(RegOp::MakeClosure {
586 dst,
587 func: func_r,
588 upvals: upvals_r,
589 count,
590 });
591 sp = func_r + 1;
592 let _ = (upvals, func);
593 }
594 Opcode::Throw => {
595 let src = sp.saturating_sub(1);
596 self.use_reg(src);
597 self.emit(RegOp::Ret { src });
598 sp = 0;
599 }
600 Opcode::TryBegin | Opcode::TryEnd | Opcode::Nop => {
601 self.emit(RegOp::Nop);
602 }
603 Opcode::WrapOk | Opcode::WrapErr | Opcode::WrapSome => {
604 let kind = match ins.opcode {
605 Opcode::WrapOk => WrapKind::Ok,
606 Opcode::WrapErr => WrapKind::Err,
607 _ => WrapKind::Some,
608 };
609 let src = sp.saturating_sub(1);
610 let dst = src;
611 self.use_reg(dst);
612 self.emit(RegOp::Wrap { kind, dst, src });
613 }
614 Opcode::UnwrapOk | Opcode::UnwrapErr | Opcode::UnwrapSome => {
615 let kind = match ins.opcode {
616 Opcode::UnwrapOk => WrapKind::Ok,
617 Opcode::UnwrapErr => WrapKind::Err,
618 _ => WrapKind::Some,
619 };
620 let src = sp.saturating_sub(1);
624 let dst = src;
625 let flag = src + 1;
626 self.use_reg(flag);
627 self.emit(RegOp::Unwrap {
628 kind,
629 dst,
630 flag,
631 src,
632 });
633 let _ = sp;
635 }
636 Opcode::Try => {
637 let src = sp.saturating_sub(1);
638 let dst = src;
639 self.use_reg(dst);
640 self.emit(RegOp::TryQ { dst, src });
641 }
642 Opcode::Argc => {
643 let dst = sp;
644 self.use_reg(dst);
645 self.emit(RegOp::Argc { dst });
646 sp += 1;
647 }
648 Opcode::Halt => {
649 let src = sp.saturating_sub(1);
650 self.use_reg(src);
651 self.emit(RegOp::Halt { src });
652 sp = 0;
653 }
654 Opcode::CallDirect => {
655 let packed = ins.operand.unwrap_or(0);
656 let chunk = packed >> 16;
657 let argc = (packed & 0xFFFF) as u8;
658 let argc_us = argc as u16;
659 let args = sp.saturating_sub(argc_us);
661 let dst = args;
662 self.use_reg(sp.saturating_sub(1).max(dst));
663 self.emit(RegOp::CallChunk {
664 dst,
665 chunk,
666 args,
667 argc,
668 });
669 sp = args + 1;
670 }
671 }
672 }
673 let mut code = core::mem::take(&mut self.out.code);
675 let ip_map = self.ip_map.clone();
676 let jumps = self.jumps.clone();
677 for (stack_target, site) in jumps {
678 let reg_target = if stack_target < ip_map.len() {
679 ip_map[stack_target]
680 } else {
681 code.len() as u32
682 };
683 match &mut code[site] {
684 RegOp::Jump { ref mut target } => *target = reg_target,
685 RegOp::JumpIfFalse { ref mut target, .. } => *target = reg_target,
686 RegOp::JumpIfTrue { ref mut target, .. } => *target = reg_target,
687 _ => {}
688 }
689 }
690 self.out.code = code;
691 self.out
692 }
693
694 pub fn with_arity(self, arity: usize, required: usize) -> RegChunk {
696 let mut c = self.translate();
697 c.arity = arity;
698 c.required = required;
699 c
700 }
701
702 pub fn translate_named(self) -> RegChunk {
703 self.translate()
704 }
705}
706
707fn bin_kind(op: Opcode) -> BinKind {
708 match op {
709 Opcode::Add | Opcode::AddImm => BinKind::Add,
710 Opcode::Sub | Opcode::SubImm => BinKind::Sub,
711 Opcode::Mul | Opcode::MulImm => BinKind::Mul,
712 Opcode::Div => BinKind::Div,
713 Opcode::Mod => BinKind::Mod,
714 Opcode::Pow => BinKind::Pow,
715 Opcode::BitAnd => BinKind::BitAnd,
716 Opcode::BitOr => BinKind::BitOr,
717 Opcode::BitXor => BinKind::BitXor,
718 Opcode::Shl => BinKind::Shl,
719 Opcode::Shr => BinKind::Shr,
720 Opcode::And => BinKind::And,
721 Opcode::Or => BinKind::Or,
722 _ => BinKind::Add,
723 }
724}
725
726fn bin_kind_imm(op: Opcode) -> BinKind {
727 match op {
728 Opcode::SubImm => BinKind::Sub,
729 Opcode::MulImm => BinKind::Mul,
730 _ => BinKind::Add,
731 }
732}
733
734fn cmp_kind(op: Opcode) -> CmpKind {
735 match op {
736 Opcode::Eq | Opcode::EqImm => CmpKind::Eq,
737 Opcode::Ne | Opcode::NeImm => CmpKind::Ne,
738 Opcode::Lt | Opcode::LtImm => CmpKind::Lt,
739 Opcode::Le | Opcode::LeImm => CmpKind::Le,
740 Opcode::Gt | Opcode::GtImm => CmpKind::Gt,
741 Opcode::Ge | Opcode::GeImm => CmpKind::Ge,
742 _ => CmpKind::Eq,
743 }
744}
745
746fn cmp_kind_imm(op: Opcode) -> CmpKind {
747 cmp_kind(op)
748}
749
750fn un_kind(op: Opcode) -> UnKind {
751 match op {
752 Opcode::Not => UnKind::Not,
753 Opcode::BitNot => UnKind::BitNot,
754 _ => UnKind::Neg,
755 }
756}
757
758fn is_unwrap(op: Opcode) -> bool {
759 matches!(
760 op,
761 Opcode::UnwrapOk | Opcode::UnwrapErr | Opcode::UnwrapSome
762 )
763}
764
765fn stack_out(op: Opcode, h: u16, operand: Option<u32>) -> u16 {
767 match op {
768 Opcode::Push
769 | Opcode::Dup
770 | Opcode::LoadLocal
771 | Opcode::LoadGlobal
772 | Opcode::LoadUpvalue
773 | Opcode::Argc
774 | Opcode::CreateClass
775 | Opcode::CreateInstance
776 | Opcode::GetMethod
777 | Opcode::MatchPattern => h.saturating_add(1),
778 Opcode::Pop | Opcode::StoreGlobal | Opcode::StoreUpvalue | Opcode::Throw => {
779 h.saturating_sub(1)
780 }
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}