1mod call;
67mod mem;
68mod ops;
69mod term;
70
71use alloc::collections::BTreeMap;
72use alloc::string::String;
73use alloc::vec;
74use alloc::vec::Vec;
75use core::fmt;
76
77use ir_lang::{
78 Block as IBlock, CallConv, FuncId, InstData, Module, PointerWidth, StackSlot as IStackSlot,
79 Terminator, Type, Value,
80};
81
82use crate::compile::Options;
83use crate::mir::{self, Block, Effects, Function, Inst, MirError, Operand, StackSlot, Symbol};
84use crate::regs::{PReg, Reg};
85use crate::target::abi::Abi;
86use crate::x86_64::{self, GPR, op};
87
88#[derive(Clone, Debug, PartialEq, Eq)]
106#[non_exhaustive]
107pub enum IselError {
108 Invalid(ir_lang::ValidationError),
110 NoBody(FuncId),
112 Unsupported {
114 func: FuncId,
116 what: &'static str,
118 },
119 Mir(MirError),
122}
123
124impl fmt::Display for IselError {
125 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126 match self {
127 IselError::Invalid(e) => write!(f, "invalid module: {e}"),
128 IselError::NoBody(func) => write!(f, "{func} has no body to select"),
129 IselError::Unsupported { func, what } => write!(f, "{func}: {what}"),
130 IselError::Mir(e) => write!(f, "building machine IR: {e}"),
131 }
132 }
133}
134
135impl core::error::Error for IselError {
136 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
137 match self {
138 IselError::Invalid(e) => Some(e),
139 IselError::Mir(e) => Some(e),
140 _ => None,
141 }
142 }
143}
144
145impl From<MirError> for IselError {
146 fn from(e: MirError) -> IselError {
147 IselError::Mir(e)
148 }
149}
150
151type Result<T> = core::result::Result<T, IselError>;
152
153pub fn select(module: &Module, func: FuncId, options: &Options) -> Result<Function> {
181 module.validate().map_err(IselError::Invalid)?;
182 select_validated(module, func, options)
183}
184
185pub(crate) fn select_validated(
187 module: &Module,
188 func: FuncId,
189 options: &Options,
190) -> Result<Function> {
191 let decl = module.func_decl(func).ok_or(IselError::NoBody(func))?;
192 let body = module.function(func).ok_or(IselError::NoBody(func))?;
193 let sig = module.func_signature(func).ok_or(IselError::NoBody(func))?;
194 if module.pointer_width() != PointerWidth::P64 {
195 return Err(IselError::Unsupported {
196 func,
197 what: "32-bit pointers need a 32-bit target",
198 });
199 }
200 let abi = abi_for(func, sig.conv, options)?;
201 let mut sel = Sel::new(module, func, body, decl.name(), abi, options);
202 sel.run(sig)?;
203 Ok(sel.f)
204}
205
206pub(crate) fn abi_for(func: FuncId, conv: CallConv, options: &Options) -> Result<&'static Abi> {
208 match conv {
209 CallConv::Fast | CallConv::C => Ok(options.abi),
210 CallConv::SysV64 => Ok(&x86_64::SYSV),
211 CallConv::Win64 => Ok(&x86_64::WIN64),
212 _ => Err(IselError::Unsupported {
213 func,
214 what: "the aapcs64 convention needs the AArch64 target",
215 }),
216 }
217}
218
219#[derive(Clone, Copy, Debug, PartialEq, Eq)]
221pub(crate) enum Ext {
222 Type,
224 Zero,
226 Sign,
228}
229
230#[derive(Clone, Copy, Debug)]
232pub(crate) enum Fail<'a> {
233 Edge(&'a ir_lang::BlockCall),
235 Trap,
237}
238
239#[derive(Clone, Copy, Debug)]
242pub(crate) enum Addr {
243 Slot(StackSlot, i64),
245 Reg(Reg, i64),
247}
248
249pub(crate) struct Sel<'m> {
251 pub(crate) m: &'m Module,
252 pub(crate) func: FuncId,
253 pub(crate) irf: &'m ir_lang::Function,
254 pub(crate) opts: &'m Options,
255 pub(crate) abi: &'static Abi,
256 pub(crate) f: Function,
257 vals: Vec<Option<Reg>>,
259 consts: Vec<Option<u64>>,
261 addrs: Vec<Option<Addr>>,
263 uses: Vec<u32>,
265 pub(crate) fused: Vec<bool>,
268 blocks: Vec<Option<Block>>,
269 slots: Vec<(StackSlot, u32)>,
272 syms: BTreeMap<String, Symbol>,
273 traps: BTreeMap<u32, Block>,
274 gc_slots: Vec<StackSlot>,
275 incoming: BTreeMap<u32, StackSlot>,
276 fence_slot: Option<StackSlot>,
277 pub(crate) cur: Block,
279}
280
281impl<'m> Sel<'m> {
282 fn new(
283 m: &'m Module,
284 func: FuncId,
285 irf: &'m ir_lang::Function,
286 name: &str,
287 abi: &'static Abi,
288 opts: &'m Options,
289 ) -> Sel<'m> {
290 let nv = irf.value_count();
291 let mut f = Function::with_capacity(name, abi, irf.inst_count() * 2, irf.inst_count() * 6);
292 if !opts.frame_pointer {
293 f.omit_frame_pointer();
294 }
295 Sel {
296 m,
297 func,
298 irf,
299 opts,
300 abi,
301 f,
302 vals: vec![None; nv],
303 consts: vec![None; nv],
304 addrs: vec![None; nv],
305 uses: vec![0; nv],
306 fused: vec![false; nv],
307 blocks: vec![None; irf.block_count()],
308 slots: Vec::new(),
309 syms: BTreeMap::new(),
310 traps: BTreeMap::new(),
311 gc_slots: Vec::new(),
312 incoming: BTreeMap::new(),
313 fence_slot: None,
314 cur: Block::new(0),
315 }
316 }
317
318 pub(crate) fn unsupported<T>(&self, what: &'static str) -> Result<T> {
319 Err(IselError::Unsupported {
320 func: self.func,
321 what,
322 })
323 }
324
325 fn run(&mut self, sig: &ir_lang::Signature) -> Result<()> {
326 let irf = self.irf;
327 let order = ir_rpo(irf);
330 self.count_uses(&order);
331 for &b in &order {
332 let mb = self.f.block()?;
333 self.blocks[b.index()] = Some(mb);
334 }
335 for &b in &order {
336 if b == irf.entry() {
337 continue;
338 }
339 let mb = self.block(b)?;
340 for &p in irf.block_params(b) {
341 let _ = self.ty_any(p)?;
343 let v = self.new_reg()?;
344 self.vals[p.index()] = Some(v);
345 self.f.add_param(mb, v.into())?;
346 }
347 }
348 for slot in 0..irf.slot_count() {
349 let data = irf
350 .slot(IStackSlot::from_u32(slot as u32))
351 .ok_or(MirError::Limit)?;
352 let (size, align, round) = if data.align > 16 {
356 (data.size.saturating_add(data.align - 16), 16, data.align)
357 } else {
358 (data.size, data.align.max(1), 0)
359 };
360 let s = self.f.stack_slot(mir::SlotKind::Local { size, align })?;
361 self.slots.push((s, round));
362 }
363 self.entry(sig)?;
364 for &b in &order {
365 self.cur = self.block(b)?;
366 let insts: Vec<ir_lang::Inst> = irf.insts(b).collect();
367 for inst in insts {
368 self.inst(inst)?;
369 }
370 let term = irf.terminator(b).ok_or(MirError::NoBlocks)?;
371 self.terminator(b, term)?;
372 }
373 Ok(())
374 }
375
376 fn entry(&mut self, sig: &ir_lang::Signature) -> Result<()> {
379 let irf = self.irf;
380 let entry = self.block(irf.entry())?;
381 self.cur = entry;
382 let classes = self.arg_classes(&sig.params)?;
383 let mut locs = Vec::new();
384 let _ = self.abi.assign_args(&classes, &mut locs);
385 let mut dsts = Vec::with_capacity(locs.len());
386 let mut srcs = Vec::with_capacity(locs.len());
387 for (k, (&p, loc)) in irf.block_params(irf.entry()).iter().zip(&locs).enumerate() {
388 let src: Operand = match *loc {
389 crate::target::abi::ArgLoc::Reg(r) => {
390 self.f.add_param(entry, r.into())?;
391 r.into()
392 }
393 crate::target::abi::ArgLoc::Stack(off) => {
394 let s = self.incoming_slot(off)?;
395 self.f.add_param(entry, Operand::slot(s))?;
396 Operand::slot(s)
397 }
398 };
399 let v = self.new_reg()?;
400 dsts.push(Operand::from(v));
401 srcs.push(src);
402 self.vals[p.index()] = Some(v);
403 let _ = k;
404 }
405 if !dsts.is_empty() {
406 dsts.extend(srcs);
407 let _ = self.f.push(entry, op::PCOPY, &dsts)?;
408 }
409 for &p in irf.block_params(irf.entry()) {
411 if irf.value_type(p) == Some(Type::Bool) {
412 let r = self.val(p)?;
413 let n = self.emit_def(op::MOVZX8, &[r.into()])?;
414 self.vals[p.index()] = Some(n);
415 }
416 }
417 Ok(())
418 }
419
420 pub(crate) fn incoming_slot(&mut self, offset: u32) -> Result<StackSlot> {
423 if let Some(&s) = self.incoming.get(&offset) {
424 return Ok(s);
425 }
426 let s = self
427 .f
428 .stack_slot(mir::SlotKind::Incoming { offset, size: 8 })?;
429 let _ = self.incoming.insert(offset, s);
430 Ok(s)
431 }
432
433 pub(crate) fn gc_slot(&mut self, i: usize) -> Result<StackSlot> {
436 while self.gc_slots.len() <= i {
437 let s = self
438 .f
439 .stack_slot(mir::SlotKind::Local { size: 8, align: 8 })?;
440 self.gc_slots.push(s);
441 }
442 self.gc_slots
443 .get(i)
444 .copied()
445 .ok_or(IselError::Mir(MirError::Limit))
446 }
447
448 pub(crate) fn fence_slot(&mut self) -> Result<StackSlot> {
450 if let Some(s) = self.fence_slot {
451 return Ok(s);
452 }
453 let s = self
454 .f
455 .stack_slot(mir::SlotKind::Local { size: 8, align: 8 })?;
456 self.fence_slot = Some(s);
457 Ok(s)
458 }
459
460 fn count_uses(&mut self, order: &[IBlock]) {
461 let irf = self.irf;
462 for &b in order {
463 for inst in irf.insts(b) {
464 if let Some(data) = irf.inst(inst) {
465 data.for_each_operand(|v| {
466 let v = irf.resolve(v);
467 if let Some(n) = self.uses.get_mut(v.index()) {
468 *n += 1;
469 }
470 });
471 }
472 }
473 if let Some(t) = irf.terminator(b) {
474 t.for_each_operand(|v| {
475 let v = irf.resolve(v);
476 if let Some(n) = self.uses.get_mut(v.index()) {
477 *n += 1;
478 }
479 });
480 }
481 }
482 let mut stamp: Vec<u32> = vec![0; self.fused.len()];
487 for (k, &b) in order.iter().enumerate() {
488 let here = k as u32 + 1;
489 let candidate = |stamp: &[u32], c: Value| stamp.get(c.index()) == Some(&here);
490 for inst in irf.insts(b) {
491 if let Some(InstData::Compare { args, .. }) = irf.inst(inst) {
492 let r = irf.results(inst);
493 if let Some(v) = r.first() {
494 let fusible_ty = irf
495 .value_type(irf.resolve(args[0]))
496 .is_some_and(|t| !t.is_float());
497 if self.uses.get(v.index()) == Some(&1) && fusible_ty {
498 if let Some(s) = stamp.get_mut(v.index()) {
499 *s = here;
500 }
501 }
502 }
503 }
504 if let Some(InstData::Select { cond, .. }) = irf.inst(inst) {
505 let c = irf.resolve(*cond);
506 if candidate(&stamp, c) {
507 self.fused[c.index()] = true;
508 }
509 }
510 }
511 if let Some(Terminator::Branch { cond, .. }) = irf.terminator(b) {
512 let c = irf.resolve(*cond);
513 if candidate(&stamp, c) {
514 self.fused[c.index()] = true;
515 }
516 }
517 }
518 }
519
520 pub(crate) fn block(&self, b: IBlock) -> Result<Block> {
522 self.blocks
523 .get(b.index())
524 .copied()
525 .flatten()
526 .ok_or(IselError::Mir(MirError::UnknownBlock(Block::new(
527 b.as_u32(),
528 ))))
529 }
530
531 pub(crate) fn new_reg(&mut self) -> Result<Reg> {
533 Ok(self.f.vreg(GPR)?)
534 }
535
536 pub(crate) fn ty(&self, v: Value) -> Result<Type> {
539 let t = self.ty_any(v)?;
540 if t.is_float() {
541 return self.unsupported(FLOAT_ARITH);
542 }
543 Ok(t)
544 }
545
546 pub(crate) fn ty_any(&self, v: Value) -> Result<Type> {
550 self.irf
551 .value_type(self.irf.resolve(v))
552 .ok_or(IselError::Mir(MirError::Limit))
553 }
554
555 pub(crate) fn val(&self, v: Value) -> Result<Reg> {
557 let v = self.irf.resolve(v);
558 self.vals
559 .get(v.index())
560 .copied()
561 .flatten()
562 .ok_or(IselError::Mir(MirError::Limit))
563 }
564
565 pub(crate) fn konst(&self, v: Value) -> Option<u64> {
567 let v = self.irf.resolve(v);
568 self.consts.get(v.index()).copied().flatten()
569 }
570
571 pub(crate) fn commute(&self, args: [Value; 2]) -> [Value; 2] {
577 let uses = |v: Value| {
578 let v = self.irf.resolve(v);
579 self.uses.get(v.index()).copied().unwrap_or(0)
580 };
581 let (k0, k1) = (self.konst(args[0]).is_some(), self.konst(args[1]).is_some());
582 if (k0 && !k1) || (!k1 && uses(args[0]) > 1 && uses(args[1]) == 1) {
583 [args[1], args[0]]
584 } else {
585 args
586 }
587 }
588
589 pub(crate) fn set_val(&mut self, v: Value, r: Reg) {
590 if let Some(slot) = self.vals.get_mut(v.index()) {
591 *slot = Some(r);
592 }
593 }
594
595 pub(crate) fn set_addr(&mut self, v: Value, a: Addr) {
596 if let Some(slot) = self.addrs.get_mut(v.index()) {
597 *slot = Some(a);
598 }
599 }
600
601 pub(crate) fn addr_of(&self, v: Value) -> Option<Addr> {
602 let v = self.irf.resolve(v);
603 self.addrs.get(v.index()).copied().flatten()
604 }
605
606 pub(crate) fn mir_slot(&self, s: IStackSlot) -> Result<(StackSlot, u32)> {
607 self.slots
608 .get(s.index())
609 .copied()
610 .ok_or(IselError::Mir(MirError::UnknownSlot(StackSlot::new(
611 s.as_u32(),
612 ))))
613 }
614
615 pub(crate) fn symbol(&mut self, name: &str) -> Result<Symbol> {
617 if let Some(&s) = self.syms.get(name) {
618 return Ok(s);
619 }
620 let s = self.f.symbol(name)?;
621 let _ = self.syms.insert(String::from(name), s);
622 Ok(s)
623 }
624
625 pub(crate) fn emit(&mut self, opcode: crate::target::Opcode, ops: &[Operand]) -> Result<Inst> {
627 Ok(self.f.push(self.cur, opcode, ops)?)
628 }
629
630 pub(crate) fn emit_with(
632 &mut self,
633 opcode: crate::target::Opcode,
634 ops: &[Operand],
635 effects: Effects<'_>,
636 ) -> Result<Inst> {
637 Ok(self.f.push_with(self.cur, opcode, ops, effects)?)
638 }
639
640 pub(crate) fn emit_def(
642 &mut self,
643 opcode: crate::target::Opcode,
644 ops: &[Operand],
645 ) -> Result<Reg> {
646 let d = self.new_reg()?;
647 let mut all = [Operand::Imm(0); 5];
648 all[0] = d.into();
649 let n = ops.len().min(4);
650 all[1..=n].copy_from_slice(&ops[..n]);
651 let _ = self.emit(opcode, &all[..=n])?;
652 Ok(d)
653 }
654
655 pub(crate) fn copy_to_new(&mut self, src: Operand) -> Result<Reg> {
657 let d = self.new_reg()?;
658 let _ = self.emit(op::COPY, &[d.into(), src])?;
659 Ok(d)
660 }
661
662 pub(crate) fn new_block(&mut self) -> Result<Block> {
664 Ok(self.f.block()?)
665 }
666
667 pub(crate) fn branch(
669 &mut self,
670 cc: isa_lang::x86_64::Cond,
671 then: Block,
672 other: Block,
673 ) -> Result<()> {
674 let t = self.f.block_call(then, &[])?;
675 let e = self.f.block_call(other, &[])?;
676 let _ = self.emit(op::JCC, &[x86_64::cond(cc).into(), t.into(), e.into()])?;
677 Ok(())
678 }
679
680 pub(crate) fn jump(&mut self, target: Block, args: &[Operand]) -> Result<()> {
682 let c = self.f.block_call(target, args)?;
683 let _ = self.emit(op::JMP, &[c.into()])?;
684 Ok(())
685 }
686
687 pub(crate) fn trap_block(&mut self, code: u32) -> Result<Block> {
690 if let Some(&b) = self.traps.get(&code) {
691 return Ok(b);
692 }
693 let saved = self.cur;
694 let b = self.new_block()?;
695 self.cur = b;
696 self.trap_call(code)?;
697 self.cur = saved;
698 let _ = self.traps.insert(code, b);
699 Ok(b)
700 }
701
702 pub(crate) fn trap_call(&mut self, code: u32) -> Result<()> {
704 let c_abi = self.opts.abi;
705 let arg = first_int_arg(c_abi)?;
706 let sym = self.symbol(self.opts.trap_symbol)?;
707 let _ = self.emit(op::COPY, &[arg.into(), Operand::Imm(i64::from(code))])?;
708 let uses = [arg];
709 let _ = self.emit_with(
710 op::CALL,
711 &[sym.into()],
712 Effects::new().uses(&uses).clobbers(c_abi.call_clobbers()),
713 )?;
714 let _ = self.emit(op::UD2, &[])?;
715 Ok(())
716 }
717
718 pub(crate) fn fail_if(
721 &mut self,
722 cc: isa_lang::x86_64::Cond,
723 fail: Fail<'_>,
724 code: u32,
725 ) -> Result<()> {
726 let next = self.new_block()?;
727 match fail {
728 Fail::Trap => {
729 let t = self.trap_block(code)?;
730 self.branch(cc, t, next)?;
731 }
732 Fail::Edge(dest) => {
733 let call = self.edge(dest, &[], None, Some(Operand::Imm(i64::from(code))))?;
734 let e = self.f.block_call(next, &[])?;
735 let _ = self.emit(op::JCC, &[x86_64::cond(cc).into(), call.into(), e.into()])?;
736 }
737 }
738 self.cur = next;
739 Ok(())
740 }
741
742 pub(crate) fn edge(
746 &mut self,
747 dest: &ir_lang::BlockCall,
748 results: &[Operand],
749 exn: Option<Operand>,
750 err: Option<Operand>,
751 ) -> Result<mir::BlockCall> {
752 let target = self.block(dest.block)?;
753 let mut args = Vec::with_capacity(dest.args.len());
754 for a in &dest.args {
755 let op = match *a {
756 ir_lang::BlockArg::Value(v) => self.operand(v)?,
757 ir_lang::BlockArg::Result(i) => results
758 .get(i as usize)
759 .copied()
760 .ok_or(IselError::Mir(MirError::Limit))?,
761 ir_lang::BlockArg::Exception => exn.ok_or(IselError::Mir(MirError::Limit))?,
762 ir_lang::BlockArg::ErrorCode => err.ok_or(IselError::Mir(MirError::Limit))?,
763 _ => return self.unsupported("an edge argument kind this release does not know"),
764 };
765 args.push(op);
766 }
767 Ok(self.f.block_call(target, &args)?)
768 }
769
770 pub(crate) fn operand(&self, v: Value) -> Result<Operand> {
773 match self.konst(v) {
774 Some(bits) => Ok(Operand::Imm(bits as i64)),
775 None => Ok(self.val(v)?.into()),
776 }
777 }
778
779 fn inst(&mut self, inst: ir_lang::Inst) -> Result<()> {
780 let irf = self.irf;
781 let data = irf.inst(inst).ok_or(MirError::Limit)?.clone();
782 let results = irf.results(inst);
783 match data {
784 InstData::Const { bits, .. } => {
785 let r = results.first().ok_or(MirError::Limit)?;
788 let d = self.emit_def(op::MOV64RI, &[Operand::Imm(bits as i64)])?;
789 self.set_val(r, d);
790 if let Some(c) = self.consts.get_mut(r.index()) {
791 *c = Some(bits);
792 }
793 Ok(())
794 }
795 InstData::Unary { op, policy, arg } => {
796 let r = results.first().ok_or(MirError::Limit)?;
797 let d = self.unary(op, policy, arg, Fail::Trap)?;
798 self.set_val(r, d);
799 Ok(())
800 }
801 InstData::Binary { op, policy, args } => {
802 let r = results.first().ok_or(MirError::Limit)?;
803 let d = self.binary(op, policy, args, Fail::Trap)?;
804 self.set_val(r, d);
805 Ok(())
806 }
807 InstData::Compare { op, args } => {
808 let r = results.first().ok_or(MirError::Limit)?;
809 if self.fused.get(r.index()).copied().unwrap_or(false) {
810 return Ok(());
811 }
812 let cc = self.compare(op, args)?;
813 let s = self.emit_def(op::SETCC, &[x86_64::cond(cc).into()])?;
814 let d = self.emit_def(op::MOVZX8, &[s.into()])?;
815 self.set_val(r, d);
816 Ok(())
817 }
818 InstData::Convert {
819 op,
820 policy,
821 to,
822 arg,
823 } => {
824 let r = results.first().ok_or(MirError::Limit)?;
825 let d = self.convert(op, policy, to, arg, Fail::Trap)?;
826 self.set_val(r, d);
827 Ok(())
828 }
829 InstData::Select { cond, args } => {
830 let r = results.first().ok_or(MirError::Limit)?;
831 let _ = self.ty_any(r)?;
832 let cc = self.condition(cond)?;
833 let (a, b) = (self.val(args[0])?, self.val(args[1])?);
834 let d =
836 self.emit_def(op::CMOV64, &[b.into(), a.into(), x86_64::cond(cc).into()])?;
837 self.set_val(r, d);
838 Ok(())
839 }
840 InstData::Fma { .. } => self.unsupported(FLOAT_ARITH),
841 InstData::Call(call) => self.call(&call, results),
842 InstData::Safepoint { gc } => self.safepoint(&gc, results),
843 other => self.memory(&other, results),
844 }
845 }
846
847 pub(crate) fn condition(&mut self, cond: Value) -> Result<isa_lang::x86_64::Cond> {
850 let c = self.irf.resolve(cond);
851 if self.fused.get(c.index()).copied().unwrap_or(false) {
852 if let Some(inst) = self.irf.defining_inst(c) {
853 if let Some(InstData::Compare { op, args }) = self.irf.inst(inst) {
854 let (op, args) = (*op, *args);
855 return self.compare(op, args);
856 }
857 }
858 }
859 let r = self.val(c)?;
860 let _ = self.emit(op::TEST32, &[r.into(), r.into()])?;
861 Ok(isa_lang::x86_64::Cond::Ne)
862 }
863}
864
865pub(crate) const FLOAT_ARITH: &str = "floating-point arithmetic waits on isa-lang's SSE forms (ISSUES P11); only pow, bitcast, constants, loads, stores, select, and edges are selected";
867
868pub(crate) fn first_int_arg(abi: &Abi) -> Result<PReg> {
870 abi.args()
871 .int()
872 .first()
873 .copied()
874 .ok_or(IselError::Mir(MirError::Limit))
875}
876
877fn ir_rpo(f: &ir_lang::Function) -> Vec<IBlock> {
879 let n = f.block_count();
880 let mut seen = vec![false; n];
881 let mut post = Vec::new();
882 let entry = f.entry();
883 if entry.index() >= n {
884 return post;
885 }
886 let mut stack: Vec<(IBlock, Vec<IBlock>, usize)> = Vec::new();
887 let succs = |b: IBlock| -> Vec<IBlock> {
888 f.terminator(b)
889 .map(|t| t.successors().map(|c| c.block).collect())
890 .unwrap_or_default()
891 };
892 seen[entry.index()] = true;
893 stack.push((entry, succs(entry), 0));
894 while let Some(top) = stack.last_mut() {
895 if let Some(&s) = top.1.get(top.2) {
896 top.2 += 1;
897 if s.index() < n && !seen[s.index()] {
898 seen[s.index()] = true;
899 let next = succs(s);
900 stack.push((s, next, 0));
901 }
902 } else {
903 post.push(top.0);
904 let _ = stack.pop();
905 }
906 }
907 post.reverse();
908 post
909}