1use alloc::collections::BTreeMap;
23use alloc::string::{String, ToString};
24use alloc::vec::Vec;
25use core::fmt;
26
27use isa_lang::x86_64::{
28 AsmError, Assembler, Cond as ICond, EncodeError, Inst as IInst, Label, Mem as IMem,
29 Mnemonic as M, Operand as IOp, Reg as IReg, Scale as IScale, Size,
30};
31
32use super::table::{DESCS, ENCODINGS, Enc, Fam, Src};
33use super::{GPR, R10, R11, REGS};
34use crate::mir::{
35 Block, BlockCall, Function, ImplicitKind, Inst, Mem, MirError, Operand, Scale, SlotKind,
36 StackSlot,
37};
38use crate::moves::{Loc, Move, MoveEnv, MoveError, MoveResolver, Source, Step};
39use crate::regs::{PReg, Reg, RegClass};
40use crate::target::frame::{FrameError, FrameInput, FrameLayout, FrameStep};
41use crate::target::{InstKind, OperandRule};
42
43#[derive(Clone, Debug, PartialEq, Eq)]
62#[non_exhaustive]
63pub enum EmitError {
64 Mir(MirError),
66 Frame(FrameError),
68 Moves(MoveError),
70 UnsupportedTarget,
74 VirtualRegister {
77 block: Block,
79 inst: Option<Inst>,
81 },
82 TiedMismatch {
85 inst: Inst,
87 },
88 ReservedWrite {
92 block: Block,
94 inst: Option<Inst>,
96 },
97 Unencodable {
101 inst: Option<Inst>,
103 },
104 Encode {
107 inst: Option<Inst>,
109 error: EncodeError,
111 },
112 Asm(AsmError),
114 UndefinedSymbol(String),
116 DuplicateSymbol(String),
118 ProbeScratch,
121}
122
123impl fmt::Display for EmitError {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 match self {
126 EmitError::Mir(e) => fmt::Display::fmt(e, f),
127 EmitError::Frame(e) => fmt::Display::fmt(e, f),
128 EmitError::Moves(e) => fmt::Display::fmt(e, f),
129 EmitError::UnsupportedTarget => {
130 f.write_str("the function's target is not the x86-64 description this emitter encodes")
131 }
132 EmitError::VirtualRegister { block, inst } => match inst {
133 Some(i) => write!(f, "{i} in {block} still names a virtual register; allocate registers first"),
134 None => write!(f, "a parameter of {block} is a virtual register; allocate registers first"),
135 },
136 EmitError::TiedMismatch { inst } => write!(
137 f,
138 "{inst}: a tied use must be the same register as its definition"
139 ),
140 EmitError::ReservedWrite { block, inst } => match inst {
141 Some(i) => write!(f, "{i} in {block} writes the stack or frame pointer"),
142 None => write!(f, "a parameter of {block} is the stack or frame pointer"),
143 },
144 EmitError::Unencodable { inst } => match inst {
145 Some(i) => write!(f, "{i}: isa-lang 0.2 has no encoding for this vector-register operation"),
146 None => f.write_str("a frame or edge move needs a vector-register instruction isa-lang 0.2 does not encode"),
147 },
148 EmitError::Encode { inst, error } => match inst {
149 Some(i) => write!(f, "{i}: {error}"),
150 None => write!(f, "frame or edge move: {error}"),
151 },
152 EmitError::Asm(e) => fmt::Display::fmt(e, f),
153 EmitError::UndefinedSymbol(name) => {
154 write!(f, "`{name}` is called but no function of that name was emitted")
155 }
156 EmitError::DuplicateSymbol(name) => {
157 write!(f, "two functions named `{name}` were emitted")
158 }
159 EmitError::ProbeScratch => f.write_str(
160 "the stack probe loop needs r10 or r11, and the entry block receives values in both",
161 ),
162 }
163 }
164}
165
166impl core::error::Error for EmitError {
167 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
168 match self {
169 EmitError::Mir(e) => Some(e),
170 EmitError::Frame(e) => Some(e),
171 EmitError::Moves(e) => Some(e),
172 EmitError::Encode { error, .. } => Some(error),
173 EmitError::Asm(e) => Some(e),
174 _ => None,
175 }
176 }
177}
178
179impl From<MirError> for EmitError {
180 fn from(e: MirError) -> EmitError {
181 EmitError::Mir(e)
182 }
183}
184
185impl From<FrameError> for EmitError {
186 fn from(e: FrameError) -> EmitError {
187 EmitError::Frame(e)
188 }
189}
190
191impl From<MoveError> for EmitError {
192 fn from(e: MoveError) -> EmitError {
193 EmitError::Moves(e)
194 }
195}
196
197#[derive(Clone, Debug, PartialEq, Eq)]
214pub struct FunctionInfo {
215 frame: FrameLayout,
216}
217
218impl FunctionInfo {
219 #[inline]
237 #[must_use]
238 pub fn frame(&self) -> &FrameLayout {
239 &self.frame
240 }
241}
242
243#[derive(Clone, Debug, PartialEq, Eq)]
263pub struct Program {
264 bytes: Vec<u8>,
265 symbols: Vec<(String, usize)>,
266}
267
268impl Program {
269 #[inline]
280 #[must_use]
281 pub fn bytes(&self) -> &[u8] {
282 &self.bytes
283 }
284
285 #[must_use]
296 pub fn into_bytes(self) -> Vec<u8> {
297 self.bytes
298 }
299
300 #[must_use]
311 pub fn symbol(&self, name: &str) -> Option<usize> {
312 self.symbols
313 .iter()
314 .find(|(n, _)| n == name)
315 .map(|&(_, off)| off)
316 }
317
318 pub fn symbols(&self) -> impl Iterator<Item = (&str, usize)> {
329 self.symbols.iter().map(|(n, o)| (n.as_str(), *o))
330 }
331}
332
333#[derive(Clone, Copy, Debug)]
334struct Sym {
335 label: Label,
336 defined: bool,
337}
338
339#[derive(Debug, Default)]
369pub struct Emitter {
370 asm: Assembler,
371 symbols: BTreeMap<String, Sym>,
372 resolver: MoveResolver,
373 moves: Vec<Move>,
374 steps: Vec<Step>,
375 sites: Vec<(u32, u32)>,
376 labels: Vec<Label>,
377}
378
379struct Ctx<'a> {
381 f: &'a Function,
382 frame: &'a FrameLayout,
383 site: usize,
384}
385
386impl Emitter {
387 #[must_use]
398 pub fn new() -> Emitter {
399 Emitter::default()
400 }
401
402 #[must_use]
412 pub fn with_capacity(bytes: usize) -> Emitter {
413 Emitter {
414 asm: Assembler::with_capacity(bytes),
415 ..Emitter::default()
416 }
417 }
418
419 pub fn function(&mut self, f: &Function) -> Result<FunctionInfo, EmitError> {
445 let target = f.target();
446 if !core::ptr::eq(target.insts(), &DESCS[..]) || !core::ptr::eq(target.regs(), ®S[..]) {
447 return Err(EmitError::UnsupportedTarget);
448 }
449 f.verify()?;
450 check_physical(f)?;
451
452 let base = f.slots().len();
455 let scratch = StackSlot::from_index(base);
456 let victim = StackSlot::from_index(base + 1);
457 let env = MoveEnv::new()
458 .with_scratch_slot(scratch)
459 .with_victim_slot(victim);
460 let (mut need_scratch, mut need_victim) = (false, false);
461 self.steps.clear();
462 self.sites.clear();
463 for block in f.blocks() {
464 for &inst in f.insts(block) {
465 let Some(opcode) = f.opcode(inst) else {
466 continue;
467 };
468 let ops = f.operands(inst);
469 let mut site =
470 |em: &mut Emitter, pairs: &mut dyn Iterator<Item = (Operand, Operand)>| {
471 em.moves.clear();
472 for (d, s) in pairs {
473 em.moves.push(to_move(f, d, s)?);
474 }
475 let start = em.steps.len();
476 let used = em
477 .resolver
478 .resolve(target, &em.moves, &env, &mut em.steps)?;
479 need_scratch |= used.scratch_slot;
480 need_victim |= used.victim_slot;
481 em.sites.push((start as u32, em.steps.len() as u32));
482 Ok::<(), EmitError>(())
483 };
484 match ENCODINGS.get(opcode.index()) {
485 Some(Enc::Copy) => {
486 site(
487 self,
488 &mut ops.first().copied().zip(ops.get(1).copied()).into_iter(),
489 )?;
490 }
491 Some(Enc::Pcopy) => {
492 let (d, s) = ops.split_at(ops.len() / 2);
493 site(self, &mut d.iter().copied().zip(s.iter().copied()))?;
494 }
495 Some(Enc::Jmp | Enc::Jcc) => {
496 for op in ops {
497 if let Operand::Block(call) = op {
498 let params = f.params(call.block()).iter().copied();
499 site(self, &mut params.zip(f.args(*call).iter().copied()))?;
500 }
501 }
502 }
503 _ => {}
504 }
505 }
506 }
507
508 let mut slots: Vec<SlotKind> = Vec::with_capacity(base + 2);
509 slots.extend_from_slice(f.slots());
510 for used in [need_scratch, need_victim] {
511 slots.push(if used {
512 SlotKind::Local {
513 size: 16,
514 align: 16,
515 }
516 } else {
517 SlotKind::Local { size: 0, align: 1 }
518 });
519 }
520 let frame = f.abi().layout_frame(&FrameInput {
521 slots: &slots,
522 written: f.written_regs(),
523 has_calls: f.has_calls(),
524 outgoing: f.outgoing(),
525 frame_pointer: f.frame_pointer(),
526 })?;
527
528 let entry = self.define(f.name())?;
529 self.asm.bind(entry).map_err(EmitError::Asm)?;
530 let probe_reg = probe_scratch(f)?;
531 for &step in frame.prologue() {
532 self.frame_step(step, probe_reg)?;
533 }
534 self.labels.clear();
535 for _ in 0..f.block_count() {
536 let l = self.asm.label();
537 self.labels.push(l);
538 }
539 let mut ctx = Ctx {
540 f,
541 frame: &frame,
542 site: 0,
543 };
544 for (bi, block) in f.blocks().enumerate() {
545 let label = self.labels[bi];
546 self.asm.bind(label).map_err(EmitError::Asm)?;
547 for &inst in f.insts(block) {
548 self.inst(&mut ctx, bi, inst)?;
549 }
550 }
551 Ok(FunctionInfo { frame })
552 }
553
554 pub fn finish(self) -> Result<Program, EmitError> {
580 if let Some((name, _)) = self.symbols.iter().find(|(_, s)| !s.defined) {
581 return Err(EmitError::UndefinedSymbol(name.clone()));
582 }
583 let code = self.asm.finish().map_err(EmitError::Asm)?;
584 let symbols = self
585 .symbols
586 .iter()
587 .filter_map(|(n, s)| code.label_offset(s.label).map(|o| (n.clone(), o)))
588 .collect();
589 Ok(Program {
590 bytes: code.into_bytes(),
591 symbols,
592 })
593 }
594
595 fn label_for(&mut self, name: &str) -> Label {
596 if let Some(s) = self.symbols.get(name) {
597 return s.label;
598 }
599 let label = self.asm.label();
600 let _ = self.symbols.insert(
601 name.to_string(),
602 Sym {
603 label,
604 defined: false,
605 },
606 );
607 label
608 }
609
610 fn define(&mut self, name: &str) -> Result<Label, EmitError> {
611 let label = self.label_for(name);
612 match self.symbols.get_mut(name) {
613 Some(s) if s.defined => Err(EmitError::DuplicateSymbol(name.to_string())),
614 Some(s) => {
615 s.defined = true;
616 Ok(label)
617 }
618 None => Ok(label),
619 }
620 }
621
622 fn emit(&mut self, inst: Option<Inst>, i: &IInst) -> Result<(), EmitError> {
623 self.asm.emit(i).map_err(|e| asm_error(inst, e))
624 }
625
626 fn frame_step(&mut self, step: FrameStep, probe_reg: PReg) -> Result<(), EmitError> {
627 let rsp = IOp::Reg(IReg::RSP);
628 match step {
629 FrameStep::Push(r) => {
630 self.emit(None, &IInst::new(M::Push, [ireg(None, r, Size::Qword)?]))
631 }
632 FrameStep::Pop(r) => {
633 self.emit(None, &IInst::new(M::Pop, [ireg(None, r, Size::Qword)?]))
634 }
635 FrameStep::SetFramePointer => {
636 self.emit(None, &IInst::new(M::Mov, [IOp::Reg(IReg::RBP), rsp]))
637 }
638 FrameStep::Alloc { bytes, probe } if probe == 0 || bytes < probe => {
639 self.emit(None, &IInst::new(M::Sub, [rsp, IOp::Imm(i64::from(bytes))]))
640 }
641 FrameStep::Alloc { bytes, probe } => self.probe(bytes, probe, probe_reg),
642 FrameStep::Free(bytes) => {
643 self.emit(None, &IInst::new(M::Add, [rsp, IOp::Imm(i64::from(bytes))]))
644 }
645 _ => Err(EmitError::Unencodable { inst: None }),
646 }
647 }
648
649 fn probe(&mut self, bytes: u32, page: u32, counter: PReg) -> Result<(), EmitError> {
654 let rsp = IOp::Reg(IReg::RSP);
655 let touch = IInst::new(M::Test, [IMem::new(Size::Qword, IReg::RSP).into(), rsp]);
656 let step = IInst::new(M::Sub, [rsp, IOp::Imm(i64::from(page))]);
657 let pages = bytes / page;
658 let rest = bytes % page;
659 if pages <= 4 {
660 for _ in 0..pages {
661 self.emit(None, &step)?;
662 self.emit(None, &touch)?;
663 }
664 } else {
665 let c = ireg(None, counter, Size::Dword)?;
666 self.emit(None, &IInst::new(M::Mov, [c, IOp::Imm(i64::from(pages))]))?;
667 let top = self.asm.label();
668 self.asm.bind(top).map_err(EmitError::Asm)?;
669 self.emit(None, &step)?;
670 self.emit(None, &touch)?;
671 self.emit(None, &IInst::new(M::Dec, [c]))?;
672 self.asm.jcc(ICond::Ne, top).map_err(EmitError::Asm)?;
673 }
674 if rest > 0 {
675 self.emit(None, &IInst::new(M::Sub, [rsp, IOp::Imm(i64::from(rest))]))?;
676 }
677 Ok(())
678 }
679
680 fn site(&mut self, ctx: &mut Ctx<'_>) -> Result<bool, EmitError> {
682 let (start, end) = self.sites.get(ctx.site).copied().unwrap_or((0, 0));
683 ctx.site += 1;
684 for i in start as usize..end as usize {
685 let step = self.steps[i];
686 self.step(ctx.frame, step)?;
687 }
688 Ok(end > start)
689 }
690
691 fn next_site_empty(&self, ctx: &Ctx<'_>, ahead: usize) -> bool {
692 self.sites
693 .get(ctx.site + ahead)
694 .is_none_or(|&(s, e)| s == e)
695 }
696
697 fn step(&mut self, frame: &FrameLayout, step: Step) -> Result<(), EmitError> {
698 let gpr = |class: RegClass| {
699 if class == GPR {
700 Ok(())
701 } else {
702 Err(EmitError::Unencodable { inst: None })
703 }
704 };
705 match step {
706 Step::Swap { a, b, class } => {
707 gpr(class)?;
708 let i = IInst::new(
709 M::Xchg,
710 [ireg(None, a, Size::Qword)?, ireg(None, b, Size::Qword)?],
711 );
712 self.emit(None, &i)
713 }
714 Step::Move { dst, src, class } => {
715 gpr(class)?;
716 let i = match (dst, src) {
717 (Loc::Reg(d), Source::Imm(v)) => return self.mov_imm(None, d, v),
718 (Loc::Reg(d), Source::Loc(Loc::Reg(s))) => IInst::new(
719 M::Mov,
720 [ireg(None, d, Size::Qword)?, ireg(None, s, Size::Qword)?],
721 ),
722 (Loc::Reg(d), Source::Loc(Loc::Slot(s))) => IInst::new(
723 M::Mov,
724 [
725 ireg(None, d, Size::Qword)?,
726 slot_mem(frame, s, 0, Size::Qword)?.into(),
727 ],
728 ),
729 (Loc::Slot(d), Source::Loc(Loc::Reg(s))) => IInst::new(
730 M::Mov,
731 [
732 slot_mem(frame, d, 0, Size::Qword)?.into(),
733 ireg(None, s, Size::Qword)?,
734 ],
735 ),
736 (Loc::Slot(d), Source::Imm(v)) => IInst::new(
737 M::Mov,
738 [slot_mem(frame, d, 0, Size::Qword)?.into(), IOp::Imm(v)],
739 ),
740 (Loc::Slot(_), Source::Loc(Loc::Slot(_))) => {
742 return Err(EmitError::Unencodable { inst: None });
743 }
744 };
745 self.emit(None, &i)
746 }
747 }
748 }
749
750 fn mov_imm(&mut self, inst: Option<Inst>, reg: PReg, value: i64) -> Result<(), EmitError> {
753 let i = if (0..=i64::from(u32::MAX)).contains(&value) {
754 IInst::new(
755 M::Mov,
756 [
757 ireg(inst, reg, Size::Dword)?,
758 IOp::Imm(i64::from(value as u32 as i32)),
759 ],
760 )
761 } else {
762 IInst::new(M::Mov, [ireg(inst, reg, Size::Qword)?, IOp::Imm(value)])
763 };
764 self.emit(inst, &i)
765 }
766
767 fn inst(&mut self, ctx: &mut Ctx<'_>, bi: usize, inst: Inst) -> Result<(), EmitError> {
768 let f = ctx.f;
769 let Some(opcode) = f.opcode(inst) else {
770 return Ok(());
771 };
772 let ops = f.operands(inst);
773 let enc = ENCODINGS
774 .get(opcode.index())
775 .copied()
776 .ok_or(EmitError::UnsupportedTarget)?;
777 let next = Block::from_index(bi + 1);
778 match enc {
779 Enc::Copy | Enc::Pcopy => {
780 let _ = self.site(ctx)?;
781 Ok(())
782 }
783 Enc::Jmp => {
784 let call = block_operand(ops, 0, inst)?;
785 let _ = self.site(ctx)?;
786 if call.block() != next {
787 self.jmp(call.block())?;
788 }
789 Ok(())
790 }
791 Enc::Jcc => {
792 let cond = cond_operand(ops, 0, inst)?;
793 let then = block_operand(ops, 1, inst)?;
794 let other = block_operand(ops, 2, inst)?;
795 let then_moves = !self.next_site_empty(ctx, 0);
796 let else_moves = !self.next_site_empty(ctx, 1);
797 if !then_moves {
798 self.jcc(cond, then.block())?;
799 let _ = self.site(ctx)?;
800 let _ = self.site(ctx)?;
801 } else if !else_moves {
802 self.jcc(cond.negate(), other.block())?;
803 let _ = self.site(ctx)?;
804 if then.block() != next {
805 self.jmp(then.block())?;
806 }
807 let _ = self.site(ctx)?;
808 return Ok(());
809 } else {
810 let stub = self.asm.label();
811 self.asm.jcc(cond.negate(), stub).map_err(EmitError::Asm)?;
812 let _ = self.site(ctx)?;
813 self.jmp(then.block())?;
814 self.asm.bind(stub).map_err(EmitError::Asm)?;
815 let _ = self.site(ctx)?;
816 }
817 if other.block() != next {
818 self.jmp(other.block())?;
819 }
820 Ok(())
821 }
822 Enc::Ret => {
823 for &step in ctx.frame.epilogue() {
824 self.frame_step(step, R11)?;
825 }
826 self.emit(Some(inst), &IInst::new(M::Ret, []))
827 }
828 Enc::Call => match ops.first() {
829 Some(Operand::Symbol(s)) => {
830 let name = f
831 .symbol_name(*s)
832 .ok_or(EmitError::Mir(MirError::UnknownSymbol(*s)))?;
833 let label = self.label_for(name);
834 self.asm.call(label).map_err(|e| asm_error(Some(inst), e))
835 }
836 Some(op) => {
837 let target = self.operand(ctx, inst, op, Size::Qword)?;
838 self.emit(Some(inst), &IInst::new(M::Call, [target]))
839 }
840 None => Err(EmitError::Mir(MirError::OperandShape { inst, index: 0 })),
841 },
842 Enc::MovImm64 => match (
843 ops.first().and_then(Operand::reg).and_then(Reg::as_phys),
844 ops.get(1),
845 ) {
846 (Some(d), Some(Operand::Imm(v))) => self.mov_imm(Some(inst), d, *v),
847 _ => Err(EmitError::Mir(MirError::OperandShape { inst, index: 0 })),
848 },
849 Enc::Raw(bytes) => self.asm.bytes(bytes).map_err(|e| asm_error(Some(inst), e)),
850 Enc::Isa {
851 fam,
852 ops: srcs,
853 cond,
854 } => {
855 let mnemonic = match fam {
856 Fam::Plain(m) => m,
857 Fam::Setcc => M::Setcc(cond_operand(ops, usize::from(cond), inst)?),
858 Fam::Cmovcc => M::Cmovcc(cond_operand(ops, usize::from(cond), inst)?),
859 };
860 let mut out = [IOp::Imm(0); 3];
861 let mut rip = None;
862 for (k, src) in srcs.iter().enumerate().take(3) {
863 out[k] = match *src {
864 Src::Fixed(r) => IOp::Reg(r),
865 Src::Op(n, size) => match ops.get(usize::from(n)) {
866 Some(Operand::Symbol(s)) => {
867 rip = Some(*s);
868 IOp::Mem(IMem::rip(size, 0))
869 }
870 Some(op) => self.operand(ctx, inst, op, size)?,
871 None => {
872 return Err(EmitError::Mir(MirError::OperandShape {
873 inst,
874 index: usize::from(n),
875 }));
876 }
877 },
878 };
879 }
880 let i = IInst::from_slice(mnemonic, &out[..srcs.len().min(3)])
881 .ok_or(EmitError::Unencodable { inst: Some(inst) })?;
882 match rip {
883 Some(s) => {
884 let name = f
885 .symbol_name(s)
886 .ok_or(EmitError::Mir(MirError::UnknownSymbol(s)))?;
887 let label = self.label_for(name);
888 self.asm
889 .emit_rip(&i, label)
890 .map_err(|e| asm_error(Some(inst), e))
891 }
892 None => self.emit(Some(inst), &i),
893 }
894 }
895 }
896 }
897
898 fn jmp(&mut self, block: Block) -> Result<(), EmitError> {
899 let label = self
900 .labels
901 .get(block.index())
902 .copied()
903 .ok_or(EmitError::Mir(MirError::UnknownBlock(block)))?;
904 self.asm.jmp(label).map_err(EmitError::Asm)
905 }
906
907 fn jcc(&mut self, cond: ICond, block: Block) -> Result<(), EmitError> {
908 let label = self
909 .labels
910 .get(block.index())
911 .copied()
912 .ok_or(EmitError::Mir(MirError::UnknownBlock(block)))?;
913 self.asm.jcc(cond, label).map_err(EmitError::Asm)
914 }
915
916 fn operand(
917 &self,
918 ctx: &Ctx<'_>,
919 inst: Inst,
920 op: &Operand,
921 size: Size,
922 ) -> Result<IOp, EmitError> {
923 Ok(match *op {
924 Operand::Reg(r) => {
925 let p = r.as_phys().ok_or(EmitError::VirtualRegister {
926 block: ctx.f.inst_block(inst).unwrap_or(Block::new(0)),
927 inst: Some(inst),
928 })?;
929 ireg(Some(inst), p, size)?
930 }
931 Operand::Imm(v) => IOp::Imm(v),
932 Operand::Mem(m) => IOp::Mem(mem(inst, &m, size)?),
933 Operand::Slot { slot, offset } => IOp::Mem(slot_mem(ctx.frame, slot, offset, size)?),
934 _ => return Err(EmitError::Unencodable { inst: Some(inst) }),
935 })
936 }
937}
938
939fn asm_error(inst: Option<Inst>, e: AsmError) -> EmitError {
940 match e {
941 AsmError::Encode(error) => EmitError::Encode { inst, error },
942 other => EmitError::Asm(other),
943 }
944}
945
946fn ireg(inst: Option<Inst>, p: PReg, size: Size) -> Result<IOp, EmitError> {
948 let info = REGS.get(p.index()).ok_or(EmitError::Unencodable { inst })?;
949 if info.class() != GPR {
950 return Err(EmitError::Unencodable { inst });
951 }
952 IReg::new(size, info.hw())
953 .map(IOp::Reg)
954 .ok_or(EmitError::Unencodable { inst })
955}
956
957fn qword(inst: Inst, r: Reg) -> Result<IReg, EmitError> {
958 let p = r
959 .as_phys()
960 .ok_or(EmitError::Unencodable { inst: Some(inst) })?;
961 match ireg(Some(inst), p, Size::Qword)? {
962 IOp::Reg(r) => Ok(r),
963 _ => Err(EmitError::Unencodable { inst: Some(inst) }),
964 }
965}
966
967fn mem(inst: Inst, m: &Mem, size: Size) -> Result<IMem, EmitError> {
968 let mut out = match m.base_reg() {
969 Some(b) => IMem::new(size, qword(inst, b)?),
970 None => IMem::absolute(size, 0),
971 };
972 if let Some((i, s)) = m.index_reg() {
973 let scale = match s {
974 Scale::X1 => IScale::X1,
975 Scale::X2 => IScale::X2,
976 Scale::X4 => IScale::X4,
977 Scale::X8 => IScale::X8,
978 };
979 out = out.with_index(qword(inst, i)?, scale);
980 }
981 Ok(out.with_disp(m.disp()))
982}
983
984fn slot_mem(
985 frame: &FrameLayout,
986 slot: StackSlot,
987 offset: i32,
988 size: Size,
989) -> Result<IMem, EmitError> {
990 let base = frame
991 .slot_offset(slot)
992 .ok_or(EmitError::Mir(MirError::UnknownSlot(slot)))?;
993 let disp = base
994 .checked_add(offset)
995 .ok_or(EmitError::Frame(FrameError::TooLarge))?;
996 Ok(IMem::new(size, IReg::RSP).with_disp(disp))
997}
998
999fn block_operand(ops: &[Operand], index: usize, inst: Inst) -> Result<BlockCall, EmitError> {
1000 match ops.get(index) {
1001 Some(Operand::Block(c)) => Ok(*c),
1002 _ => Err(EmitError::Mir(MirError::OperandShape { inst, index })),
1003 }
1004}
1005
1006fn cond_operand(ops: &[Operand], index: usize, inst: Inst) -> Result<ICond, EmitError> {
1007 match ops.get(index) {
1008 Some(Operand::Cond(c)) => {
1009 ICond::from_code(c.code()).ok_or(EmitError::Mir(MirError::OperandShape { inst, index }))
1010 }
1011 _ => Err(EmitError::Mir(MirError::OperandShape { inst, index })),
1012 }
1013}
1014
1015fn to_move(f: &Function, dst: Operand, src: Operand) -> Result<Move, EmitError> {
1018 let class_of = |op: &Operand| op.reg().and_then(|r| f.reg_class(r));
1019 let class = class_of(&dst)
1020 .or_else(|| class_of(&src))
1021 .unwrap_or(f.target().addr_class());
1022 let loc = |op: Operand| -> Option<Loc> {
1023 match op {
1024 Operand::Reg(r) => r.as_phys().map(Loc::Reg),
1025 Operand::Slot { slot, offset: 0 } => Some(Loc::Slot(slot)),
1026 _ => None,
1027 }
1028 };
1029 let bad = EmitError::Unencodable { inst: None };
1030 let d = loc(dst).ok_or(bad.clone())?;
1031 let s = match src {
1032 Operand::Imm(v) => Source::Imm(v),
1033 other => Source::Loc(loc(other).ok_or(bad)?),
1034 };
1035 Ok(Move {
1036 dst: d,
1037 src: s,
1038 class,
1039 })
1040}
1041
1042fn probe_scratch(f: &Function) -> Result<PReg, EmitError> {
1045 let params = f.entry().map_or(&[][..], |e| f.params(e));
1046 let taken = |r: PReg| params.iter().any(|p| p.reg() == Some(r.into()));
1047 [R11, R10]
1048 .into_iter()
1049 .find(|&r| !taken(r))
1050 .ok_or(EmitError::ProbeScratch)
1051}
1052
1053fn check_physical(f: &Function) -> Result<(), EmitError> {
1056 let target = f.target();
1057 let reserved = f.reserved();
1058 for block in f.blocks() {
1059 for p in f.params(block) {
1060 if let Some(r) = p.reg() {
1061 match r.as_phys() {
1062 None => return Err(EmitError::VirtualRegister { block, inst: None }),
1063 Some(p) if reserved.contains(p) => {
1064 return Err(EmitError::ReservedWrite { block, inst: None });
1065 }
1066 Some(_) => {}
1067 }
1068 }
1069 }
1070 for &inst in f.insts(block) {
1071 let virt = EmitError::VirtualRegister {
1072 block,
1073 inst: Some(inst),
1074 };
1075 let ops = f.operands(inst);
1076 for op in ops {
1077 let any_virtual = match op {
1078 Operand::Reg(r) => r.is_virtual(),
1079 Operand::Mem(m) => m.regs().any(Reg::is_virtual),
1080 Operand::Block(call) => f
1081 .args(*call)
1082 .iter()
1083 .any(|a| a.reg().is_some_and(Reg::is_virtual)),
1084 _ => false,
1085 };
1086 if any_virtual {
1087 return Err(virt);
1088 }
1089 }
1090 let Some(desc) = f.opcode(inst).and_then(|o| target.inst(o)) else {
1091 continue;
1092 };
1093 let defs = match desc.kind() {
1094 InstKind::Copy => 1,
1095 InstKind::ParallelCopy => ops.len() / 2,
1096 _ => desc.defs(),
1097 };
1098 let reserved_write = ops
1099 .iter()
1100 .take(defs)
1101 .filter_map(|o| o.reg().and_then(Reg::as_phys))
1102 .chain(
1103 f.implicit(inst)
1104 .iter()
1105 .filter(|i| i.kind == ImplicitKind::Def)
1106 .map(|i| i.reg),
1107 )
1108 .chain(desc.implicit_defs().iter().copied())
1109 .any(|p| reserved.contains(p))
1110 || !f.clobbers(inst).intersect(reserved).is_empty();
1111 if reserved_write {
1112 return Err(EmitError::ReservedWrite {
1113 block,
1114 inst: Some(inst),
1115 });
1116 }
1117 for (i, rule) in desc.operands().iter().enumerate() {
1118 if let OperandRule::Tied(d) = rule {
1119 if ops.get(i).and_then(Operand::reg)
1120 != ops.get(usize::from(*d)).and_then(Operand::reg)
1121 {
1122 return Err(EmitError::TiedMismatch { inst });
1123 }
1124 }
1125 }
1126 }
1127 }
1128 Ok(())
1129}