1mod call;
41mod mem;
42mod ops;
43mod term;
44
45use alloc::collections::BTreeMap;
46use alloc::string::String;
47use alloc::vec;
48use alloc::vec::Vec;
49use core::fmt;
50
51use ir_lang::{
52 Block as IBlock, CallConv, FuncId, InstData, Module, PointerWidth, StackSlot as IStackSlot,
53 Terminator, Type, Value,
54};
55
56use crate::compile::Options;
57use crate::mir::{self, Block, Effects, Function, Inst, MirError, Operand, StackSlot, Symbol};
58use crate::regs::{PReg, Reg};
59use crate::target::abi::Abi;
60use crate::x86_64::{self, GPR, op};
61
62#[derive(Clone, Debug, PartialEq, Eq)]
80#[non_exhaustive]
81pub enum IselError {
82 Invalid(ir_lang::ValidationError),
84 NoBody(FuncId),
86 Unsupported {
88 func: FuncId,
90 what: &'static str,
92 },
93 Mir(MirError),
96}
97
98impl fmt::Display for IselError {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 match self {
101 IselError::Invalid(e) => write!(f, "invalid module: {e}"),
102 IselError::NoBody(func) => write!(f, "{func} has no body to select"),
103 IselError::Unsupported { func, what } => write!(f, "{func}: {what}"),
104 IselError::Mir(e) => write!(f, "building machine IR: {e}"),
105 }
106 }
107}
108
109impl core::error::Error for IselError {
110 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
111 match self {
112 IselError::Invalid(e) => Some(e),
113 IselError::Mir(e) => Some(e),
114 _ => None,
115 }
116 }
117}
118
119impl From<MirError> for IselError {
120 fn from(e: MirError) -> IselError {
121 IselError::Mir(e)
122 }
123}
124
125type Result<T> = core::result::Result<T, IselError>;
126
127pub fn select(module: &Module, func: FuncId, options: &Options) -> Result<Function> {
155 module.validate().map_err(IselError::Invalid)?;
156 select_validated(module, func, options)
157}
158
159pub(crate) fn select_validated(
161 module: &Module,
162 func: FuncId,
163 options: &Options,
164) -> Result<Function> {
165 let decl = module.func_decl(func).ok_or(IselError::NoBody(func))?;
166 let body = module.function(func).ok_or(IselError::NoBody(func))?;
167 let sig = module.func_signature(func).ok_or(IselError::NoBody(func))?;
168 if module.pointer_width() != PointerWidth::P64 {
169 return Err(IselError::Unsupported {
170 func,
171 what: "32-bit pointers need a 32-bit target",
172 });
173 }
174 let abi = abi_for(func, sig.conv, options)?;
175 let mut sel = Sel::new(module, func, body, decl.name(), abi, options);
176 sel.run(sig)?;
177 Ok(sel.f)
178}
179
180pub(crate) fn abi_for(func: FuncId, conv: CallConv, options: &Options) -> Result<&'static Abi> {
182 match conv {
183 CallConv::Fast | CallConv::C => Ok(options.abi),
184 CallConv::SysV64 => Ok(&x86_64::SYSV),
185 CallConv::Win64 => Ok(&x86_64::WIN64),
186 _ => Err(IselError::Unsupported {
187 func,
188 what: "the aapcs64 convention needs the AArch64 target",
189 }),
190 }
191}
192
193#[derive(Clone, Copy, Debug, PartialEq, Eq)]
195pub(crate) enum Ext {
196 Type,
198 Zero,
200 Sign,
202}
203
204#[derive(Clone, Copy, Debug)]
206pub(crate) enum Fail<'a> {
207 Edge(&'a ir_lang::BlockCall),
209 Trap,
211}
212
213#[derive(Clone, Copy, Debug)]
216pub(crate) enum Addr {
217 Slot(StackSlot, i64),
219 Reg(Reg, i64),
221}
222
223pub(crate) struct Sel<'m> {
225 pub(crate) m: &'m Module,
226 pub(crate) func: FuncId,
227 pub(crate) irf: &'m ir_lang::Function,
228 pub(crate) opts: &'m Options,
229 pub(crate) abi: &'static Abi,
230 pub(crate) f: Function,
231 vals: Vec<Option<Reg>>,
233 consts: Vec<Option<u64>>,
235 addrs: Vec<Option<Addr>>,
237 uses: Vec<u32>,
239 pub(crate) fused: Vec<bool>,
242 blocks: Vec<Option<Block>>,
243 slots: Vec<(StackSlot, u32)>,
246 syms: BTreeMap<String, Symbol>,
247 traps: BTreeMap<u32, Block>,
248 gc_slots: Vec<StackSlot>,
249 incoming: BTreeMap<u32, StackSlot>,
250 fence_slot: Option<StackSlot>,
251 pub(crate) cur: Block,
253}
254
255impl<'m> Sel<'m> {
256 fn new(
257 m: &'m Module,
258 func: FuncId,
259 irf: &'m ir_lang::Function,
260 name: &str,
261 abi: &'static Abi,
262 opts: &'m Options,
263 ) -> Sel<'m> {
264 let nv = irf.value_count();
265 let mut f = Function::with_capacity(name, abi, irf.inst_count() * 2, irf.inst_count() * 6);
266 if !opts.frame_pointer {
267 f.omit_frame_pointer();
268 }
269 Sel {
270 m,
271 func,
272 irf,
273 opts,
274 abi,
275 f,
276 vals: vec![None; nv],
277 consts: vec![None; nv],
278 addrs: vec![None; nv],
279 uses: vec![0; nv],
280 fused: vec![false; nv],
281 blocks: vec![None; irf.block_count()],
282 slots: Vec::new(),
283 syms: BTreeMap::new(),
284 traps: BTreeMap::new(),
285 gc_slots: Vec::new(),
286 incoming: BTreeMap::new(),
287 fence_slot: None,
288 cur: Block::new(0),
289 }
290 }
291
292 pub(crate) fn unsupported<T>(&self, what: &'static str) -> Result<T> {
293 Err(IselError::Unsupported {
294 func: self.func,
295 what,
296 })
297 }
298
299 fn run(&mut self, sig: &ir_lang::Signature) -> Result<()> {
300 let irf = self.irf;
301 let order = ir_rpo(irf);
304 self.count_uses(&order);
305 for &b in &order {
306 let mb = self.f.block()?;
307 self.blocks[b.index()] = Some(mb);
308 }
309 for &b in &order {
310 if b == irf.entry() {
311 continue;
312 }
313 let mb = self.block(b)?;
314 for &p in irf.block_params(b) {
315 let ty = self.ty(p)?;
316 let _ = ty;
317 let v = self.new_reg()?;
318 self.vals[p.index()] = Some(v);
319 self.f.add_param(mb, v.into())?;
320 }
321 }
322 for slot in 0..irf.slot_count() {
323 let data = irf
324 .slot(IStackSlot::from_u32(slot as u32))
325 .ok_or(MirError::Limit)?;
326 let (size, align, round) = if data.align > 16 {
330 (data.size.saturating_add(data.align - 16), 16, data.align)
331 } else {
332 (data.size, data.align.max(1), 0)
333 };
334 let s = self.f.stack_slot(mir::SlotKind::Local { size, align })?;
335 self.slots.push((s, round));
336 }
337 self.entry(sig)?;
338 for &b in &order {
339 self.cur = self.block(b)?;
340 let insts: Vec<ir_lang::Inst> = irf.insts(b).collect();
341 for inst in insts {
342 self.inst(inst)?;
343 }
344 let term = irf.terminator(b).ok_or(MirError::NoBlocks)?;
345 self.terminator(b, term)?;
346 }
347 Ok(())
348 }
349
350 fn entry(&mut self, sig: &ir_lang::Signature) -> Result<()> {
353 let irf = self.irf;
354 let entry = self.block(irf.entry())?;
355 self.cur = entry;
356 let classes = self.arg_classes(&sig.params)?;
357 let mut locs = Vec::new();
358 let _ = self.abi.assign_args(&classes, &mut locs);
359 let mut dsts = Vec::with_capacity(locs.len());
360 let mut srcs = Vec::with_capacity(locs.len());
361 for (k, (&p, loc)) in irf.block_params(irf.entry()).iter().zip(&locs).enumerate() {
362 let src: Operand = match *loc {
363 crate::target::abi::ArgLoc::Reg(r) => {
364 self.f.add_param(entry, r.into())?;
365 r.into()
366 }
367 crate::target::abi::ArgLoc::Stack(off) => {
368 let s = self.incoming_slot(off)?;
369 self.f.add_param(entry, Operand::slot(s))?;
370 Operand::slot(s)
371 }
372 };
373 let v = self.new_reg()?;
374 dsts.push(Operand::from(v));
375 srcs.push(src);
376 self.vals[p.index()] = Some(v);
377 let _ = k;
378 }
379 if !dsts.is_empty() {
380 dsts.extend(srcs);
381 let _ = self.f.push(entry, op::PCOPY, &dsts)?;
382 }
383 for &p in irf.block_params(irf.entry()) {
385 if irf.value_type(p) == Some(Type::Bool) {
386 let r = self.val(p)?;
387 let n = self.emit_def(op::MOVZX8, &[r.into()])?;
388 self.vals[p.index()] = Some(n);
389 }
390 }
391 Ok(())
392 }
393
394 pub(crate) fn incoming_slot(&mut self, offset: u32) -> Result<StackSlot> {
397 if let Some(&s) = self.incoming.get(&offset) {
398 return Ok(s);
399 }
400 let s = self
401 .f
402 .stack_slot(mir::SlotKind::Incoming { offset, size: 8 })?;
403 let _ = self.incoming.insert(offset, s);
404 Ok(s)
405 }
406
407 pub(crate) fn gc_slot(&mut self, i: usize) -> Result<StackSlot> {
410 while self.gc_slots.len() <= i {
411 let s = self
412 .f
413 .stack_slot(mir::SlotKind::Local { size: 8, align: 8 })?;
414 self.gc_slots.push(s);
415 }
416 self.gc_slots
417 .get(i)
418 .copied()
419 .ok_or(IselError::Mir(MirError::Limit))
420 }
421
422 pub(crate) fn fence_slot(&mut self) -> Result<StackSlot> {
424 if let Some(s) = self.fence_slot {
425 return Ok(s);
426 }
427 let s = self
428 .f
429 .stack_slot(mir::SlotKind::Local { size: 8, align: 8 })?;
430 self.fence_slot = Some(s);
431 Ok(s)
432 }
433
434 fn count_uses(&mut self, order: &[IBlock]) {
435 let irf = self.irf;
436 for &b in order {
437 for inst in irf.insts(b) {
438 if let Some(data) = irf.inst(inst) {
439 data.for_each_operand(|v| {
440 let v = irf.resolve(v);
441 if let Some(n) = self.uses.get_mut(v.index()) {
442 *n += 1;
443 }
444 });
445 }
446 }
447 if let Some(t) = irf.terminator(b) {
448 t.for_each_operand(|v| {
449 let v = irf.resolve(v);
450 if let Some(n) = self.uses.get_mut(v.index()) {
451 *n += 1;
452 }
453 });
454 }
455 }
456 let mut stamp: Vec<u32> = vec![0; self.fused.len()];
461 for (k, &b) in order.iter().enumerate() {
462 let here = k as u32 + 1;
463 let candidate = |stamp: &[u32], c: Value| stamp.get(c.index()) == Some(&here);
464 for inst in irf.insts(b) {
465 if let Some(InstData::Compare { args, .. }) = irf.inst(inst) {
466 let r = irf.results(inst);
467 if let Some(v) = r.first() {
468 let fusible_ty = irf
469 .value_type(irf.resolve(args[0]))
470 .is_some_and(|t| !t.is_float());
471 if self.uses.get(v.index()) == Some(&1) && fusible_ty {
472 if let Some(s) = stamp.get_mut(v.index()) {
473 *s = here;
474 }
475 }
476 }
477 }
478 if let Some(InstData::Select { cond, .. }) = irf.inst(inst) {
479 let c = irf.resolve(*cond);
480 if candidate(&stamp, c) {
481 self.fused[c.index()] = true;
482 }
483 }
484 }
485 if let Some(Terminator::Branch { cond, .. }) = irf.terminator(b) {
486 let c = irf.resolve(*cond);
487 if candidate(&stamp, c) {
488 self.fused[c.index()] = true;
489 }
490 }
491 }
492 }
493
494 pub(crate) fn block(&self, b: IBlock) -> Result<Block> {
496 self.blocks
497 .get(b.index())
498 .copied()
499 .flatten()
500 .ok_or(IselError::Mir(MirError::UnknownBlock(Block::new(
501 b.as_u32(),
502 ))))
503 }
504
505 pub(crate) fn new_reg(&mut self) -> Result<Reg> {
507 Ok(self.f.vreg(GPR)?)
508 }
509
510 pub(crate) fn ty(&self, v: Value) -> Result<Type> {
512 let t = self
513 .irf
514 .value_type(self.irf.resolve(v))
515 .ok_or(IselError::Mir(MirError::Limit))?;
516 if t.is_float() {
517 return self.unsupported("floating point waits on isa-lang's SSE forms (ISSUES P11)");
518 }
519 Ok(t)
520 }
521
522 pub(crate) fn val(&self, v: Value) -> Result<Reg> {
524 let v = self.irf.resolve(v);
525 self.vals
526 .get(v.index())
527 .copied()
528 .flatten()
529 .ok_or(IselError::Mir(MirError::Limit))
530 }
531
532 pub(crate) fn konst(&self, v: Value) -> Option<u64> {
534 let v = self.irf.resolve(v);
535 self.consts.get(v.index()).copied().flatten()
536 }
537
538 pub(crate) fn commute(&self, args: [Value; 2]) -> [Value; 2] {
544 let uses = |v: Value| {
545 let v = self.irf.resolve(v);
546 self.uses.get(v.index()).copied().unwrap_or(0)
547 };
548 let (k0, k1) = (self.konst(args[0]).is_some(), self.konst(args[1]).is_some());
549 if (k0 && !k1) || (!k1 && uses(args[0]) > 1 && uses(args[1]) == 1) {
550 [args[1], args[0]]
551 } else {
552 args
553 }
554 }
555
556 pub(crate) fn set_val(&mut self, v: Value, r: Reg) {
557 if let Some(slot) = self.vals.get_mut(v.index()) {
558 *slot = Some(r);
559 }
560 }
561
562 pub(crate) fn set_addr(&mut self, v: Value, a: Addr) {
563 if let Some(slot) = self.addrs.get_mut(v.index()) {
564 *slot = Some(a);
565 }
566 }
567
568 pub(crate) fn addr_of(&self, v: Value) -> Option<Addr> {
569 let v = self.irf.resolve(v);
570 self.addrs.get(v.index()).copied().flatten()
571 }
572
573 pub(crate) fn mir_slot(&self, s: IStackSlot) -> Result<(StackSlot, u32)> {
574 self.slots
575 .get(s.index())
576 .copied()
577 .ok_or(IselError::Mir(MirError::UnknownSlot(StackSlot::new(
578 s.as_u32(),
579 ))))
580 }
581
582 pub(crate) fn symbol(&mut self, name: &str) -> Result<Symbol> {
584 if let Some(&s) = self.syms.get(name) {
585 return Ok(s);
586 }
587 let s = self.f.symbol(name)?;
588 let _ = self.syms.insert(String::from(name), s);
589 Ok(s)
590 }
591
592 pub(crate) fn emit(&mut self, opcode: crate::target::Opcode, ops: &[Operand]) -> Result<Inst> {
594 Ok(self.f.push(self.cur, opcode, ops)?)
595 }
596
597 pub(crate) fn emit_with(
599 &mut self,
600 opcode: crate::target::Opcode,
601 ops: &[Operand],
602 effects: Effects<'_>,
603 ) -> Result<Inst> {
604 Ok(self.f.push_with(self.cur, opcode, ops, effects)?)
605 }
606
607 pub(crate) fn emit_def(
609 &mut self,
610 opcode: crate::target::Opcode,
611 ops: &[Operand],
612 ) -> Result<Reg> {
613 let d = self.new_reg()?;
614 let mut all = [Operand::Imm(0); 5];
615 all[0] = d.into();
616 let n = ops.len().min(4);
617 all[1..=n].copy_from_slice(&ops[..n]);
618 let _ = self.emit(opcode, &all[..=n])?;
619 Ok(d)
620 }
621
622 pub(crate) fn copy_to_new(&mut self, src: Operand) -> Result<Reg> {
624 let d = self.new_reg()?;
625 let _ = self.emit(op::COPY, &[d.into(), src])?;
626 Ok(d)
627 }
628
629 pub(crate) fn new_block(&mut self) -> Result<Block> {
631 Ok(self.f.block()?)
632 }
633
634 pub(crate) fn branch(
636 &mut self,
637 cc: isa_lang::x86_64::Cond,
638 then: Block,
639 other: Block,
640 ) -> Result<()> {
641 let t = self.f.block_call(then, &[])?;
642 let e = self.f.block_call(other, &[])?;
643 let _ = self.emit(op::JCC, &[x86_64::cond(cc).into(), t.into(), e.into()])?;
644 Ok(())
645 }
646
647 pub(crate) fn jump(&mut self, target: Block, args: &[Operand]) -> Result<()> {
649 let c = self.f.block_call(target, args)?;
650 let _ = self.emit(op::JMP, &[c.into()])?;
651 Ok(())
652 }
653
654 pub(crate) fn trap_block(&mut self, code: u32) -> Result<Block> {
657 if let Some(&b) = self.traps.get(&code) {
658 return Ok(b);
659 }
660 let saved = self.cur;
661 let b = self.new_block()?;
662 self.cur = b;
663 self.trap_call(code)?;
664 self.cur = saved;
665 let _ = self.traps.insert(code, b);
666 Ok(b)
667 }
668
669 pub(crate) fn trap_call(&mut self, code: u32) -> Result<()> {
671 let c_abi = self.opts.abi;
672 let arg = first_int_arg(c_abi)?;
673 let sym = self.symbol(self.opts.trap_symbol)?;
674 let _ = self.emit(op::COPY, &[arg.into(), Operand::Imm(i64::from(code))])?;
675 let uses = [arg];
676 let _ = self.emit_with(
677 op::CALL,
678 &[sym.into()],
679 Effects::new().uses(&uses).clobbers(c_abi.call_clobbers()),
680 )?;
681 let _ = self.emit(op::UD2, &[])?;
682 Ok(())
683 }
684
685 pub(crate) fn fail_if(
688 &mut self,
689 cc: isa_lang::x86_64::Cond,
690 fail: Fail<'_>,
691 code: u32,
692 ) -> Result<()> {
693 let next = self.new_block()?;
694 match fail {
695 Fail::Trap => {
696 let t = self.trap_block(code)?;
697 self.branch(cc, t, next)?;
698 }
699 Fail::Edge(dest) => {
700 let call = self.edge(dest, &[], None, Some(Operand::Imm(i64::from(code))))?;
701 let e = self.f.block_call(next, &[])?;
702 let _ = self.emit(op::JCC, &[x86_64::cond(cc).into(), call.into(), e.into()])?;
703 }
704 }
705 self.cur = next;
706 Ok(())
707 }
708
709 pub(crate) fn edge(
713 &mut self,
714 dest: &ir_lang::BlockCall,
715 results: &[Operand],
716 exn: Option<Operand>,
717 err: Option<Operand>,
718 ) -> Result<mir::BlockCall> {
719 let target = self.block(dest.block)?;
720 let mut args = Vec::with_capacity(dest.args.len());
721 for a in &dest.args {
722 let op = match *a {
723 ir_lang::BlockArg::Value(v) => self.operand(v)?,
724 ir_lang::BlockArg::Result(i) => results
725 .get(i as usize)
726 .copied()
727 .ok_or(IselError::Mir(MirError::Limit))?,
728 ir_lang::BlockArg::Exception => exn.ok_or(IselError::Mir(MirError::Limit))?,
729 ir_lang::BlockArg::ErrorCode => err.ok_or(IselError::Mir(MirError::Limit))?,
730 _ => return self.unsupported("an edge argument kind this release does not know"),
731 };
732 args.push(op);
733 }
734 Ok(self.f.block_call(target, &args)?)
735 }
736
737 pub(crate) fn operand(&self, v: Value) -> Result<Operand> {
740 match self.konst(v) {
741 Some(bits) => Ok(Operand::Imm(bits as i64)),
742 None => Ok(self.val(v)?.into()),
743 }
744 }
745
746 fn inst(&mut self, inst: ir_lang::Inst) -> Result<()> {
747 let irf = self.irf;
748 let data = irf.inst(inst).ok_or(MirError::Limit)?.clone();
749 let results = irf.results(inst);
750 match data {
751 InstData::Const { ty, bits } => {
752 if ty.is_float() {
753 return self
754 .unsupported("floating point waits on isa-lang's SSE forms (ISSUES P11)");
755 }
756 let r = results.first().ok_or(MirError::Limit)?;
757 let d = self.emit_def(op::MOV64RI, &[Operand::Imm(bits as i64)])?;
758 self.set_val(r, d);
759 if let Some(c) = self.consts.get_mut(r.index()) {
760 *c = Some(bits);
761 }
762 Ok(())
763 }
764 InstData::Unary { op, policy, arg } => {
765 let r = results.first().ok_or(MirError::Limit)?;
766 let d = self.unary(op, policy, arg, Fail::Trap)?;
767 self.set_val(r, d);
768 Ok(())
769 }
770 InstData::Binary { op, policy, args } => {
771 let r = results.first().ok_or(MirError::Limit)?;
772 let d = self.binary(op, policy, args, Fail::Trap)?;
773 self.set_val(r, d);
774 Ok(())
775 }
776 InstData::Compare { op, args } => {
777 let r = results.first().ok_or(MirError::Limit)?;
778 if self.fused.get(r.index()).copied().unwrap_or(false) {
779 return Ok(());
780 }
781 let cc = self.compare(op, args)?;
782 let s = self.emit_def(op::SETCC, &[x86_64::cond(cc).into()])?;
783 let d = self.emit_def(op::MOVZX8, &[s.into()])?;
784 self.set_val(r, d);
785 Ok(())
786 }
787 InstData::Convert {
788 op,
789 policy,
790 to,
791 arg,
792 } => {
793 let r = results.first().ok_or(MirError::Limit)?;
794 let d = self.convert(op, policy, to, arg, Fail::Trap)?;
795 self.set_val(r, d);
796 Ok(())
797 }
798 InstData::Select { cond, args } => {
799 let r = results.first().ok_or(MirError::Limit)?;
800 let _ = self.ty(r)?;
801 let cc = self.condition(cond)?;
802 let (a, b) = (self.val(args[0])?, self.val(args[1])?);
803 let d =
805 self.emit_def(op::CMOV64, &[b.into(), a.into(), x86_64::cond(cc).into()])?;
806 self.set_val(r, d);
807 Ok(())
808 }
809 InstData::Fma { .. } => self.unsupported(
810 "fma is floating point, which waits on isa-lang's SSE forms (ISSUES P11)",
811 ),
812 InstData::Call(call) => self.call(&call, results),
813 InstData::Safepoint { gc } => self.safepoint(&gc, results),
814 other => self.memory(&other, results),
815 }
816 }
817
818 pub(crate) fn condition(&mut self, cond: Value) -> Result<isa_lang::x86_64::Cond> {
821 let c = self.irf.resolve(cond);
822 if self.fused.get(c.index()).copied().unwrap_or(false) {
823 if let Some(inst) = self.irf.defining_inst(c) {
824 if let Some(InstData::Compare { op, args }) = self.irf.inst(inst) {
825 let (op, args) = (*op, *args);
826 return self.compare(op, args);
827 }
828 }
829 }
830 let r = self.val(c)?;
831 let _ = self.emit(op::TEST32, &[r.into(), r.into()])?;
832 Ok(isa_lang::x86_64::Cond::Ne)
833 }
834}
835
836pub(crate) fn first_int_arg(abi: &Abi) -> Result<PReg> {
838 abi.args()
839 .int()
840 .first()
841 .copied()
842 .ok_or(IselError::Mir(MirError::Limit))
843}
844
845fn ir_rpo(f: &ir_lang::Function) -> Vec<IBlock> {
847 let n = f.block_count();
848 let mut seen = vec![false; n];
849 let mut post = Vec::new();
850 let entry = f.entry();
851 if entry.index() >= n {
852 return post;
853 }
854 let mut stack: Vec<(IBlock, Vec<IBlock>, usize)> = Vec::new();
855 let succs = |b: IBlock| -> Vec<IBlock> {
856 f.terminator(b)
857 .map(|t| t.successors().map(|c| c.block).collect())
858 .unwrap_or_default()
859 };
860 seen[entry.index()] = true;
861 stack.push((entry, succs(entry), 0));
862 while let Some(top) = stack.last_mut() {
863 if let Some(&s) = top.1.get(top.2) {
864 top.2 += 1;
865 if s.index() < n && !seen[s.index()] {
866 seen[s.index()] = true;
867 let next = succs(s);
868 stack.push((s, next, 0));
869 }
870 } else {
871 post.push(top.0);
872 let _ = stack.pop();
873 }
874 }
875 post.reverse();
876 post
877}