1use crate::ir::{self, types, LibCall, MemFlags, Opcode, Signature, TrapCode, Type};
4use crate::ir::{types::*, ExternalName};
5use crate::isa;
6use crate::isa::{unwind::UnwindInst, x64::inst::*, x64::settings as x64_settings, CallConv};
7use crate::machinst::abi::*;
8use crate::machinst::*;
9use crate::settings;
10use crate::{CodegenError, CodegenResult};
11use alloc::boxed::Box;
12use alloc::vec::Vec;
13use args::*;
14use regalloc2::{PRegSet, VReg};
15use smallvec::{smallvec, SmallVec};
16use std::convert::TryFrom;
17
18static STACK_ARG_RET_SIZE_LIMIT: u32 = 128 * 1024 * 1024;
22
23pub(crate) type X64Callee = Callee<X64ABIMachineSpec>;
25
26pub(crate) type X64Caller = Caller<X64ABIMachineSpec>;
28
29pub struct X64ABIMachineSpec;
31
32impl X64ABIMachineSpec {
33 fn gen_probestack_unroll(insts: &mut SmallInstVec<Inst>, guard_size: u32, probe_count: u32) {
34 insts.reserve(probe_count as usize);
35 for i in 0..probe_count {
36 let offset = (guard_size * (i + 1)) as i64;
37
38 insts.push(Self::gen_store_stack(
41 StackAMode::SPOffset(-offset, I8),
42 regs::rsp(),
43 I32,
44 ));
45 }
46 }
47 fn gen_probestack_loop(insts: &mut SmallInstVec<Inst>, frame_size: u32, guard_size: u32) {
48 let tmp = regs::r11();
54 debug_assert!({
55 let real_reg = tmp.to_real_reg().unwrap();
56 !is_callee_save_systemv(real_reg, false) && !is_callee_save_fastcall(real_reg, false)
57 });
58
59 insts.push(Inst::StackProbeLoop {
60 tmp: Writable::from_reg(tmp),
61 frame_size,
62 guard_size,
63 });
64 }
65}
66
67impl IsaFlags for x64_settings::Flags {}
68
69impl ABIMachineSpec for X64ABIMachineSpec {
70 type I = Inst;
71
72 type F = x64_settings::Flags;
73
74 fn word_bits() -> u32 {
75 64
76 }
77
78 fn stack_align(_call_conv: isa::CallConv) -> u32 {
80 16
81 }
82
83 fn compute_arg_locs<'a, I>(
84 call_conv: isa::CallConv,
85 flags: &settings::Flags,
86 params: I,
87 args_or_rets: ArgsOrRets,
88 add_ret_area_ptr: bool,
89 mut args: ArgsAccumulator<'_>,
90 ) -> CodegenResult<(u32, Option<usize>)>
91 where
92 I: IntoIterator<Item = &'a ir::AbiParam>,
93 {
94 let is_fastcall = call_conv.extends_windows_fastcall();
95
96 let mut next_gpr = 0;
97 let mut next_vreg = 0;
98 let mut next_stack: u32 = 0;
99 let mut next_param_idx = 0; if args_or_rets == ArgsOrRets::Args && is_fastcall {
102 next_stack = 32;
108 }
109
110 for param in params {
111 if let ir::ArgumentPurpose::StructArgument(size) = param.purpose {
112 let offset = next_stack as i64;
113 let size = size;
114 assert!(size % 8 == 0, "StructArgument size is not properly aligned");
115 next_stack += size;
116 args.push(ABIArg::StructArg {
117 pointer: None,
118 offset,
119 size: size as u64,
120 purpose: param.purpose,
121 });
122 continue;
123 }
124
125 let (rcs, reg_tys) = Inst::rc_for_type(param.value_type)?;
127
128 if param.value_type.bits() > 64
149 && !param.value_type.is_vector()
150 && !flags.enable_llvm_abi_extensions()
151 {
152 panic!(
153 "i128 args/return values not supported unless LLVM ABI extensions are enabled"
154 );
155 }
156
157 let mut slots = ABIArgSlotVec::new();
158 for (rc, reg_ty) in rcs.iter().zip(reg_tys.iter()) {
159 let intreg = *rc == RegClass::Int;
160 let nextreg = if intreg {
161 match args_or_rets {
162 ArgsOrRets::Args => {
163 get_intreg_for_arg(&call_conv, next_gpr, next_param_idx)
164 }
165 ArgsOrRets::Rets => {
166 get_intreg_for_retval(&call_conv, next_gpr, next_param_idx)
167 }
168 }
169 } else {
170 match args_or_rets {
171 ArgsOrRets::Args => {
172 get_fltreg_for_arg(&call_conv, next_vreg, next_param_idx)
173 }
174 ArgsOrRets::Rets => {
175 get_fltreg_for_retval(&call_conv, next_vreg, next_param_idx)
176 }
177 }
178 };
179 next_param_idx += 1;
180 if let Some(reg) = nextreg {
181 if intreg {
182 next_gpr += 1;
183 } else {
184 next_vreg += 1;
185 }
186 slots.push(ABIArgSlot::Reg {
187 reg: reg.to_real_reg().unwrap(),
188 ty: *reg_ty,
189 extension: param.extension,
190 });
191 } else {
192 let size = reg_ty.bits() / 8;
201 let size = if args_or_rets == ArgsOrRets::Rets && call_conv.extends_wasmtime() {
202 size
203 } else {
204 std::cmp::max(size, 8)
205 };
206 debug_assert!(size.is_power_of_two());
208 next_stack = align_to(next_stack, size);
209 slots.push(ABIArgSlot::Stack {
210 offset: next_stack as i64,
211 ty: *reg_ty,
212 extension: param.extension,
213 });
214 next_stack += size;
215 }
216 }
217
218 args.push(ABIArg::Slots {
219 slots,
220 purpose: param.purpose,
221 });
222 }
223
224 let extra_arg = if add_ret_area_ptr {
225 debug_assert!(args_or_rets == ArgsOrRets::Args);
226 if let Some(reg) = get_intreg_for_arg(&call_conv, next_gpr, next_param_idx) {
227 args.push(ABIArg::reg(
228 reg.to_real_reg().unwrap(),
229 types::I64,
230 ir::ArgumentExtension::None,
231 ir::ArgumentPurpose::Normal,
232 ));
233 } else {
234 args.push(ABIArg::stack(
235 next_stack as i64,
236 types::I64,
237 ir::ArgumentExtension::None,
238 ir::ArgumentPurpose::Normal,
239 ));
240 next_stack += 8;
241 }
242 Some(args.args().len() - 1)
243 } else {
244 None
245 };
246
247 next_stack = align_to(next_stack, 16);
248
249 if next_stack > STACK_ARG_RET_SIZE_LIMIT {
251 return Err(CodegenError::ImplLimitExceeded);
252 }
253
254 Ok((next_stack, extra_arg))
255 }
256
257 fn fp_to_arg_offset(_call_conv: isa::CallConv, _flags: &settings::Flags) -> i64 {
258 16 }
260
261 fn gen_load_stack(mem: StackAMode, into_reg: Writable<Reg>, ty: Type) -> Self::I {
262 let ty = match ty {
265 types::I8 | types::I16 | types::I32 => types::I64,
266 _ => ty,
267 };
268 Inst::load(ty, mem, into_reg, ExtKind::None)
269 }
270
271 fn gen_store_stack(mem: StackAMode, from_reg: Reg, ty: Type) -> Self::I {
272 Inst::store(ty, from_reg, mem)
273 }
274
275 fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Self::I {
276 Inst::gen_move(to_reg, from_reg, ty)
277 }
278
279 fn gen_extend(
281 to_reg: Writable<Reg>,
282 from_reg: Reg,
283 is_signed: bool,
284 from_bits: u8,
285 to_bits: u8,
286 ) -> Self::I {
287 let ext_mode = ExtMode::new(from_bits as u16, to_bits as u16)
288 .unwrap_or_else(|| panic!("invalid extension: {} -> {}", from_bits, to_bits));
289 if is_signed {
290 Inst::movsx_rm_r(ext_mode, RegMem::reg(from_reg), to_reg)
291 } else {
292 Inst::movzx_rm_r(ext_mode, RegMem::reg(from_reg), to_reg)
293 }
294 }
295
296 fn gen_args(_isa_flags: &x64_settings::Flags, args: Vec<ArgPair>) -> Inst {
297 Inst::Args { args }
298 }
299
300 fn gen_ret(
301 _setup_frame: bool,
302 _isa_flags: &x64_settings::Flags,
303 rets: Vec<RetPair>,
304 ) -> Self::I {
305 Inst::ret(rets)
306 }
307
308 fn gen_add_imm(into_reg: Writable<Reg>, from_reg: Reg, imm: u32) -> SmallInstVec<Self::I> {
309 let mut ret = SmallVec::new();
310 if from_reg != into_reg.to_reg() {
311 ret.push(Inst::gen_move(into_reg, from_reg, I64));
312 }
313 ret.push(Inst::alu_rmi_r(
314 OperandSize::Size64,
315 AluRmiROpcode::Add,
316 RegMemImm::imm(imm),
317 into_reg,
318 ));
319 ret
320 }
321
322 fn gen_stack_lower_bound_trap(limit_reg: Reg) -> SmallInstVec<Self::I> {
323 smallvec![
324 Inst::cmp_rmi_r(OperandSize::Size64, RegMemImm::reg(regs::rsp()), limit_reg),
325 Inst::TrapIf {
326 cc: CC::NBE,
328 trap_code: TrapCode::StackOverflow,
329 },
330 ]
331 }
332
333 fn gen_get_stack_addr(mem: StackAMode, into_reg: Writable<Reg>, _ty: Type) -> Self::I {
334 let mem: SyntheticAmode = mem.into();
335 Inst::lea(mem, into_reg)
336 }
337
338 fn get_stacklimit_reg() -> Reg {
339 debug_assert!(!is_callee_save_systemv(
340 regs::r10().to_real_reg().unwrap(),
341 false
342 ));
343
344 regs::r10()
347 }
348
349 fn gen_load_base_offset(into_reg: Writable<Reg>, base: Reg, offset: i32, ty: Type) -> Self::I {
350 assert_eq!(ty, I64);
352 let simm32 = offset as u32;
353 let mem = Amode::imm_reg(simm32, base);
354 Inst::load(ty, mem, into_reg, ExtKind::None)
355 }
356
357 fn gen_store_base_offset(base: Reg, offset: i32, from_reg: Reg, ty: Type) -> Self::I {
358 let simm32 = offset as u32;
359 let mem = Amode::imm_reg(simm32, base);
360 Inst::store(ty, from_reg, mem)
361 }
362
363 fn gen_sp_reg_adjust(amount: i32) -> SmallInstVec<Self::I> {
364 let (alu_op, amount) = if amount >= 0 {
365 (AluRmiROpcode::Add, amount)
366 } else {
367 (AluRmiROpcode::Sub, -amount)
368 };
369
370 let amount = amount as u32;
371
372 smallvec![Inst::alu_rmi_r(
373 OperandSize::Size64,
374 alu_op,
375 RegMemImm::imm(amount),
376 Writable::from_reg(regs::rsp()),
377 )]
378 }
379
380 fn gen_nominal_sp_adj(offset: i32) -> Self::I {
381 Inst::VirtualSPOffsetAdj {
382 offset: offset as i64,
383 }
384 }
385
386 fn gen_prologue_frame_setup(flags: &settings::Flags) -> SmallInstVec<Self::I> {
387 let r_rsp = regs::rsp();
388 let r_rbp = regs::rbp();
389 let w_rbp = Writable::from_reg(r_rbp);
390 let mut insts = SmallVec::new();
391 insts.push(Inst::push64(RegMemImm::reg(r_rbp)));
394
395 if flags.unwind_info() {
396 insts.push(Inst::Unwind {
397 inst: UnwindInst::PushFrameRegs {
398 offset_upward_to_caller_sp: 16, },
400 });
401 }
402
403 insts.push(Inst::mov_r_r(OperandSize::Size64, r_rsp, w_rbp));
406 insts
407 }
408
409 fn gen_epilogue_frame_restore(_: &settings::Flags) -> SmallInstVec<Self::I> {
410 let mut insts = SmallVec::new();
411 insts.push(Inst::mov_r_r(
413 OperandSize::Size64,
414 regs::rbp(),
415 Writable::from_reg(regs::rsp()),
416 ));
417 insts.push(Inst::pop64(Writable::from_reg(regs::rbp())));
419 insts
420 }
421
422 fn gen_probestack(insts: &mut SmallInstVec<Self::I>, frame_size: u32) {
423 insts.push(Inst::imm(
424 OperandSize::Size32,
425 frame_size as u64,
426 Writable::from_reg(regs::rax()),
427 ));
428 insts.push(Inst::CallKnown {
429 dest: ExternalName::LibCall(LibCall::Probestack),
430 info: Box::new(CallInfo {
431 uses: smallvec![],
434 defs: smallvec![],
435 clobbers: PRegSet::empty(),
436 opcode: Opcode::Call,
437 }),
438 });
439 }
440
441 fn gen_inline_probestack(insts: &mut SmallInstVec<Self::I>, frame_size: u32, guard_size: u32) {
442 const PROBE_MAX_UNROLL: u32 = 5;
447
448 let probe_count = align_to(frame_size, guard_size) / guard_size;
450
451 if probe_count <= PROBE_MAX_UNROLL {
452 Self::gen_probestack_unroll(insts, guard_size, probe_count)
453 } else {
454 Self::gen_probestack_loop(insts, frame_size, guard_size)
455 }
456 }
457
458 fn gen_clobber_save(
459 _call_conv: isa::CallConv,
460 setup_frame: bool,
461 flags: &settings::Flags,
462 clobbered_callee_saves: &[Writable<RealReg>],
463 fixed_frame_storage_size: u32,
464 _outgoing_args_size: u32,
465 ) -> (u64, SmallVec<[Self::I; 16]>) {
466 let mut insts = SmallVec::new();
467 let clobbered_size = compute_clobber_size(&clobbered_callee_saves);
468
469 if flags.unwind_info() && setup_frame {
470 insts.push(Inst::Unwind {
475 inst: UnwindInst::DefineNewFrame {
476 offset_downward_to_clobbers: clobbered_size,
477 offset_upward_to_caller_sp: 16, },
479 });
480 }
481
482 let stack_size = fixed_frame_storage_size + clobbered_size;
485 if stack_size > 0 {
486 insts.push(Inst::alu_rmi_r(
487 OperandSize::Size64,
488 AluRmiROpcode::Sub,
489 RegMemImm::imm(stack_size),
490 Writable::from_reg(regs::rsp()),
491 ));
492 }
493 let mut cur_offset = fixed_frame_storage_size;
496 for reg in clobbered_callee_saves {
497 let r_reg = reg.to_reg();
498 let off = cur_offset;
499 match r_reg.class() {
500 RegClass::Int => {
501 insts.push(Inst::store(
502 types::I64,
503 r_reg.into(),
504 Amode::imm_reg(cur_offset, regs::rsp()),
505 ));
506 cur_offset += 8;
507 }
508 RegClass::Float => {
509 cur_offset = align_to(cur_offset, 16);
510 insts.push(Inst::store(
511 types::I8X16,
512 r_reg.into(),
513 Amode::imm_reg(cur_offset, regs::rsp()),
514 ));
515 cur_offset += 16;
516 }
517 };
518 if flags.unwind_info() {
519 insts.push(Inst::Unwind {
520 inst: UnwindInst::SaveReg {
521 clobber_offset: off - fixed_frame_storage_size,
522 reg: r_reg,
523 },
524 });
525 }
526 }
527
528 (clobbered_size as u64, insts)
529 }
530
531 fn gen_clobber_restore(
532 call_conv: isa::CallConv,
533 sig: &Signature,
534 flags: &settings::Flags,
535 clobbers: &[Writable<RealReg>],
536 fixed_frame_storage_size: u32,
537 _outgoing_args_size: u32,
538 ) -> SmallVec<[Self::I; 16]> {
539 let mut insts = SmallVec::new();
540
541 let clobbered_callee_saves =
542 Self::get_clobbered_callee_saves(call_conv, flags, sig, clobbers);
543 let stack_size = fixed_frame_storage_size + compute_clobber_size(&clobbered_callee_saves);
544
545 let mut cur_offset = fixed_frame_storage_size;
549 for reg in &clobbered_callee_saves {
550 let rreg = reg.to_reg();
551 match rreg.class() {
552 RegClass::Int => {
553 insts.push(Inst::mov64_m_r(
554 Amode::imm_reg(cur_offset, regs::rsp()),
555 Writable::from_reg(rreg.into()),
556 ));
557 cur_offset += 8;
558 }
559 RegClass::Float => {
560 cur_offset = align_to(cur_offset, 16);
561 insts.push(Inst::load(
562 types::I8X16,
563 Amode::imm_reg(cur_offset, regs::rsp()),
564 Writable::from_reg(rreg.into()),
565 ExtKind::None,
566 ));
567 cur_offset += 16;
568 }
569 }
570 }
571 if stack_size > 0 {
573 insts.push(Inst::alu_rmi_r(
574 OperandSize::Size64,
575 AluRmiROpcode::Add,
576 RegMemImm::imm(stack_size),
577 Writable::from_reg(regs::rsp()),
578 ));
579 }
580
581 insts
582 }
583
584 fn gen_call(
586 dest: &CallDest,
587 uses: CallArgList,
588 defs: CallRetList,
589 clobbers: PRegSet,
590 opcode: ir::Opcode,
591 tmp: Writable<Reg>,
592 _callee_conv: isa::CallConv,
593 _caller_conv: isa::CallConv,
594 ) -> SmallVec<[Self::I; 2]> {
595 let mut insts = SmallVec::new();
596 match dest {
597 &CallDest::ExtName(ref name, RelocDistance::Near) => {
598 insts.push(Inst::call_known(name.clone(), uses, defs, clobbers, opcode));
599 }
600 &CallDest::ExtName(ref name, RelocDistance::Far) => {
601 insts.push(Inst::LoadExtName {
602 dst: tmp,
603 name: Box::new(name.clone()),
604 offset: 0,
605 });
606 insts.push(Inst::call_unknown(
607 RegMem::reg(tmp.to_reg()),
608 uses,
609 defs,
610 clobbers,
611 opcode,
612 ));
613 }
614 &CallDest::Reg(reg) => {
615 insts.push(Inst::call_unknown(
616 RegMem::reg(reg),
617 uses,
618 defs,
619 clobbers,
620 opcode,
621 ));
622 }
623 }
624 insts
625 }
626
627 fn gen_memcpy<F: FnMut(Type) -> Writable<Reg>>(
628 call_conv: isa::CallConv,
629 dst: Reg,
630 src: Reg,
631 size: usize,
632 mut alloc_tmp: F,
633 ) -> SmallVec<[Self::I; 8]> {
634 let mut insts = SmallVec::new();
635 let arg0 = get_intreg_for_arg(&call_conv, 0, 0).unwrap();
636 let arg1 = get_intreg_for_arg(&call_conv, 1, 1).unwrap();
637 let arg2 = get_intreg_for_arg(&call_conv, 2, 2).unwrap();
638 let temp = alloc_tmp(Self::word_type());
639 let temp2 = alloc_tmp(Self::word_type());
640 insts.push(Inst::imm(OperandSize::Size64, size as u64, temp));
641 insts.push(Inst::LoadExtName {
645 dst: temp2,
646 name: Box::new(ExternalName::LibCall(LibCall::Memcpy)),
647 offset: 0,
648 });
649 insts.push(Inst::call_unknown(
650 RegMem::reg(temp2.to_reg()),
651 smallvec![
653 CallArgPair {
654 vreg: dst,
655 preg: arg0
656 },
657 CallArgPair {
658 vreg: src,
659 preg: arg1
660 },
661 CallArgPair {
662 vreg: temp.to_reg(),
663 preg: arg2
664 },
665 ],
666 smallvec![],
667 Self::get_regs_clobbered_by_call(call_conv),
668 Opcode::Call,
669 ));
670 insts
671 }
672
673 fn get_number_of_spillslots_for_value(rc: RegClass, vector_scale: u32) -> u32 {
674 match rc {
676 RegClass::Int => 1,
677 RegClass::Float => vector_scale / 8,
678 }
679 }
680
681 fn get_virtual_sp_offset_from_state(s: &<Self::I as MachInstEmit>::State) -> i64 {
682 s.virtual_sp_offset
683 }
684
685 fn get_nominal_sp_to_fp(s: &<Self::I as MachInstEmit>::State) -> i64 {
686 s.nominal_sp_to_fp
687 }
688
689 fn get_regs_clobbered_by_call(call_conv_of_callee: isa::CallConv) -> PRegSet {
690 if call_conv_of_callee.extends_windows_fastcall() {
691 WINDOWS_CLOBBERS
692 } else {
693 SYSV_CLOBBERS
694 }
695 }
696
697 fn get_ext_mode(
698 _call_conv: isa::CallConv,
699 _specified: ir::ArgumentExtension,
700 ) -> ir::ArgumentExtension {
701 ir::ArgumentExtension::None
702 }
703
704 fn get_clobbered_callee_saves(
705 call_conv: CallConv,
706 flags: &settings::Flags,
707 _sig: &Signature,
708 regs: &[Writable<RealReg>],
709 ) -> Vec<Writable<RealReg>> {
710 let mut regs: Vec<Writable<RealReg>> = match call_conv {
711 CallConv::Tail => unimplemented!(),
712 CallConv::Fast | CallConv::Cold | CallConv::SystemV | CallConv::WasmtimeSystemV => regs
713 .iter()
714 .cloned()
715 .filter(|r| is_callee_save_systemv(r.to_reg(), flags.enable_pinned_reg()))
716 .collect(),
717 CallConv::WindowsFastcall | CallConv::WasmtimeFastcall => regs
718 .iter()
719 .cloned()
720 .filter(|r| is_callee_save_fastcall(r.to_reg(), flags.enable_pinned_reg()))
721 .collect(),
722 CallConv::Probestack => todo!("probestack?"),
723 CallConv::AppleAarch64 | CallConv::WasmtimeAppleAarch64 => unreachable!(),
724 };
725 regs.sort_unstable_by_key(|r| VReg::from(r.to_reg()).vreg());
728 regs
729 }
730
731 fn is_frame_setup_needed(
732 _is_leaf: bool,
733 _stack_args_size: u32,
734 _num_clobbered_callee_saves: usize,
735 _frame_storage_size: u32,
736 ) -> bool {
737 true
738 }
739}
740
741impl From<StackAMode> for SyntheticAmode {
742 fn from(amode: StackAMode) -> Self {
743 match amode {
746 StackAMode::FPOffset(off, _ty) => {
747 let off = i32::try_from(off)
748 .expect("Offset in FPOffset is greater than 2GB; should hit impl limit first");
749 let simm32 = off as u32;
750 SyntheticAmode::Real(Amode::ImmReg {
751 simm32,
752 base: regs::rbp(),
753 flags: MemFlags::trusted(),
754 })
755 }
756 StackAMode::NominalSPOffset(off, _ty) => {
757 let off = i32::try_from(off).expect(
758 "Offset in NominalSPOffset is greater than 2GB; should hit impl limit first",
759 );
760 let simm32 = off as u32;
761 SyntheticAmode::nominal_sp_offset(simm32)
762 }
763 StackAMode::SPOffset(off, _ty) => {
764 let off = i32::try_from(off)
765 .expect("Offset in SPOffset is greater than 2GB; should hit impl limit first");
766 let simm32 = off as u32;
767 SyntheticAmode::Real(Amode::ImmReg {
768 simm32,
769 base: regs::rsp(),
770 flags: MemFlags::trusted(),
771 })
772 }
773 }
774 }
775}
776
777fn get_intreg_for_arg(call_conv: &CallConv, idx: usize, arg_idx: usize) -> Option<Reg> {
778 let is_fastcall = call_conv.extends_windows_fastcall();
779
780 let i = if is_fastcall { arg_idx } else { idx };
783 match (i, is_fastcall) {
784 (0, false) => Some(regs::rdi()),
785 (1, false) => Some(regs::rsi()),
786 (2, false) => Some(regs::rdx()),
787 (3, false) => Some(regs::rcx()),
788 (4, false) => Some(regs::r8()),
789 (5, false) => Some(regs::r9()),
790 (0, true) => Some(regs::rcx()),
791 (1, true) => Some(regs::rdx()),
792 (2, true) => Some(regs::r8()),
793 (3, true) => Some(regs::r9()),
794 _ => None,
795 }
796}
797
798fn get_fltreg_for_arg(call_conv: &CallConv, idx: usize, arg_idx: usize) -> Option<Reg> {
799 let is_fastcall = call_conv.extends_windows_fastcall();
800
801 let i = if is_fastcall { arg_idx } else { idx };
804 match (i, is_fastcall) {
805 (0, false) => Some(regs::xmm0()),
806 (1, false) => Some(regs::xmm1()),
807 (2, false) => Some(regs::xmm2()),
808 (3, false) => Some(regs::xmm3()),
809 (4, false) => Some(regs::xmm4()),
810 (5, false) => Some(regs::xmm5()),
811 (6, false) => Some(regs::xmm6()),
812 (7, false) => Some(regs::xmm7()),
813 (0, true) => Some(regs::xmm0()),
814 (1, true) => Some(regs::xmm1()),
815 (2, true) => Some(regs::xmm2()),
816 (3, true) => Some(regs::xmm3()),
817 _ => None,
818 }
819}
820
821fn get_intreg_for_retval(
822 call_conv: &CallConv,
823 intreg_idx: usize,
824 retval_idx: usize,
825) -> Option<Reg> {
826 match call_conv {
827 CallConv::Tail => unimplemented!(),
828 CallConv::Fast | CallConv::Cold | CallConv::SystemV => match intreg_idx {
829 0 => Some(regs::rax()),
830 1 => Some(regs::rdx()),
831 _ => None,
832 },
833 CallConv::WasmtimeSystemV | CallConv::WasmtimeFastcall => {
834 if intreg_idx == 0 && retval_idx == 0 {
835 Some(regs::rax())
836 } else {
837 None
838 }
839 }
840 CallConv::WindowsFastcall => match intreg_idx {
841 0 => Some(regs::rax()),
842 1 => Some(regs::rdx()), _ => None,
844 },
845 CallConv::Probestack => todo!(),
846 CallConv::AppleAarch64 | CallConv::WasmtimeAppleAarch64 => unreachable!(),
847 }
848}
849
850fn get_fltreg_for_retval(
851 call_conv: &CallConv,
852 fltreg_idx: usize,
853 retval_idx: usize,
854) -> Option<Reg> {
855 match call_conv {
856 CallConv::Tail => unimplemented!(),
857 CallConv::Fast | CallConv::Cold | CallConv::SystemV => match fltreg_idx {
858 0 => Some(regs::xmm0()),
859 1 => Some(regs::xmm1()),
860 _ => None,
861 },
862 CallConv::WasmtimeFastcall | CallConv::WasmtimeSystemV => {
863 if fltreg_idx == 0 && retval_idx == 0 {
864 Some(regs::xmm0())
865 } else {
866 None
867 }
868 }
869 CallConv::WindowsFastcall => match fltreg_idx {
870 0 => Some(regs::xmm0()),
871 _ => None,
872 },
873 CallConv::Probestack => todo!(),
874 CallConv::AppleAarch64 | CallConv::WasmtimeAppleAarch64 => unreachable!(),
875 }
876}
877
878fn is_callee_save_systemv(r: RealReg, enable_pinned_reg: bool) -> bool {
879 use regs::*;
880 match r.class() {
881 RegClass::Int => match r.hw_enc() {
882 ENC_RBX | ENC_RBP | ENC_R12 | ENC_R13 | ENC_R14 => true,
883 ENC_R15 => !enable_pinned_reg,
887 _ => false,
888 },
889 RegClass::Float => false,
890 }
891}
892
893fn is_callee_save_fastcall(r: RealReg, enable_pinned_reg: bool) -> bool {
894 use regs::*;
895 match r.class() {
896 RegClass::Int => match r.hw_enc() {
897 ENC_RBX | ENC_RBP | ENC_RSI | ENC_RDI | ENC_R12 | ENC_R13 | ENC_R14 => true,
898 ENC_R15 => !enable_pinned_reg,
900 _ => false,
901 },
902 RegClass::Float => match r.hw_enc() {
903 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 => true,
904 _ => false,
905 },
906 }
907}
908
909fn compute_clobber_size(clobbers: &[Writable<RealReg>]) -> u32 {
910 let mut clobbered_size = 0;
911 for reg in clobbers {
912 match reg.to_reg().class() {
913 RegClass::Int => {
914 clobbered_size += 8;
915 }
916 RegClass::Float => {
917 clobbered_size = align_to(clobbered_size, 16);
918 clobbered_size += 16;
919 }
920 }
921 }
922 align_to(clobbered_size, 16)
923}
924
925const WINDOWS_CLOBBERS: PRegSet = windows_clobbers();
926const SYSV_CLOBBERS: PRegSet = sysv_clobbers();
927
928const fn windows_clobbers() -> PRegSet {
929 PRegSet::empty()
930 .with(regs::gpr_preg(regs::ENC_RAX))
931 .with(regs::gpr_preg(regs::ENC_RCX))
932 .with(regs::gpr_preg(regs::ENC_RDX))
933 .with(regs::gpr_preg(regs::ENC_R8))
934 .with(regs::gpr_preg(regs::ENC_R9))
935 .with(regs::gpr_preg(regs::ENC_R10))
936 .with(regs::gpr_preg(regs::ENC_R11))
937 .with(regs::fpr_preg(0))
938 .with(regs::fpr_preg(1))
939 .with(regs::fpr_preg(2))
940 .with(regs::fpr_preg(3))
941 .with(regs::fpr_preg(4))
942 .with(regs::fpr_preg(5))
943}
944
945const fn sysv_clobbers() -> PRegSet {
946 PRegSet::empty()
947 .with(regs::gpr_preg(regs::ENC_RAX))
948 .with(regs::gpr_preg(regs::ENC_RCX))
949 .with(regs::gpr_preg(regs::ENC_RDX))
950 .with(regs::gpr_preg(regs::ENC_RSI))
951 .with(regs::gpr_preg(regs::ENC_RDI))
952 .with(regs::gpr_preg(regs::ENC_R8))
953 .with(regs::gpr_preg(regs::ENC_R9))
954 .with(regs::gpr_preg(regs::ENC_R10))
955 .with(regs::gpr_preg(regs::ENC_R11))
956 .with(regs::fpr_preg(0))
957 .with(regs::fpr_preg(1))
958 .with(regs::fpr_preg(2))
959 .with(regs::fpr_preg(3))
960 .with(regs::fpr_preg(4))
961 .with(regs::fpr_preg(5))
962 .with(regs::fpr_preg(6))
963 .with(regs::fpr_preg(7))
964 .with(regs::fpr_preg(8))
965 .with(regs::fpr_preg(9))
966 .with(regs::fpr_preg(10))
967 .with(regs::fpr_preg(11))
968 .with(regs::fpr_preg(12))
969 .with(regs::fpr_preg(13))
970 .with(regs::fpr_preg(14))
971 .with(regs::fpr_preg(15))
972}