cranelift_codegen/isa/pulley_shared/
abi.rs

1//! Implementation of a standard Pulley ABI.
2
3use super::{PulleyFlags, PulleyTargetKind, inst::*};
4use crate::isa::pulley_shared::PointerWidth;
5use crate::{
6    CodegenResult,
7    ir::{self, MemFlags, Signature, types::*},
8    isa,
9    machinst::*,
10    settings,
11};
12use alloc::vec::Vec;
13use core::marker::PhantomData;
14use cranelift_bitset::ScalarBitSet;
15use regalloc2::{MachineEnv, PReg, PRegSet};
16use smallvec::{SmallVec, smallvec};
17use std::borrow::ToOwned;
18use std::sync::OnceLock;
19
20/// Support for the Pulley ABI from the callee side (within a function body).
21pub(crate) type PulleyCallee<P> = Callee<PulleyMachineDeps<P>>;
22
23/// Pulley-specific ABI behavior. This struct just serves as an implementation
24/// point for the trait; it is never actually instantiated.
25pub struct PulleyMachineDeps<P>
26where
27    P: PulleyTargetKind,
28{
29    _phantom: PhantomData<P>,
30}
31
32impl<P> ABIMachineSpec for PulleyMachineDeps<P>
33where
34    P: PulleyTargetKind,
35{
36    type I = InstAndKind<P>;
37    type F = PulleyFlags;
38
39    /// This is the limit for the size of argument and return-value areas on the
40    /// stack. We place a reasonable limit here to avoid integer overflow issues
41    /// with 32-bit arithmetic: for now, 128 MB.
42    const STACK_ARG_RET_SIZE_LIMIT: u32 = 128 * 1024 * 1024;
43
44    fn word_bits() -> u32 {
45        P::pointer_width().bits().into()
46    }
47
48    /// Return required stack alignment in bytes.
49    fn stack_align(_call_conv: isa::CallConv) -> u32 {
50        16
51    }
52
53    fn compute_arg_locs(
54        call_conv: isa::CallConv,
55        flags: &settings::Flags,
56        params: &[ir::AbiParam],
57        args_or_rets: ArgsOrRets,
58        add_ret_area_ptr: bool,
59        mut args: ArgsAccumulator,
60    ) -> CodegenResult<(u32, Option<usize>)> {
61        // NB: make sure this method stays in sync with
62        // `cranelift_pulley::interp::Vm::call`.
63        //
64        // In general we use the first half of all register banks as argument
65        // passing registers because, well, why not for now. Currently the only
66        // exception is x15 which is reserved as a single caller-saved register
67        // not used for arguments. This is used in `ReturnCallIndirect` to hold
68        // the location of where we're jumping to.
69
70        let x_end = 14;
71        let f_end = 15;
72        let v_end = 15;
73
74        let mut next_x_reg = 0;
75        let mut next_f_reg = 0;
76        let mut next_v_reg = 0;
77        let mut next_stack: u32 = 0;
78
79        let ret_area_ptr = if add_ret_area_ptr {
80            debug_assert_eq!(args_or_rets, ArgsOrRets::Args);
81            next_x_reg += 1;
82            Some(ABIArg::reg(
83                x_reg(next_x_reg - 1).to_real_reg().unwrap(),
84                I64,
85                ir::ArgumentExtension::None,
86                ir::ArgumentPurpose::Normal,
87            ))
88        } else {
89            None
90        };
91
92        for param in params {
93            // Find the regclass(es) of the register(s) used to store a value of
94            // this type.
95            let (rcs, reg_tys) = Self::I::rc_for_type(param.value_type)?;
96
97            let mut slots = ABIArgSlotVec::new();
98            for (rc, reg_ty) in rcs.iter().zip(reg_tys.iter()) {
99                let next_reg = if (next_x_reg <= x_end) && *rc == RegClass::Int {
100                    let x = Some(x_reg(next_x_reg));
101                    next_x_reg += 1;
102                    x
103                } else if (next_f_reg <= f_end) && *rc == RegClass::Float {
104                    let f = Some(f_reg(next_f_reg));
105                    next_f_reg += 1;
106                    f
107                } else if (next_v_reg <= v_end) && *rc == RegClass::Vector {
108                    let v = Some(v_reg(next_v_reg));
109                    next_v_reg += 1;
110                    v
111                } else {
112                    None
113                };
114
115                if let Some(reg) = next_reg {
116                    slots.push(ABIArgSlot::Reg {
117                        reg: reg.to_real_reg().unwrap(),
118                        ty: *reg_ty,
119                        extension: param.extension,
120                    });
121                } else {
122                    if args_or_rets == ArgsOrRets::Rets && !flags.enable_multi_ret_implicit_sret() {
123                        return Err(crate::CodegenError::Unsupported(
124                            "Too many return values to fit in registers. \
125                            Use a StructReturn argument instead. (#9510)"
126                                .to_owned(),
127                        ));
128                    }
129
130                    // Compute size and 16-byte stack alignment happens
131                    // separately after all args.
132                    let size = reg_ty.bits() / 8;
133                    let size = std::cmp::max(size, 8);
134
135                    // Align.
136                    debug_assert!(size.is_power_of_two());
137                    next_stack = align_to(next_stack, size);
138
139                    slots.push(ABIArgSlot::Stack {
140                        offset: i64::from(next_stack),
141                        ty: *reg_ty,
142                        extension: param.extension,
143                    });
144
145                    next_stack += size;
146                }
147            }
148
149            args.push(ABIArg::Slots {
150                slots,
151                purpose: param.purpose,
152            });
153        }
154
155        let pos = if let Some(ret_area_ptr) = ret_area_ptr {
156            args.push_non_formal(ret_area_ptr);
157            Some(args.args().len() - 1)
158        } else {
159            None
160        };
161
162        next_stack = align_to(next_stack, Self::stack_align(call_conv));
163
164        Ok((next_stack, pos))
165    }
166
167    fn gen_load_stack(mem: StackAMode, into_reg: Writable<Reg>, ty: Type) -> Self::I {
168        let mut flags = MemFlags::trusted();
169        // Stack loads/stores of vectors always use little-endianess to avoid
170        // implementing a byte-swap of vectors on big-endian platforms.
171        if ty.is_vector() {
172            flags.set_endianness(ir::Endianness::Little);
173        }
174        Inst::gen_load(into_reg, mem.into(), ty, flags).into()
175    }
176
177    fn gen_store_stack(mem: StackAMode, from_reg: Reg, ty: Type) -> Self::I {
178        let mut flags = MemFlags::trusted();
179        // Stack loads/stores of vectors always use little-endianess to avoid
180        // implementing a byte-swap of vectors on big-endian platforms.
181        if ty.is_vector() {
182            flags.set_endianness(ir::Endianness::Little);
183        }
184        Inst::gen_store(mem.into(), from_reg, ty, flags).into()
185    }
186
187    fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Self::I {
188        Self::I::gen_move(to_reg, from_reg, ty)
189    }
190
191    fn gen_extend(
192        dst: Writable<Reg>,
193        src: Reg,
194        signed: bool,
195        from_bits: u8,
196        to_bits: u8,
197    ) -> Self::I {
198        assert!(from_bits < to_bits);
199        let src = XReg::new(src).unwrap();
200        let dst = dst.try_into().unwrap();
201        match (signed, from_bits) {
202            (true, 8) => RawInst::Sext8 { dst, src }.into(),
203            (true, 16) => RawInst::Sext16 { dst, src }.into(),
204            (true, 32) => RawInst::Sext32 { dst, src }.into(),
205            (false, 8) => RawInst::Zext8 { dst, src }.into(),
206            (false, 16) => RawInst::Zext16 { dst, src }.into(),
207            (false, 32) => RawInst::Zext32 { dst, src }.into(),
208            _ => unimplemented!("extend {from_bits} to {to_bits} as signed? {signed}"),
209        }
210    }
211
212    fn get_ext_mode(
213        _call_conv: isa::CallConv,
214        specified: ir::ArgumentExtension,
215    ) -> ir::ArgumentExtension {
216        specified
217    }
218
219    fn gen_args(args: Vec<ArgPair>) -> Self::I {
220        Inst::Args { args }.into()
221    }
222
223    fn gen_rets(rets: Vec<RetPair>) -> Self::I {
224        Inst::Rets { rets }.into()
225    }
226
227    fn get_stacklimit_reg(_call_conv: isa::CallConv) -> Reg {
228        spilltmp_reg()
229    }
230
231    fn gen_add_imm(
232        _call_conv: isa::CallConv,
233        into_reg: Writable<Reg>,
234        from_reg: Reg,
235        imm: u32,
236    ) -> SmallInstVec<Self::I> {
237        let dst = into_reg.try_into().unwrap();
238        let imm = imm as i32;
239        smallvec![
240            RawInst::Xconst32 { dst, imm }.into(),
241            RawInst::Xadd32 {
242                dst,
243                src1: from_reg.try_into().unwrap(),
244                src2: dst.to_reg(),
245            }
246            .into()
247        ]
248    }
249
250    fn gen_stack_lower_bound_trap(_limit_reg: Reg) -> SmallInstVec<Self::I> {
251        unimplemented!("pulley shouldn't need stack bound checks")
252    }
253
254    fn gen_get_stack_addr(mem: StackAMode, dst: Writable<Reg>) -> Self::I {
255        let dst = dst.to_reg();
256        let dst = XReg::new(dst).unwrap();
257        let dst = WritableXReg::from_reg(dst);
258        let mem = mem.into();
259        Inst::LoadAddr { dst, mem }.into()
260    }
261
262    fn gen_load_base_offset(into_reg: Writable<Reg>, base: Reg, offset: i32, ty: Type) -> Self::I {
263        let base = XReg::try_from(base).unwrap();
264        let mem = Amode::RegOffset { base, offset };
265        Inst::gen_load(into_reg, mem, ty, MemFlags::trusted()).into()
266    }
267
268    fn gen_store_base_offset(base: Reg, offset: i32, from_reg: Reg, ty: Type) -> Self::I {
269        let base = XReg::try_from(base).unwrap();
270        let mem = Amode::RegOffset { base, offset };
271        Inst::gen_store(mem, from_reg, ty, MemFlags::trusted()).into()
272    }
273
274    fn gen_sp_reg_adjust(amount: i32) -> SmallInstVec<Self::I> {
275        if amount == 0 {
276            return smallvec![];
277        }
278
279        let inst = if amount < 0 {
280            let amount = amount.checked_neg().unwrap();
281            if let Ok(amt) = u32::try_from(amount) {
282                RawInst::StackAlloc32 { amt }
283            } else {
284                unreachable!()
285            }
286        } else {
287            if let Ok(amt) = u32::try_from(amount) {
288                RawInst::StackFree32 { amt }
289            } else {
290                unreachable!()
291            }
292        };
293        smallvec![inst.into()]
294    }
295
296    /// Generates the entire prologue for the function.
297    ///
298    /// Note that this is different from other backends where it's not spread
299    /// out among a few individual functions. That's because the goal here is to
300    /// generate a single macro-instruction for the entire prologue in the most
301    /// common cases and we don't want to spread the logic over multiple
302    /// functions.
303    ///
304    /// The general machinst methods are split to accommodate stack checks and
305    /// things like stack probes, all of which are empty on Pulley because
306    /// Pulley has its own stack check mechanism.
307    fn gen_prologue_frame_setup(
308        _call_conv: isa::CallConv,
309        _flags: &settings::Flags,
310        _isa_flags: &PulleyFlags,
311        frame_layout: &FrameLayout,
312    ) -> SmallInstVec<Self::I> {
313        let mut insts = SmallVec::new();
314
315        let incoming_args_diff = frame_layout.tail_args_size - frame_layout.incoming_args_size;
316        if incoming_args_diff > 0 {
317            // Decrement SP by the amount of additional incoming argument space
318            // we need
319            insts.extend(Self::gen_sp_reg_adjust(-(incoming_args_diff as i32)));
320        }
321
322        let style = frame_layout.pulley_frame_style();
323
324        match &style {
325            FrameStyle::None => {}
326            FrameStyle::PulleyBasicSetup { frame_size } => {
327                insts.push(RawInst::PushFrame.into());
328                insts.extend(Self::gen_sp_reg_adjust(
329                    -i32::try_from(*frame_size).unwrap(),
330                ));
331            }
332            FrameStyle::PulleySetupAndSaveClobbers {
333                frame_size,
334                saved_by_pulley,
335            } => insts.push(
336                RawInst::PushFrameSave {
337                    amt: *frame_size,
338                    regs: pulley_interpreter::UpperRegSet::from_bitset(*saved_by_pulley),
339                }
340                .into(),
341            ),
342            FrameStyle::Manual { frame_size } => insts.extend(Self::gen_sp_reg_adjust(
343                -i32::try_from(*frame_size).unwrap(),
344            )),
345        }
346
347        for (offset, ty, reg) in frame_layout.manually_managed_clobbers(&style) {
348            insts.push(
349                Inst::gen_store(Amode::SpOffset { offset }, reg, ty, MemFlags::trusted()).into(),
350            );
351        }
352
353        insts
354    }
355
356    /// Reverse of `gen_prologue_frame_setup`.
357    fn gen_epilogue_frame_restore(
358        _call_conv: isa::CallConv,
359        _flags: &settings::Flags,
360        _isa_flags: &PulleyFlags,
361        frame_layout: &FrameLayout,
362    ) -> SmallInstVec<Self::I> {
363        let mut insts = SmallVec::new();
364
365        let style = frame_layout.pulley_frame_style();
366
367        // Restore clobbered registers that are manually managed in Cranelift.
368        for (offset, ty, reg) in frame_layout.manually_managed_clobbers(&style) {
369            insts.push(
370                Inst::gen_load(
371                    Writable::from_reg(reg),
372                    Amode::SpOffset { offset },
373                    ty,
374                    MemFlags::trusted(),
375                )
376                .into(),
377            );
378        }
379
380        // Perform the inverse of `gen_prologue_frame_setup`.
381        match &style {
382            FrameStyle::None => {}
383            FrameStyle::PulleyBasicSetup { frame_size } => {
384                insts.extend(Self::gen_sp_reg_adjust(i32::try_from(*frame_size).unwrap()));
385                insts.push(RawInst::PopFrame.into());
386            }
387            FrameStyle::PulleySetupAndSaveClobbers {
388                frame_size,
389                saved_by_pulley,
390            } => insts.push(
391                RawInst::PopFrameRestore {
392                    amt: *frame_size,
393                    regs: pulley_interpreter::UpperRegSet::from_bitset(*saved_by_pulley),
394                }
395                .into(),
396            ),
397            FrameStyle::Manual { frame_size } => {
398                insts.extend(Self::gen_sp_reg_adjust(i32::try_from(*frame_size).unwrap()))
399            }
400        }
401
402        insts
403    }
404
405    fn gen_return(
406        call_conv: isa::CallConv,
407        _isa_flags: &PulleyFlags,
408        frame_layout: &FrameLayout,
409    ) -> SmallInstVec<Self::I> {
410        let mut insts = SmallVec::new();
411
412        // Handle final stack adjustments for the tail-call ABI.
413        if call_conv == isa::CallConv::Tail && frame_layout.tail_args_size > 0 {
414            insts.extend(Self::gen_sp_reg_adjust(
415                frame_layout.tail_args_size.try_into().unwrap(),
416            ));
417        }
418        insts.push(RawInst::Ret {}.into());
419
420        insts
421    }
422
423    fn gen_probestack(_insts: &mut SmallInstVec<Self::I>, _frame_size: u32) {
424        // Pulley doesn't implement stack probes since all stack pointer
425        // decrements are checked already.
426    }
427
428    fn gen_clobber_save(
429        _call_conv: isa::CallConv,
430        _flags: &settings::Flags,
431        _frame_layout: &FrameLayout,
432    ) -> SmallVec<[Self::I; 16]> {
433        // Note that this is intentionally empty because everything necessary
434        // was already done in `gen_prologue_frame_setup`.
435        SmallVec::new()
436    }
437
438    fn gen_clobber_restore(
439        _call_conv: isa::CallConv,
440        _flags: &settings::Flags,
441        _frame_layout: &FrameLayout,
442    ) -> SmallVec<[Self::I; 16]> {
443        // Intentionally empty as restores happen for Pulley in `gen_return`.
444        SmallVec::new()
445    }
446
447    fn gen_memcpy<F: FnMut(Type) -> Writable<Reg>>(
448        _call_conv: isa::CallConv,
449        _dst: Reg,
450        _src: Reg,
451        _size: usize,
452        _alloc_tmp: F,
453    ) -> SmallVec<[Self::I; 8]> {
454        todo!()
455    }
456
457    fn get_number_of_spillslots_for_value(
458        rc: RegClass,
459        _target_vector_bytes: u32,
460        _isa_flags: &PulleyFlags,
461    ) -> u32 {
462        // Spill slots are the size of a "word" or a pointer, but Pulley
463        // registers are 8-byte for integers/floats regardless of pointer size.
464        // Calculate the number of slots necessary to store 8 bytes.
465        let slots_for_8bytes = match P::pointer_width() {
466            PointerWidth::PointerWidth32 => 2,
467            PointerWidth::PointerWidth64 => 1,
468        };
469        match rc {
470            // Int/float registers are 8-bytes
471            RegClass::Int | RegClass::Float => slots_for_8bytes,
472            // Vector registers are 16 bytes
473            RegClass::Vector => 2 * slots_for_8bytes,
474        }
475    }
476
477    fn get_machine_env(_flags: &settings::Flags, _call_conv: isa::CallConv) -> &MachineEnv {
478        static MACHINE_ENV: OnceLock<MachineEnv> = OnceLock::new();
479        MACHINE_ENV.get_or_init(create_reg_environment)
480    }
481
482    fn get_regs_clobbered_by_call(
483        _call_conv_of_callee: isa::CallConv,
484        is_exception: bool,
485    ) -> PRegSet {
486        if is_exception {
487            ALL_CLOBBERS
488        } else {
489            DEFAULT_CLOBBERS
490        }
491    }
492
493    fn compute_frame_layout(
494        _call_conv: isa::CallConv,
495        flags: &settings::Flags,
496        _sig: &Signature,
497        regs: &[Writable<RealReg>],
498        function_calls: FunctionCalls,
499        incoming_args_size: u32,
500        tail_args_size: u32,
501        stackslots_size: u32,
502        fixed_frame_storage_size: u32,
503        outgoing_args_size: u32,
504    ) -> FrameLayout {
505        let mut regs: Vec<Writable<RealReg>> = regs
506            .iter()
507            .cloned()
508            .filter(|r| DEFAULT_CALLEE_SAVES.contains(r.to_reg().into()))
509            .collect();
510
511        regs.sort_unstable();
512
513        // Compute clobber size.
514        let clobber_size = compute_clobber_size(&regs);
515
516        // Compute linkage frame size.
517        let setup_area_size = if flags.preserve_frame_pointers()
518            || function_calls != FunctionCalls::None
519            // The function arguments that are passed on the stack are addressed
520            // relative to the Frame Pointer.
521            || incoming_args_size > 0
522            || clobber_size > 0
523            || fixed_frame_storage_size > 0
524        {
525            P::pointer_width().bytes() * 2 // FP, LR
526        } else {
527            0
528        };
529
530        FrameLayout {
531            word_bytes: u32::from(P::pointer_width().bytes()),
532            incoming_args_size,
533            tail_args_size,
534            setup_area_size: setup_area_size.into(),
535            clobber_size,
536            fixed_frame_storage_size,
537            stackslots_size,
538            outgoing_args_size,
539            clobbered_callee_saves: regs,
540            function_calls,
541        }
542    }
543
544    fn gen_inline_probestack(
545        _insts: &mut SmallInstVec<Self::I>,
546        _call_conv: isa::CallConv,
547        _frame_size: u32,
548        _guard_size: u32,
549    ) {
550        // Pulley doesn't need inline probestacks because it always checks stack
551        // decrements.
552    }
553
554    fn retval_temp_reg(_call_conv_of_callee: isa::CallConv) -> Writable<Reg> {
555        // Use x15 as a temp if needed: clobbered, not a
556        // retval.
557        Writable::from_reg(regs::x_reg(15))
558    }
559
560    fn exception_payload_regs(_call_conv: isa::CallConv) -> &'static [Reg] {
561        const PAYLOAD_REGS: &'static [Reg] = &[
562            Reg::from_real_reg(regs::px_reg(0)),
563            Reg::from_real_reg(regs::px_reg(1)),
564        ];
565        PAYLOAD_REGS
566    }
567}
568
569/// Different styles of management of fp/lr and clobbered registers.
570///
571/// This helps decide, depending on Cranelift settings and frame layout, what
572/// macro instruction is used to setup the pulley frame.
573enum FrameStyle {
574    /// No management is happening, fp/lr aren't saved by Pulley or Cranelift.
575    /// No stack is being allocated either.
576    None,
577
578    /// Pulley saves the fp/lr combo and then stack adjustments/clobbers are
579    /// handled manually.
580    PulleyBasicSetup { frame_size: u32 },
581
582    /// Pulley is managing the fp/lr combo, the stack size, and clobbered
583    /// X-class registers.
584    ///
585    /// Note that `saved_by_pulley` is not the exhaustive set of clobbered
586    /// registers. It's only those that are part of the `PushFrameSave`
587    /// instruction.
588    PulleySetupAndSaveClobbers {
589        /// The size of the frame, including clobbers, that's being allocated.
590        frame_size: u16,
591        /// Registers that pulley is saving/restoring.
592        saved_by_pulley: ScalarBitSet<u16>,
593    },
594
595    /// Cranelift is manually managing everything, both clobbers and stack
596    /// increments/decrements.
597    ///
598    /// Note that fp/lr are not saved in this mode.
599    Manual {
600        /// The size of the stack being allocated.
601        frame_size: u32,
602    },
603}
604
605/// Pulley-specific helpers when dealing with ABI code.
606impl FrameLayout {
607    /// Whether or not this frame saves fp/lr.
608    fn setup_frame(&self) -> bool {
609        self.setup_area_size > 0
610    }
611
612    /// Returns the stack size allocated by this function, excluding incoming
613    /// tail args or the optional "setup area" of fp/lr.
614    fn stack_size(&self) -> u32 {
615        self.clobber_size + self.fixed_frame_storage_size + self.outgoing_args_size
616    }
617
618    /// Returns the style of frame being used for this function.
619    ///
620    /// See `FrameStyle` for more information.
621    fn pulley_frame_style(&self) -> FrameStyle {
622        let saved_by_pulley = self.clobbered_xregs_saved_by_pulley();
623        match (
624            self.stack_size(),
625            self.setup_frame(),
626            saved_by_pulley.is_empty(),
627        ) {
628            // No stack allocated, not saving fp/lr, no clobbers, nothing to do
629            (0, false, true) => FrameStyle::None,
630
631            // No stack allocated, saving fp/lr, no clobbers, so this is
632            // pulley-managed via push/pop_frame.
633            (0, true, true) => FrameStyle::PulleyBasicSetup { frame_size: 0 },
634
635            // Some stack is being allocated and pulley is managing fp/lr. Let
636            // pulley manage clobbered registers as well, regardless if they're
637            // present or not.
638            //
639            // If the stack is too large then `PulleyBasicSetup` is used
640            // otherwise we'll be pushing `PushFrameSave` and `PopFrameRestore`.
641            (frame_size, true, _) => match frame_size.try_into() {
642                Ok(frame_size) => FrameStyle::PulleySetupAndSaveClobbers {
643                    frame_size,
644                    saved_by_pulley,
645                },
646                Err(_) => FrameStyle::PulleyBasicSetup { frame_size },
647            },
648
649            // Some stack is being allocated, but pulley isn't managing fp/lr,
650            // so we're manually doing everything.
651            (frame_size, false, true) => FrameStyle::Manual { frame_size },
652
653            // If there's no frame setup and there's clobbered registers this
654            // technically should have already hit a case above, so panic here.
655            (_, false, false) => unreachable!(),
656        }
657    }
658
659    /// Returns the set of clobbered registers that Pulley is managing via its
660    /// macro instructions rather than the generated code.
661    fn clobbered_xregs_saved_by_pulley(&self) -> ScalarBitSet<u16> {
662        let mut clobbered: ScalarBitSet<u16> = ScalarBitSet::new();
663        // Pulley only manages clobbers if it's also managing fp/lr.
664        if !self.setup_frame() {
665            return clobbered;
666        }
667        let mut found_manual_clobber = false;
668        for reg in self.clobbered_callee_saves.iter() {
669            let r_reg = reg.to_reg();
670            // Pulley can only manage clobbers of integer registers at this
671            // time, float registers are managed manually.
672            //
673            // Also assert that all pulley-managed clobbers come first,
674            // otherwise the loop below in `manually_managed_clobbers` is
675            // incorrect.
676            if r_reg.class() == RegClass::Int {
677                assert!(!found_manual_clobber);
678                if let Some(offset) = r_reg.hw_enc().checked_sub(16) {
679                    clobbered.insert(offset);
680                }
681            } else {
682                found_manual_clobber = true;
683            }
684        }
685        clobbered
686    }
687
688    /// Returns an iterator over the clobbers that Cranelift is managing, not
689    /// Pulley.
690    ///
691    /// If this frame has clobbers then they're either saved by Pulley with
692    /// `FrameStyle::PulleySetupAndSaveClobbers`. Cranelift might need to manage
693    /// these registers depending on Cranelift settings. Cranelift also always
694    /// manages floating-point registers.
695    fn manually_managed_clobbers<'a>(
696        &'a self,
697        style: &'a FrameStyle,
698    ) -> impl Iterator<Item = (i32, Type, Reg)> + 'a {
699        let mut offset = self.stack_size();
700        self.clobbered_callee_saves.iter().filter_map(move |reg| {
701            // Allocate space for this clobber no matter what. If pulley is
702            // managing this then we're just accounting for the pulley-saved
703            // registers as well. Note that all pulley-managed registers come
704            // first in the list here.
705            offset -= 8;
706            let r_reg = reg.to_reg();
707            let ty = match r_reg.class() {
708                RegClass::Int => {
709                    // If this register is saved by pulley, skip this clobber.
710                    if let FrameStyle::PulleySetupAndSaveClobbers {
711                        saved_by_pulley, ..
712                    } = style
713                    {
714                        if let Some(reg) = r_reg.hw_enc().checked_sub(16) {
715                            if saved_by_pulley.contains(reg) {
716                                return None;
717                            }
718                        }
719                    }
720                    I64
721                }
722                RegClass::Float => F64,
723                RegClass::Vector => unreachable!("no vector registers are callee-save"),
724            };
725            let offset = i32::try_from(offset).unwrap();
726            Some((offset, ty, Reg::from(reg.to_reg())))
727        })
728    }
729}
730
731const DEFAULT_CALLEE_SAVES: PRegSet = PRegSet::empty()
732    // Integer registers.
733    .with(px_reg(16))
734    .with(px_reg(17))
735    .with(px_reg(18))
736    .with(px_reg(19))
737    .with(px_reg(20))
738    .with(px_reg(21))
739    .with(px_reg(22))
740    .with(px_reg(23))
741    .with(px_reg(24))
742    .with(px_reg(25))
743    .with(px_reg(26))
744    .with(px_reg(27))
745    .with(px_reg(28))
746    .with(px_reg(29))
747    .with(px_reg(30))
748    .with(px_reg(31))
749    // Note: no float/vector registers are callee-saved.
750;
751
752fn compute_clobber_size(clobbers: &[Writable<RealReg>]) -> u32 {
753    let mut clobbered_size = 0;
754    for reg in clobbers {
755        match reg.to_reg().class() {
756            RegClass::Int => {
757                clobbered_size += 8;
758            }
759            RegClass::Float => {
760                clobbered_size += 8;
761            }
762            RegClass::Vector => unimplemented!("Vector Size Clobbered"),
763        }
764    }
765    align_to(clobbered_size, 16)
766}
767
768const DEFAULT_CLOBBERS: PRegSet = PRegSet::empty()
769    // Integer registers: the first 16 get clobbered.
770    .with(px_reg(0))
771    .with(px_reg(1))
772    .with(px_reg(2))
773    .with(px_reg(3))
774    .with(px_reg(4))
775    .with(px_reg(5))
776    .with(px_reg(6))
777    .with(px_reg(7))
778    .with(px_reg(8))
779    .with(px_reg(9))
780    .with(px_reg(10))
781    .with(px_reg(11))
782    .with(px_reg(12))
783    .with(px_reg(13))
784    .with(px_reg(14))
785    .with(px_reg(15))
786    // All float registers get clobbered.
787    .with(pf_reg(0))
788    .with(pf_reg(1))
789    .with(pf_reg(2))
790    .with(pf_reg(3))
791    .with(pf_reg(4))
792    .with(pf_reg(5))
793    .with(pf_reg(6))
794    .with(pf_reg(7))
795    .with(pf_reg(8))
796    .with(pf_reg(9))
797    .with(pf_reg(10))
798    .with(pf_reg(11))
799    .with(pf_reg(12))
800    .with(pf_reg(13))
801    .with(pf_reg(14))
802    .with(pf_reg(15))
803    .with(pf_reg(16))
804    .with(pf_reg(17))
805    .with(pf_reg(18))
806    .with(pf_reg(19))
807    .with(pf_reg(20))
808    .with(pf_reg(21))
809    .with(pf_reg(22))
810    .with(pf_reg(23))
811    .with(pf_reg(24))
812    .with(pf_reg(25))
813    .with(pf_reg(26))
814    .with(pf_reg(27))
815    .with(pf_reg(28))
816    .with(pf_reg(29))
817    .with(pf_reg(30))
818    .with(pf_reg(31))
819    // All vector registers get clobbered.
820    .with(pv_reg(0))
821    .with(pv_reg(1))
822    .with(pv_reg(2))
823    .with(pv_reg(3))
824    .with(pv_reg(4))
825    .with(pv_reg(5))
826    .with(pv_reg(6))
827    .with(pv_reg(7))
828    .with(pv_reg(8))
829    .with(pv_reg(9))
830    .with(pv_reg(10))
831    .with(pv_reg(11))
832    .with(pv_reg(12))
833    .with(pv_reg(13))
834    .with(pv_reg(14))
835    .with(pv_reg(15))
836    .with(pv_reg(16))
837    .with(pv_reg(17))
838    .with(pv_reg(18))
839    .with(pv_reg(19))
840    .with(pv_reg(20))
841    .with(pv_reg(21))
842    .with(pv_reg(22))
843    .with(pv_reg(23))
844    .with(pv_reg(24))
845    .with(pv_reg(25))
846    .with(pv_reg(26))
847    .with(pv_reg(27))
848    .with(pv_reg(28))
849    .with(pv_reg(29))
850    .with(pv_reg(30))
851    .with(pv_reg(31));
852
853const ALL_CLOBBERS: PRegSet = PRegSet::empty()
854    .with(px_reg(0))
855    .with(px_reg(1))
856    .with(px_reg(2))
857    .with(px_reg(3))
858    .with(px_reg(4))
859    .with(px_reg(5))
860    .with(px_reg(6))
861    .with(px_reg(7))
862    .with(px_reg(8))
863    .with(px_reg(9))
864    .with(px_reg(10))
865    .with(px_reg(11))
866    .with(px_reg(12))
867    .with(px_reg(13))
868    .with(px_reg(14))
869    .with(px_reg(15))
870    .with(px_reg(16))
871    .with(px_reg(17))
872    .with(px_reg(18))
873    .with(px_reg(19))
874    .with(px_reg(20))
875    .with(px_reg(21))
876    .with(px_reg(22))
877    .with(px_reg(23))
878    .with(px_reg(24))
879    .with(px_reg(25))
880    .with(px_reg(26))
881    .with(px_reg(27))
882    .with(px_reg(28))
883    .with(px_reg(29))
884    .with(px_reg(30))
885    .with(px_reg(31))
886    .with(pf_reg(0))
887    .with(pf_reg(1))
888    .with(pf_reg(2))
889    .with(pf_reg(3))
890    .with(pf_reg(4))
891    .with(pf_reg(5))
892    .with(pf_reg(6))
893    .with(pf_reg(7))
894    .with(pf_reg(8))
895    .with(pf_reg(9))
896    .with(pf_reg(10))
897    .with(pf_reg(11))
898    .with(pf_reg(12))
899    .with(pf_reg(13))
900    .with(pf_reg(14))
901    .with(pf_reg(15))
902    .with(pf_reg(16))
903    .with(pf_reg(17))
904    .with(pf_reg(18))
905    .with(pf_reg(19))
906    .with(pf_reg(20))
907    .with(pf_reg(21))
908    .with(pf_reg(22))
909    .with(pf_reg(23))
910    .with(pf_reg(24))
911    .with(pf_reg(25))
912    .with(pf_reg(26))
913    .with(pf_reg(27))
914    .with(pf_reg(28))
915    .with(pf_reg(29))
916    .with(pf_reg(30))
917    .with(pf_reg(31))
918    .with(pv_reg(0))
919    .with(pv_reg(1))
920    .with(pv_reg(2))
921    .with(pv_reg(3))
922    .with(pv_reg(4))
923    .with(pv_reg(5))
924    .with(pv_reg(6))
925    .with(pv_reg(7))
926    .with(pv_reg(8))
927    .with(pv_reg(9))
928    .with(pv_reg(10))
929    .with(pv_reg(11))
930    .with(pv_reg(12))
931    .with(pv_reg(13))
932    .with(pv_reg(14))
933    .with(pv_reg(15))
934    .with(pv_reg(16))
935    .with(pv_reg(17))
936    .with(pv_reg(18))
937    .with(pv_reg(19))
938    .with(pv_reg(20))
939    .with(pv_reg(21))
940    .with(pv_reg(22))
941    .with(pv_reg(23))
942    .with(pv_reg(24))
943    .with(pv_reg(25))
944    .with(pv_reg(26))
945    .with(pv_reg(27))
946    .with(pv_reg(28))
947    .with(pv_reg(29))
948    .with(pv_reg(30))
949    .with(pv_reg(31));
950
951fn create_reg_environment() -> MachineEnv {
952    // Prefer caller-saved registers over callee-saved registers, because that
953    // way we don't need to emit code to save and restore them if we don't
954    // mutate them.
955
956    let preferred_regs_by_class: [Vec<PReg>; 3] = {
957        let x_registers: Vec<PReg> = (0..16).map(|x| px_reg(x)).collect();
958        let f_registers: Vec<PReg> = (0..32).map(|x| pf_reg(x)).collect();
959        let v_registers: Vec<PReg> = (0..32).map(|x| pv_reg(x)).collect();
960        [x_registers, f_registers, v_registers]
961    };
962
963    let non_preferred_regs_by_class: [Vec<PReg>; 3] = {
964        let x_registers: Vec<PReg> = (16..XReg::SPECIAL_START)
965            .map(|x| px_reg(x.into()))
966            .collect();
967        let f_registers: Vec<PReg> = vec![];
968        let v_registers: Vec<PReg> = vec![];
969        [x_registers, f_registers, v_registers]
970    };
971
972    MachineEnv {
973        preferred_regs_by_class,
974        non_preferred_regs_by_class,
975        fixed_stack_slots: vec![],
976        scratch_by_class: [None, None, None],
977    }
978}