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 Constraint {
125 inst: Inst,
127 },
128 ExternalConflict(String),
131}
132
133impl fmt::Display for EmitError {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 match self {
136 EmitError::Mir(e) => fmt::Display::fmt(e, f),
137 EmitError::Frame(e) => fmt::Display::fmt(e, f),
138 EmitError::Moves(e) => fmt::Display::fmt(e, f),
139 EmitError::UnsupportedTarget => {
140 f.write_str("the function's target is not the x86-64 description this emitter encodes")
141 }
142 EmitError::VirtualRegister { block, inst } => match inst {
143 Some(i) => write!(f, "{i} in {block} still names a virtual register; allocate registers first"),
144 None => write!(f, "a parameter of {block} is a virtual register; allocate registers first"),
145 },
146 EmitError::TiedMismatch { inst } => write!(
147 f,
148 "{inst}: a tied use must be the same register as its definition"
149 ),
150 EmitError::ReservedWrite { block, inst } => match inst {
151 Some(i) => write!(f, "{i} in {block} writes the stack or frame pointer"),
152 None => write!(f, "a parameter of {block} is the stack or frame pointer"),
153 },
154 EmitError::Unencodable { inst } => match inst {
155 Some(i) => write!(f, "{i}: isa-lang 0.2 has no encoding for this vector-register operation"),
156 None => f.write_str("a frame or edge move needs a vector-register instruction isa-lang 0.2 does not encode"),
157 },
158 EmitError::Encode { inst, error } => match inst {
159 Some(i) => write!(f, "{i}: {error}"),
160 None => write!(f, "frame or edge move: {error}"),
161 },
162 EmitError::Asm(e) => fmt::Display::fmt(e, f),
163 EmitError::UndefinedSymbol(name) => {
164 write!(f, "`{name}` is called but no function of that name was emitted")
165 }
166 EmitError::DuplicateSymbol(name) => {
167 write!(f, "two functions named `{name}` were emitted")
168 }
169 EmitError::ProbeScratch => f.write_str(
170 "the stack probe loop needs r10 or r11, and the entry block receives values in both",
171 ),
172 EmitError::Constraint { inst } => write!(
173 f,
174 "{inst}: a table branch's temporary is its index, or a tail call's register target is restored by the epilogue"
175 ),
176 EmitError::ExternalConflict(name) => {
177 write!(f, "`{name}` is declared external and also emitted")
178 }
179 }
180 }
181}
182
183impl core::error::Error for EmitError {
184 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
185 match self {
186 EmitError::Mir(e) => Some(e),
187 EmitError::Frame(e) => Some(e),
188 EmitError::Moves(e) => Some(e),
189 EmitError::Encode { error, .. } => Some(error),
190 EmitError::Asm(e) => Some(e),
191 _ => None,
192 }
193 }
194}
195
196impl From<MirError> for EmitError {
197 fn from(e: MirError) -> EmitError {
198 EmitError::Mir(e)
199 }
200}
201
202impl From<FrameError> for EmitError {
203 fn from(e: FrameError) -> EmitError {
204 EmitError::Frame(e)
205 }
206}
207
208impl From<MoveError> for EmitError {
209 fn from(e: MoveError) -> EmitError {
210 EmitError::Moves(e)
211 }
212}
213
214#[derive(Clone, Debug, PartialEq, Eq)]
231pub struct FunctionInfo {
232 frame: FrameLayout,
233}
234
235impl FunctionInfo {
236 #[inline]
254 #[must_use]
255 pub fn frame(&self) -> &FrameLayout {
256 &self.frame
257 }
258}
259
260#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
272#[non_exhaustive]
273pub enum RelocKind {
274 Call32,
277 Pc32,
280 Abs64,
283}
284
285impl RelocKind {
286 #[inline]
296 #[must_use]
297 pub const fn size(self) -> usize {
298 match self {
299 RelocKind::Call32 | RelocKind::Pc32 => 4,
300 RelocKind::Abs64 => 8,
301 }
302 }
303}
304
305#[derive(Clone, Debug, PartialEq, Eq, Hash)]
333#[non_exhaustive]
334pub struct Reloc {
335 pub offset: usize,
337 pub kind: RelocKind,
339 pub symbol: String,
341 pub addend: i64,
343}
344
345impl Reloc {
346 #[must_use]
357 pub fn new(offset: usize, kind: RelocKind, symbol: &str, addend: i64) -> Reloc {
358 Reloc {
359 offset,
360 kind,
361 symbol: String::from(symbol),
362 addend,
363 }
364 }
365}
366
367#[derive(Clone, Debug, PartialEq, Eq)]
393pub struct CallSiteRecord {
394 return_offset: usize,
395 gc_slots: Vec<i32>,
396 landing_pad: Option<usize>,
397}
398
399impl CallSiteRecord {
400 #[inline]
407 #[must_use]
408 pub fn return_offset(&self) -> usize {
409 self.return_offset
410 }
411
412 #[inline]
422 #[must_use]
423 pub fn gc_slots(&self) -> &[i32] {
424 &self.gc_slots
425 }
426
427 #[inline]
434 #[must_use]
435 pub fn landing_pad(&self) -> Option<usize> {
436 self.landing_pad
437 }
438}
439
440#[derive(Clone, Debug, PartialEq, Eq)]
460pub struct EmittedFunction {
461 name: String,
462 offset: usize,
463 size: usize,
464 frame: FrameLayout,
465 call_sites: Vec<CallSiteRecord>,
466}
467
468impl EmittedFunction {
469 #[inline]
475 #[must_use]
476 pub fn name(&self) -> &str {
477 &self.name
478 }
479
480 #[inline]
486 #[must_use]
487 pub fn offset(&self) -> usize {
488 self.offset
489 }
490
491 #[inline]
497 #[must_use]
498 pub fn size(&self) -> usize {
499 self.size
500 }
501
502 #[inline]
511 #[must_use]
512 pub fn frame(&self) -> &FrameLayout {
513 &self.frame
514 }
515
516 #[inline]
522 #[must_use]
523 pub fn call_sites(&self) -> &[CallSiteRecord] {
524 &self.call_sites
525 }
526}
527
528#[derive(Clone, Debug, PartialEq, Eq)]
549pub struct Program {
550 bytes: Vec<u8>,
551 symbols: Vec<(String, usize)>,
552 functions: Vec<EmittedFunction>,
553 relocs: Vec<Reloc>,
554}
555
556impl Program {
557 #[inline]
568 #[must_use]
569 pub fn bytes(&self) -> &[u8] {
570 &self.bytes
571 }
572
573 #[must_use]
584 pub fn into_bytes(self) -> Vec<u8> {
585 self.bytes
586 }
587
588 #[must_use]
599 pub fn symbol(&self, name: &str) -> Option<usize> {
600 self.symbols
601 .binary_search_by(|(n, _)| n.as_str().cmp(name))
602 .ok()
603 .and_then(|i| self.symbols.get(i))
604 .map(|&(_, off)| off)
605 }
606
607 pub fn symbols(&self) -> impl Iterator<Item = (&str, usize)> {
618 self.symbols.iter().map(|(n, o)| (n.as_str(), *o))
619 }
620
621 #[inline]
633 #[must_use]
634 pub fn functions(&self) -> &[EmittedFunction] {
635 &self.functions
636 }
637
638 #[inline]
644 #[must_use]
645 pub fn relocations(&self) -> &[Reloc] {
646 &self.relocs
647 }
648}
649
650#[derive(Clone, Copy, Debug)]
651struct Sym {
652 label: Label,
653 defined: bool,
654 external: bool,
655}
656
657#[derive(Clone, Debug)]
659struct PendingFunc {
660 name: String,
661 start: Label,
662 end: Label,
663 frame: FrameLayout,
664 sites: core::ops::Range<usize>,
665}
666
667#[derive(Clone, Debug)]
669struct PendingSite {
670 ret: Label,
671 gc: core::ops::Range<usize>,
672 landing: Option<Label>,
673}
674
675#[derive(Clone, Debug)]
679struct PendingReloc {
680 after: Label,
681 kind: RelocKind,
682 symbol: String,
683}
684
685#[derive(Clone, Debug)]
688struct PendingTable {
689 at: Label,
690 targets: core::ops::Range<usize>,
691}
692
693#[derive(Debug, Default)]
723pub struct Emitter {
724 asm: Assembler,
725 symbols: BTreeMap<String, Sym>,
726 resolver: MoveResolver,
727 moves: Vec<Move>,
728 steps: Vec<Step>,
729 sites: Vec<(u32, u32)>,
730 labels: Vec<Label>,
731 funcs: Vec<PendingFunc>,
732 call_sites: Vec<PendingSite>,
733 gc_offsets: Vec<i32>,
734 relocs: Vec<PendingReloc>,
735 tables: Vec<PendingTable>,
736 table_targets: Vec<Label>,
737}
738
739struct Ctx<'a> {
741 f: &'a Function,
742 frame: &'a FrameLayout,
743 site: usize,
744 tables: usize,
745}
746impl Emitter {
747 #[must_use]
758 pub fn new() -> Emitter {
759 Emitter::default()
760 }
761
762 #[must_use]
772 pub fn with_capacity(bytes: usize) -> Emitter {
773 Emitter {
774 asm: Assembler::with_capacity(bytes),
775 ..Emitter::default()
776 }
777 }
778
779 pub fn function(&mut self, f: &Function) -> Result<FunctionInfo, EmitError> {
805 let target = f.target();
806 if !core::ptr::eq(target.insts(), &DESCS[..]) || !core::ptr::eq(target.regs(), ®S[..]) {
807 return Err(EmitError::UnsupportedTarget);
808 }
809 f.verify()?;
810 check_physical(f)?;
811
812 let base = f.slots().len();
815 let scratch = StackSlot::from_index(base);
816 let victim = StackSlot::from_index(base + 1);
817 let env = MoveEnv::new()
818 .with_scratch_slot(scratch)
819 .with_victim_slot(victim);
820 let (mut need_scratch, mut need_victim) = (false, false);
821 self.steps.clear();
822 self.sites.clear();
823 for block in f.blocks() {
824 for &inst in f.insts(block) {
825 let Some(opcode) = f.opcode(inst) else {
826 continue;
827 };
828 let ops = f.operands(inst);
829 let mut site =
830 |em: &mut Emitter, pairs: &mut dyn Iterator<Item = (Operand, Operand)>| {
831 em.moves.clear();
832 for (d, s) in pairs {
833 em.moves.push(to_move(f, d, s)?);
834 }
835 let start = em.steps.len();
836 let used = em
837 .resolver
838 .resolve(target, &em.moves, &env, &mut em.steps)?;
839 need_scratch |= used.scratch_slot;
840 need_victim |= used.victim_slot;
841 em.sites.push((start as u32, em.steps.len() as u32));
842 Ok::<(), EmitError>(())
843 };
844 match ENCODINGS.get(opcode.index()) {
845 Some(Enc::Copy) => {
846 site(
847 self,
848 &mut ops.first().copied().zip(ops.get(1).copied()).into_iter(),
849 )?;
850 }
851 Some(Enc::Pcopy) => {
852 let (d, s) = ops.split_at(ops.len() / 2);
853 site(self, &mut d.iter().copied().zip(s.iter().copied()))?;
854 }
855 Some(Enc::Jmp | Enc::Jcc) => {
856 for op in ops {
857 if let Operand::Block(call) = op {
858 let params = f.params(call.block()).iter().copied();
859 site(self, &mut params.zip(f.args(*call).iter().copied()))?;
860 }
861 }
862 }
863 _ => {}
864 }
865 }
866 }
867
868 let mut slots: Vec<SlotKind> = Vec::with_capacity(base + 2);
869 slots.extend_from_slice(f.slots());
870 for used in [need_scratch, need_victim] {
871 slots.push(if used {
872 SlotKind::Local {
873 size: 16,
874 align: 16,
875 }
876 } else {
877 SlotKind::Local { size: 0, align: 1 }
878 });
879 }
880 let frame = f.abi().layout_frame(&FrameInput {
881 slots: &slots,
882 written: f.written_regs(),
883 has_calls: f.has_calls(),
884 outgoing: f.outgoing(),
885 frame_pointer: f.frame_pointer(),
886 })?;
887
888 let entry = self.define(f.name())?;
889 self.asm.bind(entry).map_err(EmitError::Asm)?;
890 let probe_reg = probe_scratch(f)?;
891 for &step in frame.prologue() {
892 self.frame_step(step, probe_reg)?;
893 }
894 self.labels.clear();
895 for _ in 0..f.block_count() {
896 let l = self.asm.label();
897 self.labels.push(l);
898 }
899 let first_site = self.call_sites.len();
900 let first_table = self.tables.len();
901 let mut ctx = Ctx {
902 f,
903 frame: &frame,
904 site: 0,
905 tables: 0,
906 };
907 for (bi, block) in f.blocks().enumerate() {
908 let label = self.labels[bi];
909 self.asm.bind(label).map_err(EmitError::Asm)?;
910 for &inst in f.insts(block) {
911 self.inst(&mut ctx, bi, inst)?;
912 }
913 }
914 for t in first_table..self.tables.len() {
916 let (at, n) = match self.tables.get(t) {
917 Some(p) => (p.at, p.targets.len()),
918 None => continue,
919 };
920 self.asm.bind(at).map_err(EmitError::Asm)?;
921 let zeros = [0u8; 64];
922 let mut left = n.saturating_mul(8);
923 while left > 0 {
924 let k = left.min(zeros.len());
925 self.asm
926 .bytes(&zeros[..k])
927 .map_err(|e| asm_error(None, e))?;
928 left -= k;
929 }
930 }
931 let end = self.asm.label();
932 self.asm.bind(end).map_err(EmitError::Asm)?;
933 self.funcs.push(PendingFunc {
934 name: f.name().to_string(),
935 start: entry,
936 end,
937 frame: frame.clone(),
938 sites: first_site..self.call_sites.len(),
939 });
940 Ok(FunctionInfo { frame })
941 }
942
943 pub fn declare_external(&mut self, name: &str) -> Result<(), EmitError> {
970 let _ = self.label_for(name);
971 match self.symbols.get_mut(name) {
972 Some(s) if s.defined => Err(EmitError::ExternalConflict(name.to_string())),
973 Some(s) => {
974 s.external = true;
975 Ok(())
976 }
977 None => Ok(()),
978 }
979 }
980
981 pub fn finish(self) -> Result<Program, EmitError> {
1008 if let Some((name, _)) = self.symbols.iter().find(|(_, s)| !s.defined && !s.external) {
1009 return Err(EmitError::UndefinedSymbol(name.clone()));
1010 }
1011 let code = self.asm.finish().map_err(EmitError::Asm)?;
1012 let at = |l: Label| {
1013 code.label_offset(l)
1014 .ok_or(EmitError::Asm(AsmError::Unbound(l)))
1015 };
1016 let symbols = self
1017 .symbols
1018 .iter()
1019 .filter(|(_, s)| s.defined)
1020 .filter_map(|(n, s)| code.label_offset(s.label).map(|o| (n.clone(), o)))
1021 .collect();
1022 let mut relocs = Vec::with_capacity(self.relocs.len());
1023 for r in &self.relocs {
1024 let end = at(r.after)?;
1027 relocs.push(Reloc {
1028 offset: end.saturating_sub(4),
1029 kind: r.kind,
1030 symbol: r.symbol.clone(),
1031 addend: -4,
1032 });
1033 }
1034 let mut patches: Vec<(usize, i64)> = Vec::new();
1035 for t in &self.tables {
1036 let base = at(t.at)?;
1037 for (k, &target) in self
1038 .table_targets
1039 .get(t.targets.clone())
1040 .unwrap_or(&[])
1041 .iter()
1042 .enumerate()
1043 {
1044 let to = at(target)?;
1045 patches.push((base + 8 * k, to as i64 - base as i64));
1046 }
1047 }
1048 let mut functions = Vec::with_capacity(self.funcs.len());
1049 for pf in &self.funcs {
1050 let offset = at(pf.start)?;
1051 let size = at(pf.end)?.saturating_sub(offset);
1052 let mut call_sites = Vec::with_capacity(pf.sites.len());
1053 for ps in self.call_sites.get(pf.sites.clone()).unwrap_or(&[]) {
1054 call_sites.push(CallSiteRecord {
1055 return_offset: at(ps.ret)?,
1056 gc_slots: self.gc_offsets.get(ps.gc.clone()).unwrap_or(&[]).to_vec(),
1057 landing_pad: match ps.landing {
1058 Some(l) => Some(at(l)?),
1059 None => None,
1060 },
1061 });
1062 }
1063 functions.push(EmittedFunction {
1064 name: pf.name.clone(),
1065 offset,
1066 size,
1067 frame: pf.frame.clone(),
1068 call_sites,
1069 });
1070 }
1071 let mut bytes = code.into_bytes();
1072 for (pos, value) in patches {
1073 if let Some(field) = bytes.get_mut(pos..pos + 8) {
1074 field.copy_from_slice(&value.to_le_bytes());
1075 }
1076 }
1077 Ok(Program {
1078 bytes,
1079 symbols,
1080 functions,
1081 relocs,
1082 })
1083 }
1084
1085 fn label_for(&mut self, name: &str) -> Label {
1086 if let Some(s) = self.symbols.get(name) {
1087 return s.label;
1088 }
1089 let label = self.asm.label();
1090 let _ = self.symbols.insert(
1091 name.to_string(),
1092 Sym {
1093 label,
1094 defined: false,
1095 external: false,
1096 },
1097 );
1098 label
1099 }
1100
1101 fn define(&mut self, name: &str) -> Result<Label, EmitError> {
1102 let label = self.label_for(name);
1103 match self.symbols.get_mut(name) {
1104 Some(s) if s.external => Err(EmitError::ExternalConflict(name.to_string())),
1105 Some(s) if s.defined => Err(EmitError::DuplicateSymbol(name.to_string())),
1106 Some(s) => {
1107 s.defined = true;
1108 Ok(label)
1109 }
1110 None => Ok(label),
1111 }
1112 }
1113
1114 fn is_external(&self, name: &str) -> bool {
1115 self.symbols.get(name).is_some_and(|s| s.external)
1116 }
1117
1118 fn external_ref(
1121 &mut self,
1122 inst: Option<Inst>,
1123 bytes: &[u8],
1124 kind: RelocKind,
1125 symbol: &str,
1126 ) -> Result<(), EmitError> {
1127 self.asm.bytes(bytes).map_err(|e| asm_error(inst, e))?;
1128 let after = self.asm.label();
1129 self.asm.bind(after).map_err(EmitError::Asm)?;
1130 self.relocs.push(PendingReloc {
1131 after,
1132 kind,
1133 symbol: symbol.to_string(),
1134 });
1135 Ok(())
1136 }
1137
1138 fn branch_to_symbol(&mut self, inst: Inst, name: &str, jump: bool) -> Result<(), EmitError> {
1141 if self.is_external(name) {
1142 let opcode = if jump { 0xE9 } else { 0xE8 };
1143 return self.external_ref(Some(inst), &[opcode, 0, 0, 0, 0], RelocKind::Call32, name);
1144 }
1145 let label = self.label_for(name);
1146 if jump {
1147 self.asm.jmp(label).map_err(|e| asm_error(Some(inst), e))
1148 } else {
1149 self.asm.call(label).map_err(|e| asm_error(Some(inst), e))
1150 }
1151 }
1152
1153 fn emit(&mut self, inst: Option<Inst>, i: &IInst) -> Result<(), EmitError> {
1154 self.asm.emit(i).map_err(|e| asm_error(inst, e))
1155 }
1156 fn frame_step(&mut self, step: FrameStep, probe_reg: PReg) -> Result<(), EmitError> {
1157 let rsp = IOp::Reg(IReg::RSP);
1158 match step {
1159 FrameStep::Push(r) => {
1160 self.emit(None, &IInst::new(M::Push, [ireg(None, r, Size::Qword)?]))
1161 }
1162 FrameStep::Pop(r) => {
1163 self.emit(None, &IInst::new(M::Pop, [ireg(None, r, Size::Qword)?]))
1164 }
1165 FrameStep::SetFramePointer => {
1166 self.emit(None, &IInst::new(M::Mov, [IOp::Reg(IReg::RBP), rsp]))
1167 }
1168 FrameStep::Alloc { bytes, probe } if probe == 0 || bytes < probe => {
1169 self.emit(None, &IInst::new(M::Sub, [rsp, IOp::Imm(i64::from(bytes))]))
1170 }
1171 FrameStep::Alloc { bytes, probe } => self.probe(bytes, probe, probe_reg),
1172 FrameStep::Free(bytes) => {
1173 self.emit(None, &IInst::new(M::Add, [rsp, IOp::Imm(i64::from(bytes))]))
1174 }
1175 _ => Err(EmitError::Unencodable { inst: None }),
1176 }
1177 }
1178
1179 fn probe(&mut self, bytes: u32, page: u32, counter: PReg) -> Result<(), EmitError> {
1184 let rsp = IOp::Reg(IReg::RSP);
1185 let touch = IInst::new(M::Test, [IMem::new(Size::Qword, IReg::RSP).into(), rsp]);
1186 let step = IInst::new(M::Sub, [rsp, IOp::Imm(i64::from(page))]);
1187 let pages = bytes / page;
1188 let rest = bytes % page;
1189 if pages <= 4 {
1190 for _ in 0..pages {
1191 self.emit(None, &step)?;
1192 self.emit(None, &touch)?;
1193 }
1194 } else {
1195 let c = ireg(None, counter, Size::Dword)?;
1196 self.emit(None, &IInst::new(M::Mov, [c, IOp::Imm(i64::from(pages))]))?;
1197 let top = self.asm.label();
1198 self.asm.bind(top).map_err(EmitError::Asm)?;
1199 self.emit(None, &step)?;
1200 self.emit(None, &touch)?;
1201 self.emit(None, &IInst::new(M::Dec, [c]))?;
1202 self.asm.jcc(ICond::Ne, top).map_err(EmitError::Asm)?;
1203 }
1204 if rest > 0 {
1205 self.emit(None, &IInst::new(M::Sub, [rsp, IOp::Imm(i64::from(rest))]))?;
1206 }
1207 Ok(())
1208 }
1209
1210 fn site(&mut self, ctx: &mut Ctx<'_>) -> Result<bool, EmitError> {
1212 let (start, end) = self.sites.get(ctx.site).copied().unwrap_or((0, 0));
1213 ctx.site += 1;
1214 for i in start as usize..end as usize {
1215 let step = self.steps[i];
1216 self.step(ctx.frame, step)?;
1217 }
1218 Ok(end > start)
1219 }
1220
1221 fn next_site_empty(&self, ctx: &Ctx<'_>, ahead: usize) -> bool {
1222 self.sites
1223 .get(ctx.site + ahead)
1224 .is_none_or(|&(s, e)| s == e)
1225 }
1226
1227 fn step(&mut self, frame: &FrameLayout, step: Step) -> Result<(), EmitError> {
1228 let gpr = |class: RegClass| {
1229 if class == GPR {
1230 Ok(())
1231 } else {
1232 Err(EmitError::Unencodable { inst: None })
1233 }
1234 };
1235 match step {
1236 Step::Swap { a, b, class } => {
1237 gpr(class)?;
1238 let i = IInst::new(
1239 M::Xchg,
1240 [ireg(None, a, Size::Qword)?, ireg(None, b, Size::Qword)?],
1241 );
1242 self.emit(None, &i)
1243 }
1244 Step::Move { dst, src, class } => {
1245 gpr(class)?;
1246 let i = match (dst, src) {
1247 (Loc::Reg(d), Source::Imm(v)) => return self.mov_imm(None, d, v),
1248 (Loc::Reg(d), Source::Loc(Loc::Reg(s))) => IInst::new(
1249 M::Mov,
1250 [ireg(None, d, Size::Qword)?, ireg(None, s, Size::Qword)?],
1251 ),
1252 (Loc::Reg(d), Source::Loc(Loc::Slot(s))) => IInst::new(
1253 M::Mov,
1254 [
1255 ireg(None, d, Size::Qword)?,
1256 slot_mem(frame, s, 0, Size::Qword)?.into(),
1257 ],
1258 ),
1259 (Loc::Slot(d), Source::Loc(Loc::Reg(s))) => IInst::new(
1260 M::Mov,
1261 [
1262 slot_mem(frame, d, 0, Size::Qword)?.into(),
1263 ireg(None, s, Size::Qword)?,
1264 ],
1265 ),
1266 (Loc::Slot(d), Source::Imm(v)) => IInst::new(
1267 M::Mov,
1268 [slot_mem(frame, d, 0, Size::Qword)?.into(), IOp::Imm(v)],
1269 ),
1270 (Loc::Slot(_), Source::Loc(Loc::Slot(_))) => {
1272 return Err(EmitError::Unencodable { inst: None });
1273 }
1274 };
1275 self.emit(None, &i)
1276 }
1277 }
1278 }
1279
1280 fn mov_imm(&mut self, inst: Option<Inst>, reg: PReg, value: i64) -> Result<(), EmitError> {
1283 let i = if (0..=i64::from(u32::MAX)).contains(&value) {
1284 IInst::new(
1285 M::Mov,
1286 [
1287 ireg(inst, reg, Size::Dword)?,
1288 IOp::Imm(i64::from(value as u32 as i32)),
1289 ],
1290 )
1291 } else {
1292 IInst::new(M::Mov, [ireg(inst, reg, Size::Qword)?, IOp::Imm(value)])
1293 };
1294 self.emit(inst, &i)
1295 }
1296
1297 fn inst(&mut self, ctx: &mut Ctx<'_>, bi: usize, inst: Inst) -> Result<(), EmitError> {
1298 let f = ctx.f;
1299 let Some(opcode) = f.opcode(inst) else {
1300 return Ok(());
1301 };
1302 let ops = f.operands(inst);
1303 let enc = ENCODINGS
1304 .get(opcode.index())
1305 .copied()
1306 .ok_or(EmitError::UnsupportedTarget)?;
1307 let next = Block::from_index(bi + 1);
1308 match enc {
1309 Enc::Copy | Enc::Pcopy => {
1310 let _ = self.site(ctx)?;
1311 Ok(())
1312 }
1313 Enc::Jmp => {
1314 let call = block_operand(ops, 0, inst)?;
1315 let _ = self.site(ctx)?;
1316 if call.block() != next {
1317 self.jmp(call.block())?;
1318 }
1319 Ok(())
1320 }
1321 Enc::Jcc => {
1322 let cond = cond_operand(ops, 0, inst)?;
1323 let then = block_operand(ops, 1, inst)?;
1324 let other = block_operand(ops, 2, inst)?;
1325 let then_moves = !self.next_site_empty(ctx, 0);
1326 let else_moves = !self.next_site_empty(ctx, 1);
1327 if !then_moves {
1328 self.jcc(cond, then.block())?;
1329 let _ = self.site(ctx)?;
1330 let _ = self.site(ctx)?;
1331 } else if !else_moves {
1332 self.jcc(cond.negate(), other.block())?;
1333 let _ = self.site(ctx)?;
1334 if then.block() != next {
1335 self.jmp(then.block())?;
1336 }
1337 let _ = self.site(ctx)?;
1338 return Ok(());
1339 } else {
1340 let stub = self.asm.label();
1341 self.asm.jcc(cond.negate(), stub).map_err(EmitError::Asm)?;
1342 let _ = self.site(ctx)?;
1343 self.jmp(then.block())?;
1344 self.asm.bind(stub).map_err(EmitError::Asm)?;
1345 let _ = self.site(ctx)?;
1346 }
1347 if other.block() != next {
1348 self.jmp(other.block())?;
1349 }
1350 Ok(())
1351 }
1352 Enc::Ret => {
1353 for &step in ctx.frame.epilogue() {
1354 self.frame_step(step, R11)?;
1355 }
1356 self.emit(Some(inst), &IInst::new(M::Ret, []))
1357 }
1358 Enc::TailCall => {
1359 let target = match ops.first() {
1361 Some(Operand::Reg(r)) => {
1362 let p = r.as_phys().ok_or(EmitError::VirtualRegister {
1363 block: Block::from_index(bi),
1364 inst: Some(inst),
1365 })?;
1366 if ctx.frame.pushed().contains(&p)
1367 || (ctx.frame.frame_pointer() && p == super::RBP)
1368 {
1369 return Err(EmitError::Constraint { inst });
1370 }
1371 Some(ireg(Some(inst), p, Size::Qword)?)
1372 }
1373 _ => None,
1374 };
1375 for &step in ctx.frame.epilogue() {
1376 self.frame_step(step, R11)?;
1377 }
1378 match (target, ops.first()) {
1379 (Some(t), _) => self.emit(Some(inst), &IInst::new(M::Jmp, [t])),
1380 (None, Some(Operand::Symbol(s))) => {
1381 let name = f
1382 .symbol_name(*s)
1383 .ok_or(EmitError::Mir(MirError::UnknownSymbol(*s)))?;
1384 self.branch_to_symbol(inst, name, true)
1385 }
1386 _ => Err(EmitError::Mir(MirError::OperandShape { inst, index: 0 })),
1387 }
1388 }
1389 Enc::JumpTable => {
1390 let reg = |index: usize| -> Result<IReg, EmitError> {
1391 match ops.get(index) {
1392 Some(Operand::Reg(r)) => qword(inst, *r),
1393 _ => Err(EmitError::Mir(MirError::OperandShape { inst, index })),
1394 }
1395 };
1396 let (t, i) = (reg(0)?, reg(1)?);
1397 if t == i {
1398 return Err(EmitError::Constraint { inst });
1399 }
1400 let table = self.asm.label();
1401 let start = self.table_targets.len();
1402 for (index, op) in ops.iter().enumerate().skip(2) {
1403 let b = match op {
1404 Operand::Block(call) => call.block(),
1405 _ => return Err(EmitError::Mir(MirError::OperandShape { inst, index })),
1406 };
1407 let l = self
1408 .labels
1409 .get(b.index())
1410 .copied()
1411 .ok_or(EmitError::Mir(MirError::UnknownBlock(b)))?;
1412 self.table_targets.push(l);
1413 }
1414 self.tables.push(PendingTable {
1415 at: table,
1416 targets: start..self.table_targets.len(),
1417 });
1418 ctx.tables += 1;
1419 let lea = IInst::new(M::Lea, [IOp::Reg(t), IOp::Mem(IMem::rip(Size::Qword, 0))]);
1422 self.asm
1423 .emit_rip(&lea, table)
1424 .map_err(|e| asm_error(Some(inst), e))?;
1425 let entry = IMem::new(Size::Qword, t).with_index(i, IScale::X8);
1426 self.emit(
1427 Some(inst),
1428 &IInst::new(M::Add, [IOp::Reg(t), IOp::Mem(entry)]),
1429 )?;
1430 self.emit(Some(inst), &IInst::new(M::Jmp, [IOp::Reg(t)]))
1431 }
1432 Enc::Call => {
1433 match ops.first() {
1434 Some(Operand::Symbol(s)) => {
1435 let name = f
1436 .symbol_name(*s)
1437 .ok_or(EmitError::Mir(MirError::UnknownSymbol(*s)))?;
1438 self.branch_to_symbol(inst, name, false)?;
1439 }
1440 Some(op) => {
1441 let target = self.operand(ctx, inst, op, Size::Qword)?;
1442 self.emit(Some(inst), &IInst::new(M::Call, [target]))?;
1443 }
1444 None => return Err(EmitError::Mir(MirError::OperandShape { inst, index: 0 })),
1445 }
1446 if let Some(site) = f.call_site(inst) {
1447 let ret = self.asm.label();
1450 self.asm.bind(ret).map_err(EmitError::Asm)?;
1451 let gc_start = self.gc_offsets.len();
1452 for &slot in site.gc_slots() {
1453 let off = ctx
1454 .frame
1455 .slot_offset(slot)
1456 .ok_or(EmitError::Mir(MirError::UnknownSlot(slot)))?;
1457 self.gc_offsets.push(off);
1458 }
1459 let landing = match site.landing_pad() {
1460 Some(b) => Some(
1461 self.labels
1462 .get(b.index())
1463 .copied()
1464 .ok_or(EmitError::Mir(MirError::UnknownBlock(b)))?,
1465 ),
1466 None => None,
1467 };
1468 self.call_sites.push(PendingSite {
1469 ret,
1470 gc: gc_start..self.gc_offsets.len(),
1471 landing,
1472 });
1473 }
1474 Ok(())
1475 }
1476 Enc::MovImm64 => match (
1477 ops.first().and_then(Operand::reg).and_then(Reg::as_phys),
1478 ops.get(1),
1479 ) {
1480 (Some(d), Some(Operand::Imm(v))) => self.mov_imm(Some(inst), d, *v),
1481 _ => Err(EmitError::Mir(MirError::OperandShape { inst, index: 0 })),
1482 },
1483 Enc::Raw(bytes) => self.asm.bytes(bytes).map_err(|e| asm_error(Some(inst), e)),
1484 Enc::Isa {
1485 fam,
1486 ops: srcs,
1487 cond,
1488 } => {
1489 let mnemonic = match fam {
1490 Fam::Plain(m) => m,
1491 Fam::Setcc => M::Setcc(cond_operand(ops, usize::from(cond), inst)?),
1492 Fam::Cmovcc => M::Cmovcc(cond_operand(ops, usize::from(cond), inst)?),
1493 };
1494 let mut out = [IOp::Imm(0); 3];
1495 let mut rip = None;
1496 for (k, src) in srcs.iter().enumerate().take(3) {
1497 out[k] = match *src {
1498 Src::Fixed(r) => IOp::Reg(r),
1499 Src::Op(n, size) => match ops.get(usize::from(n)) {
1500 Some(Operand::Symbol(s)) => {
1501 rip = Some(*s);
1502 IOp::Mem(IMem::rip(size, 0))
1503 }
1504 Some(op) => self.operand(ctx, inst, op, size)?,
1505 None => {
1506 return Err(EmitError::Mir(MirError::OperandShape {
1507 inst,
1508 index: usize::from(n),
1509 }));
1510 }
1511 },
1512 };
1513 }
1514 let i = IInst::from_slice(mnemonic, &out[..srcs.len().min(3)])
1515 .ok_or(EmitError::Unencodable { inst: Some(inst) })?;
1516 match rip {
1517 Some(s) => {
1518 let name = f
1519 .symbol_name(s)
1520 .ok_or(EmitError::Mir(MirError::UnknownSymbol(s)))?;
1521 if self.is_external(name) {
1522 let encoded = isa_lang::x86_64::encode(&i).map_err(|error| {
1526 EmitError::Encode {
1527 inst: Some(inst),
1528 error,
1529 }
1530 })?;
1531 return self.external_ref(
1532 Some(inst),
1533 encoded.as_bytes(),
1534 RelocKind::Pc32,
1535 name,
1536 );
1537 }
1538 let label = self.label_for(name);
1539 self.asm
1540 .emit_rip(&i, label)
1541 .map_err(|e| asm_error(Some(inst), e))
1542 }
1543 None => self.emit(Some(inst), &i),
1544 }
1545 }
1546 }
1547 }
1548
1549 fn jmp(&mut self, block: Block) -> Result<(), EmitError> {
1550 let label = self
1551 .labels
1552 .get(block.index())
1553 .copied()
1554 .ok_or(EmitError::Mir(MirError::UnknownBlock(block)))?;
1555 self.asm.jmp(label).map_err(EmitError::Asm)
1556 }
1557
1558 fn jcc(&mut self, cond: ICond, block: Block) -> Result<(), EmitError> {
1559 let label = self
1560 .labels
1561 .get(block.index())
1562 .copied()
1563 .ok_or(EmitError::Mir(MirError::UnknownBlock(block)))?;
1564 self.asm.jcc(cond, label).map_err(EmitError::Asm)
1565 }
1566
1567 fn operand(
1568 &self,
1569 ctx: &Ctx<'_>,
1570 inst: Inst,
1571 op: &Operand,
1572 size: Size,
1573 ) -> Result<IOp, EmitError> {
1574 Ok(match *op {
1575 Operand::Reg(r) => {
1576 let p = r.as_phys().ok_or(EmitError::VirtualRegister {
1577 block: ctx.f.inst_block(inst).unwrap_or(Block::new(0)),
1578 inst: Some(inst),
1579 })?;
1580 ireg(Some(inst), p, size)?
1581 }
1582 Operand::Imm(v) => IOp::Imm(v),
1583 Operand::Mem(m) => IOp::Mem(mem(inst, &m, size)?),
1584 Operand::Slot { slot, offset } => IOp::Mem(slot_mem(ctx.frame, slot, offset, size)?),
1585 _ => return Err(EmitError::Unencodable { inst: Some(inst) }),
1586 })
1587 }
1588}
1589
1590fn asm_error(inst: Option<Inst>, e: AsmError) -> EmitError {
1591 match e {
1592 AsmError::Encode(error) => EmitError::Encode { inst, error },
1593 other => EmitError::Asm(other),
1594 }
1595}
1596
1597fn ireg(inst: Option<Inst>, p: PReg, size: Size) -> Result<IOp, EmitError> {
1599 let info = REGS.get(p.index()).ok_or(EmitError::Unencodable { inst })?;
1600 if info.class() != GPR {
1601 return Err(EmitError::Unencodable { inst });
1602 }
1603 IReg::new(size, info.hw())
1604 .map(IOp::Reg)
1605 .ok_or(EmitError::Unencodable { inst })
1606}
1607
1608fn qword(inst: Inst, r: Reg) -> Result<IReg, EmitError> {
1609 let p = r
1610 .as_phys()
1611 .ok_or(EmitError::Unencodable { inst: Some(inst) })?;
1612 match ireg(Some(inst), p, Size::Qword)? {
1613 IOp::Reg(r) => Ok(r),
1614 _ => Err(EmitError::Unencodable { inst: Some(inst) }),
1615 }
1616}
1617
1618fn mem(inst: Inst, m: &Mem, size: Size) -> Result<IMem, EmitError> {
1619 let mut out = match m.base_reg() {
1620 Some(b) => IMem::new(size, qword(inst, b)?),
1621 None => IMem::absolute(size, 0),
1622 };
1623 if let Some((i, s)) = m.index_reg() {
1624 let scale = match s {
1625 Scale::X1 => IScale::X1,
1626 Scale::X2 => IScale::X2,
1627 Scale::X4 => IScale::X4,
1628 Scale::X8 => IScale::X8,
1629 };
1630 out = out.with_index(qword(inst, i)?, scale);
1631 }
1632 Ok(out.with_disp(m.disp()))
1633}
1634
1635fn slot_mem(
1636 frame: &FrameLayout,
1637 slot: StackSlot,
1638 offset: i32,
1639 size: Size,
1640) -> Result<IMem, EmitError> {
1641 let base = frame
1642 .slot_offset(slot)
1643 .ok_or(EmitError::Mir(MirError::UnknownSlot(slot)))?;
1644 let disp = base
1645 .checked_add(offset)
1646 .ok_or(EmitError::Frame(FrameError::TooLarge))?;
1647 Ok(IMem::new(size, IReg::RSP).with_disp(disp))
1648}
1649
1650fn block_operand(ops: &[Operand], index: usize, inst: Inst) -> Result<BlockCall, EmitError> {
1651 match ops.get(index) {
1652 Some(Operand::Block(c)) => Ok(*c),
1653 _ => Err(EmitError::Mir(MirError::OperandShape { inst, index })),
1654 }
1655}
1656
1657fn cond_operand(ops: &[Operand], index: usize, inst: Inst) -> Result<ICond, EmitError> {
1658 match ops.get(index) {
1659 Some(Operand::Cond(c)) => {
1660 ICond::from_code(c.code()).ok_or(EmitError::Mir(MirError::OperandShape { inst, index }))
1661 }
1662 _ => Err(EmitError::Mir(MirError::OperandShape { inst, index })),
1663 }
1664}
1665
1666fn to_move(f: &Function, dst: Operand, src: Operand) -> Result<Move, EmitError> {
1669 let class_of = |op: &Operand| op.reg().and_then(|r| f.reg_class(r));
1670 let class = class_of(&dst)
1671 .or_else(|| class_of(&src))
1672 .unwrap_or(f.target().addr_class());
1673 let loc = |op: Operand| -> Option<Loc> {
1674 match op {
1675 Operand::Reg(r) => r.as_phys().map(Loc::Reg),
1676 Operand::Slot { slot, offset: 0 } => Some(Loc::Slot(slot)),
1677 _ => None,
1678 }
1679 };
1680 let bad = EmitError::Unencodable { inst: None };
1681 let d = loc(dst).ok_or(bad.clone())?;
1682 let s = match src {
1683 Operand::Imm(v) => Source::Imm(v),
1684 other => Source::Loc(loc(other).ok_or(bad)?),
1685 };
1686 Ok(Move {
1687 dst: d,
1688 src: s,
1689 class,
1690 })
1691}
1692
1693fn probe_scratch(f: &Function) -> Result<PReg, EmitError> {
1696 let params = f.entry().map_or(&[][..], |e| f.params(e));
1697 let taken = |r: PReg| params.iter().any(|p| p.reg() == Some(r.into()));
1698 [R11, R10]
1699 .into_iter()
1700 .find(|&r| !taken(r))
1701 .ok_or(EmitError::ProbeScratch)
1702}
1703
1704fn check_physical(f: &Function) -> Result<(), EmitError> {
1707 let target = f.target();
1708 let reserved = f.reserved();
1709 for block in f.blocks() {
1710 for p in f.params(block) {
1711 if let Some(r) = p.reg() {
1712 match r.as_phys() {
1713 None => return Err(EmitError::VirtualRegister { block, inst: None }),
1714 Some(p) if reserved.contains(p) => {
1715 return Err(EmitError::ReservedWrite { block, inst: None });
1716 }
1717 Some(_) => {}
1718 }
1719 }
1720 }
1721 for &inst in f.insts(block) {
1722 let virt = EmitError::VirtualRegister {
1723 block,
1724 inst: Some(inst),
1725 };
1726 let ops = f.operands(inst);
1727 for op in ops {
1728 let any_virtual = match op {
1729 Operand::Reg(r) => r.is_virtual(),
1730 Operand::Mem(m) => m.regs().any(Reg::is_virtual),
1731 Operand::Block(call) => f
1732 .args(*call)
1733 .iter()
1734 .any(|a| a.reg().is_some_and(Reg::is_virtual)),
1735 _ => false,
1736 };
1737 if any_virtual {
1738 return Err(virt);
1739 }
1740 }
1741 let Some(desc) = f.opcode(inst).and_then(|o| target.inst(o)) else {
1742 continue;
1743 };
1744 let defs = match desc.kind() {
1745 InstKind::Copy => 1,
1746 InstKind::ParallelCopy => ops.len() / 2,
1747 _ => desc.defs(),
1748 };
1749 let reserved_write = ops
1750 .iter()
1751 .take(defs)
1752 .filter_map(|o| o.reg().and_then(Reg::as_phys))
1753 .chain(
1754 f.implicit(inst)
1755 .iter()
1756 .filter(|i| i.kind == ImplicitKind::Def)
1757 .map(|i| i.reg),
1758 )
1759 .chain(desc.implicit_defs().iter().copied())
1760 .any(|p| reserved.contains(p))
1761 || !f.clobbers(inst).intersect(reserved).is_empty();
1762 if reserved_write {
1763 return Err(EmitError::ReservedWrite {
1764 block,
1765 inst: Some(inst),
1766 });
1767 }
1768 for (i, rule) in desc.operands().iter().enumerate() {
1769 if let OperandRule::Tied(d) = rule {
1770 if ops.get(i).and_then(Operand::reg)
1771 != ops.get(usize::from(*d)).and_then(Operand::reg)
1772 {
1773 return Err(EmitError::TiedMismatch { inst });
1774 }
1775 }
1776 }
1777 }
1778 }
1779 Ok(())
1780}