cranelift_codegen/isa/riscv64/
abi.rs

1//! Implementation of a standard Riscv64 ABI.
2
3use crate::ir;
4use crate::ir::types::*;
5
6use crate::isa;
7
8use crate::isa::CallConv;
9use crate::isa::riscv64::inst::*;
10use crate::machinst::*;
11
12use crate::CodegenResult;
13use crate::ir::LibCall;
14use crate::ir::Signature;
15use crate::isa::riscv64::settings::Flags as RiscvFlags;
16use crate::isa::unwind::UnwindInst;
17use crate::settings;
18use alloc::boxed::Box;
19use alloc::vec::Vec;
20use regalloc2::{MachineEnv, PReg, PRegSet};
21
22use smallvec::{SmallVec, smallvec};
23use std::borrow::ToOwned;
24use std::sync::OnceLock;
25
26/// Support for the Riscv64 ABI from the callee side (within a function body).
27pub(crate) type Riscv64Callee = Callee<Riscv64MachineDeps>;
28
29/// Riscv64-specific ABI behavior. This struct just serves as an implementation
30/// point for the trait; it is never actually instantiated.
31pub struct Riscv64MachineDeps;
32
33impl IsaFlags for RiscvFlags {}
34
35impl RiscvFlags {
36    pub(crate) fn min_vec_reg_size(&self) -> u64 {
37        let entries = [
38            (self.has_zvl65536b(), 65536),
39            (self.has_zvl32768b(), 32768),
40            (self.has_zvl16384b(), 16384),
41            (self.has_zvl8192b(), 8192),
42            (self.has_zvl4096b(), 4096),
43            (self.has_zvl2048b(), 2048),
44            (self.has_zvl1024b(), 1024),
45            (self.has_zvl512b(), 512),
46            (self.has_zvl256b(), 256),
47            // In order to claim the Application Profile V extension, a minimum
48            // register size of 128 is required. i.e. V implies Zvl128b.
49            (self.has_v(), 128),
50            (self.has_zvl128b(), 128),
51            (self.has_zvl64b(), 64),
52            (self.has_zvl32b(), 32),
53        ];
54
55        for (has_flag, size) in entries.into_iter() {
56            if !has_flag {
57                continue;
58            }
59
60            // Due to a limitation in regalloc2, we can't support types
61            // larger than 1024 bytes. So limit that here.
62            return std::cmp::min(size, 1024);
63        }
64
65        return 0;
66    }
67}
68
69impl ABIMachineSpec for Riscv64MachineDeps {
70    type I = Inst;
71    type F = RiscvFlags;
72
73    /// This is the limit for the size of argument and return-value areas on the
74    /// stack. We place a reasonable limit here to avoid integer overflow issues
75    /// with 32-bit arithmetic: for now, 128 MB.
76    const STACK_ARG_RET_SIZE_LIMIT: u32 = 128 * 1024 * 1024;
77
78    fn word_bits() -> u32 {
79        64
80    }
81
82    /// Return required stack alignment in bytes.
83    fn stack_align(_call_conv: isa::CallConv) -> u32 {
84        16
85    }
86
87    fn compute_arg_locs(
88        call_conv: isa::CallConv,
89        flags: &settings::Flags,
90        params: &[ir::AbiParam],
91        args_or_rets: ArgsOrRets,
92        add_ret_area_ptr: bool,
93        mut args: ArgsAccumulator,
94    ) -> CodegenResult<(u32, Option<usize>)> {
95        // This implements the LP64D RISC-V ABI.
96
97        assert_ne!(
98            call_conv,
99            isa::CallConv::Winch,
100            "riscv64 does not support the 'winch' calling convention yet"
101        );
102
103        // All registers that can be used as parameters or rets.
104        // both start and end are included.
105        let (x_start, x_end, f_start, f_end) = match args_or_rets {
106            ArgsOrRets::Args => (10, 17, 10, 17),
107            ArgsOrRets::Rets => (10, 11, 10, 11),
108        };
109        let mut next_x_reg = x_start;
110        let mut next_f_reg = f_start;
111        // Stack space.
112        let mut next_stack: u32 = 0;
113
114        let ret_area_ptr = if add_ret_area_ptr {
115            assert!(ArgsOrRets::Args == args_or_rets);
116            next_x_reg += 1;
117            Some(ABIArg::reg(
118                x_reg(x_start).to_real_reg().unwrap(),
119                I64,
120                ir::ArgumentExtension::None,
121                ir::ArgumentPurpose::Normal,
122            ))
123        } else {
124            None
125        };
126
127        for param in params {
128            if let ir::ArgumentPurpose::StructArgument(_) = param.purpose {
129                panic!(
130                    "StructArgument parameters are not supported on riscv64. \
131                    Use regular pointer arguments instead."
132                );
133            }
134
135            // Find regclass(es) of the register(s) used to store a value of this type.
136            let (rcs, reg_tys) = Inst::rc_for_type(param.value_type)?;
137            let mut slots = ABIArgSlotVec::new();
138            for (rc, reg_ty) in rcs.iter().zip(reg_tys.iter()) {
139                let next_reg = if (next_x_reg <= x_end) && *rc == RegClass::Int {
140                    let x = Some(x_reg(next_x_reg));
141                    next_x_reg += 1;
142                    x
143                } else if (next_f_reg <= f_end) && *rc == RegClass::Float {
144                    let x = Some(f_reg(next_f_reg));
145                    next_f_reg += 1;
146                    x
147                } else {
148                    None
149                };
150                if let Some(reg) = next_reg {
151                    slots.push(ABIArgSlot::Reg {
152                        reg: reg.to_real_reg().unwrap(),
153                        ty: *reg_ty,
154                        extension: param.extension,
155                    });
156                } else {
157                    if args_or_rets == ArgsOrRets::Rets && !flags.enable_multi_ret_implicit_sret() {
158                        return Err(crate::CodegenError::Unsupported(
159                            "Too many return values to fit in registers. \
160                            Use a StructReturn argument instead. (#9510)"
161                                .to_owned(),
162                        ));
163                    }
164
165                    // Compute size and 16-byte stack alignment happens
166                    // separately after all args.
167                    let size = reg_ty.bits() / 8;
168                    let size = std::cmp::max(size, 8);
169                    // Align.
170                    debug_assert!(size.is_power_of_two());
171                    next_stack = align_to(next_stack, size);
172                    slots.push(ABIArgSlot::Stack {
173                        offset: next_stack as i64,
174                        ty: *reg_ty,
175                        extension: param.extension,
176                    });
177                    next_stack += size;
178                }
179            }
180            args.push(ABIArg::Slots {
181                slots,
182                purpose: param.purpose,
183            });
184        }
185        let pos = if let Some(ret_area_ptr) = ret_area_ptr {
186            args.push_non_formal(ret_area_ptr);
187            Some(args.args().len() - 1)
188        } else {
189            None
190        };
191
192        next_stack = align_to(next_stack, Self::stack_align(call_conv));
193
194        Ok((next_stack, pos))
195    }
196
197    fn gen_load_stack(mem: StackAMode, into_reg: Writable<Reg>, ty: Type) -> Inst {
198        Inst::gen_load(into_reg, mem.into(), ty, MemFlags::trusted())
199    }
200
201    fn gen_store_stack(mem: StackAMode, from_reg: Reg, ty: Type) -> Inst {
202        Inst::gen_store(mem.into(), from_reg, ty, MemFlags::trusted())
203    }
204
205    fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Inst {
206        Inst::gen_move(to_reg, from_reg, ty)
207    }
208
209    fn gen_extend(
210        to_reg: Writable<Reg>,
211        from_reg: Reg,
212        signed: bool,
213        from_bits: u8,
214        to_bits: u8,
215    ) -> Inst {
216        assert!(from_bits < to_bits);
217        Inst::Extend {
218            rd: to_reg,
219            rn: from_reg,
220            signed,
221            from_bits,
222            to_bits,
223        }
224    }
225
226    fn get_ext_mode(
227        _call_conv: isa::CallConv,
228        specified: ir::ArgumentExtension,
229    ) -> ir::ArgumentExtension {
230        specified
231    }
232
233    fn gen_args(args: Vec<ArgPair>) -> Inst {
234        Inst::Args { args }
235    }
236
237    fn gen_rets(rets: Vec<RetPair>) -> Inst {
238        Inst::Rets { rets }
239    }
240
241    fn get_stacklimit_reg(_call_conv: isa::CallConv) -> Reg {
242        spilltmp_reg()
243    }
244
245    fn gen_add_imm(
246        _call_conv: isa::CallConv,
247        into_reg: Writable<Reg>,
248        from_reg: Reg,
249        imm: u32,
250    ) -> SmallInstVec<Inst> {
251        let mut insts = SmallInstVec::new();
252        if let Some(imm12) = Imm12::maybe_from_u64(imm as u64) {
253            insts.push(Inst::AluRRImm12 {
254                alu_op: AluOPRRI::Addi,
255                rd: into_reg,
256                rs: from_reg,
257                imm12,
258            });
259        } else {
260            insts.extend(Inst::load_constant_u32(
261                writable_spilltmp_reg2(),
262                imm as u64,
263            ));
264            insts.push(Inst::AluRRR {
265                alu_op: AluOPRRR::Add,
266                rd: into_reg,
267                rs1: spilltmp_reg2(),
268                rs2: from_reg,
269            });
270        }
271        insts
272    }
273
274    fn gen_stack_lower_bound_trap(limit_reg: Reg) -> SmallInstVec<Inst> {
275        let mut insts = SmallVec::new();
276        insts.push(Inst::TrapIf {
277            cc: IntCC::UnsignedLessThan,
278            rs1: stack_reg(),
279            rs2: limit_reg,
280            trap_code: ir::TrapCode::STACK_OVERFLOW,
281        });
282        insts
283    }
284
285    fn gen_get_stack_addr(mem: StackAMode, into_reg: Writable<Reg>) -> Inst {
286        Inst::LoadAddr {
287            rd: into_reg,
288            mem: mem.into(),
289        }
290    }
291
292    fn gen_load_base_offset(into_reg: Writable<Reg>, base: Reg, offset: i32, ty: Type) -> Inst {
293        let mem = AMode::RegOffset(base, offset as i64);
294        Inst::gen_load(into_reg, mem, ty, MemFlags::trusted())
295    }
296
297    fn gen_store_base_offset(base: Reg, offset: i32, from_reg: Reg, ty: Type) -> Inst {
298        let mem = AMode::RegOffset(base, offset as i64);
299        Inst::gen_store(mem, from_reg, ty, MemFlags::trusted())
300    }
301
302    fn gen_sp_reg_adjust(amount: i32) -> SmallInstVec<Inst> {
303        let mut insts = SmallVec::new();
304
305        if amount == 0 {
306            return insts;
307        }
308
309        if let Some(imm) = Imm12::maybe_from_i64(amount as i64) {
310            insts.push(Inst::AluRRImm12 {
311                alu_op: AluOPRRI::Addi,
312                rd: writable_stack_reg(),
313                rs: stack_reg(),
314                imm12: imm,
315            })
316        } else {
317            let tmp = writable_spilltmp_reg();
318            insts.extend(Inst::load_constant_u64(tmp, amount as i64 as u64));
319            insts.push(Inst::AluRRR {
320                alu_op: AluOPRRR::Add,
321                rd: writable_stack_reg(),
322                rs1: stack_reg(),
323                rs2: tmp.to_reg(),
324            });
325        }
326
327        insts
328    }
329
330    fn gen_prologue_frame_setup(
331        _call_conv: isa::CallConv,
332        flags: &settings::Flags,
333        _isa_flags: &RiscvFlags,
334        frame_layout: &FrameLayout,
335    ) -> SmallInstVec<Inst> {
336        let mut insts = SmallVec::new();
337
338        if frame_layout.setup_area_size > 0 {
339            // add  sp,sp,-16    ;; alloc stack space for fp.
340            // sd   ra,8(sp)     ;; save ra.
341            // sd   fp,0(sp)     ;; store old fp.
342            // mv   fp,sp        ;; set fp to sp.
343            insts.extend(Self::gen_sp_reg_adjust(-16));
344            insts.push(Inst::gen_store(
345                AMode::SPOffset(8),
346                link_reg(),
347                I64,
348                MemFlags::trusted(),
349            ));
350            insts.push(Inst::gen_store(
351                AMode::SPOffset(0),
352                fp_reg(),
353                I64,
354                MemFlags::trusted(),
355            ));
356
357            if flags.unwind_info() {
358                insts.push(Inst::Unwind {
359                    inst: UnwindInst::PushFrameRegs {
360                        offset_upward_to_caller_sp: frame_layout.setup_area_size,
361                    },
362                });
363            }
364            insts.push(Inst::Mov {
365                rd: writable_fp_reg(),
366                rm: stack_reg(),
367                ty: I64,
368            });
369        }
370
371        insts
372    }
373    /// reverse of gen_prologue_frame_setup.
374    fn gen_epilogue_frame_restore(
375        call_conv: isa::CallConv,
376        _flags: &settings::Flags,
377        _isa_flags: &RiscvFlags,
378        frame_layout: &FrameLayout,
379    ) -> SmallInstVec<Inst> {
380        let mut insts = SmallVec::new();
381
382        if frame_layout.setup_area_size > 0 {
383            insts.push(Inst::gen_load(
384                writable_link_reg(),
385                AMode::SPOffset(8),
386                I64,
387                MemFlags::trusted(),
388            ));
389            insts.push(Inst::gen_load(
390                writable_fp_reg(),
391                AMode::SPOffset(0),
392                I64,
393                MemFlags::trusted(),
394            ));
395            insts.extend(Self::gen_sp_reg_adjust(16));
396        }
397
398        if call_conv == isa::CallConv::Tail && frame_layout.tail_args_size > 0 {
399            insts.extend(Self::gen_sp_reg_adjust(
400                frame_layout.tail_args_size.try_into().unwrap(),
401            ));
402        }
403
404        insts
405    }
406
407    fn gen_return(
408        _call_conv: isa::CallConv,
409        _isa_flags: &RiscvFlags,
410        _frame_layout: &FrameLayout,
411    ) -> SmallInstVec<Inst> {
412        smallvec![Inst::Ret {}]
413    }
414
415    fn gen_probestack(insts: &mut SmallInstVec<Self::I>, frame_size: u32) {
416        insts.extend(Inst::load_constant_u32(writable_a0(), frame_size as u64));
417        let mut info = CallInfo::empty(
418            ExternalName::LibCall(LibCall::Probestack),
419            CallConv::SystemV,
420        );
421        info.uses.push(CallArgPair {
422            vreg: a0(),
423            preg: a0(),
424        });
425        insts.push(Inst::Call {
426            info: Box::new(info),
427        });
428    }
429
430    fn gen_clobber_save(
431        _call_conv: isa::CallConv,
432        flags: &settings::Flags,
433        frame_layout: &FrameLayout,
434    ) -> SmallVec<[Inst; 16]> {
435        let mut insts = SmallVec::new();
436        let setup_frame = frame_layout.setup_area_size > 0;
437
438        let incoming_args_diff = frame_layout.tail_args_size - frame_layout.incoming_args_size;
439        if incoming_args_diff > 0 {
440            // Decrement SP by the amount of additional incoming argument space we need
441            insts.extend(Self::gen_sp_reg_adjust(-(incoming_args_diff as i32)));
442
443            if setup_frame {
444                // Write the lr position on the stack again, as it hasn't changed since it was
445                // pushed in `gen_prologue_frame_setup`
446                insts.push(Inst::gen_store(
447                    AMode::SPOffset(8),
448                    link_reg(),
449                    I64,
450                    MemFlags::trusted(),
451                ));
452                insts.push(Inst::gen_load(
453                    writable_fp_reg(),
454                    AMode::SPOffset(i64::from(incoming_args_diff)),
455                    I64,
456                    MemFlags::trusted(),
457                ));
458                insts.push(Inst::gen_store(
459                    AMode::SPOffset(0),
460                    fp_reg(),
461                    I64,
462                    MemFlags::trusted(),
463                ));
464
465                // Finally, sync the frame pointer with SP
466                insts.push(Inst::gen_move(writable_fp_reg(), stack_reg(), I64));
467            }
468        }
469
470        if flags.unwind_info() && setup_frame {
471            // The *unwind* frame (but not the actual frame) starts at the
472            // clobbers, just below the saved FP/LR pair.
473            insts.push(Inst::Unwind {
474                inst: UnwindInst::DefineNewFrame {
475                    offset_downward_to_clobbers: frame_layout.clobber_size,
476                    offset_upward_to_caller_sp: frame_layout.setup_area_size,
477                },
478            });
479        }
480
481        // Adjust the stack pointer downward for clobbers, the function fixed
482        // frame (spillslots and storage slots), and outgoing arguments.
483        let stack_size = frame_layout.clobber_size
484            + frame_layout.fixed_frame_storage_size
485            + frame_layout.outgoing_args_size;
486
487        // Store each clobbered register in order at offsets from SP,
488        // placing them above the fixed frame slots.
489        if stack_size > 0 {
490            insts.extend(Self::gen_sp_reg_adjust(-(stack_size as i32)));
491
492            let mut cur_offset = 8;
493            for reg in &frame_layout.clobbered_callee_saves {
494                let r_reg = reg.to_reg();
495                let ty = match r_reg.class() {
496                    RegClass::Int => I64,
497                    RegClass::Float => F64,
498                    RegClass::Vector => unimplemented!("Vector Clobber Saves"),
499                };
500                insts.push(Inst::gen_store(
501                    AMode::SPOffset((stack_size - cur_offset) as i64),
502                    Reg::from(reg.to_reg()),
503                    ty,
504                    MemFlags::trusted(),
505                ));
506
507                if flags.unwind_info() {
508                    insts.push(Inst::Unwind {
509                        inst: UnwindInst::SaveReg {
510                            clobber_offset: frame_layout.clobber_size - cur_offset,
511                            reg: r_reg,
512                        },
513                    });
514                }
515
516                cur_offset += 8
517            }
518        }
519        insts
520    }
521
522    fn gen_clobber_restore(
523        _call_conv: isa::CallConv,
524        _flags: &settings::Flags,
525        frame_layout: &FrameLayout,
526    ) -> SmallVec<[Inst; 16]> {
527        let mut insts = SmallVec::new();
528
529        let stack_size = frame_layout.clobber_size
530            + frame_layout.fixed_frame_storage_size
531            + frame_layout.outgoing_args_size;
532
533        let mut cur_offset = 8;
534        for reg in &frame_layout.clobbered_callee_saves {
535            let rreg = reg.to_reg();
536            let ty = match rreg.class() {
537                RegClass::Int => I64,
538                RegClass::Float => F64,
539                RegClass::Vector => unimplemented!("Vector Clobber Restores"),
540            };
541            insts.push(Inst::gen_load(
542                reg.map(Reg::from),
543                AMode::SPOffset(i64::from(stack_size - cur_offset)),
544                ty,
545                MemFlags::trusted(),
546            ));
547            cur_offset += 8
548        }
549
550        if stack_size > 0 {
551            insts.extend(Self::gen_sp_reg_adjust(stack_size as i32));
552        }
553
554        insts
555    }
556
557    fn gen_memcpy<F: FnMut(Type) -> Writable<Reg>>(
558        call_conv: isa::CallConv,
559        dst: Reg,
560        src: Reg,
561        size: usize,
562        mut alloc_tmp: F,
563    ) -> SmallVec<[Self::I; 8]> {
564        let mut insts = SmallVec::new();
565        let arg0 = Writable::from_reg(x_reg(10));
566        let arg1 = Writable::from_reg(x_reg(11));
567        let arg2 = Writable::from_reg(x_reg(12));
568        let tmp = alloc_tmp(Self::word_type());
569        insts.extend(Inst::load_constant_u64(tmp, size as u64));
570        insts.push(Inst::Call {
571            info: Box::new(CallInfo {
572                dest: ExternalName::LibCall(LibCall::Memcpy),
573                uses: smallvec![
574                    CallArgPair {
575                        vreg: dst,
576                        preg: arg0.to_reg()
577                    },
578                    CallArgPair {
579                        vreg: src,
580                        preg: arg1.to_reg()
581                    },
582                    CallArgPair {
583                        vreg: tmp.to_reg(),
584                        preg: arg2.to_reg()
585                    }
586                ],
587                defs: smallvec![],
588                clobbers: Self::get_regs_clobbered_by_call(call_conv, false),
589                caller_conv: call_conv,
590                callee_conv: call_conv,
591                callee_pop_size: 0,
592                try_call_info: None,
593            }),
594        });
595        insts
596    }
597
598    fn get_number_of_spillslots_for_value(
599        rc: RegClass,
600        _target_vector_bytes: u32,
601        isa_flags: &RiscvFlags,
602    ) -> u32 {
603        // We allocate in terms of 8-byte slots.
604        match rc {
605            RegClass::Int => 1,
606            RegClass::Float => 1,
607            RegClass::Vector => (isa_flags.min_vec_reg_size() / 8) as u32,
608        }
609    }
610
611    fn get_machine_env(_flags: &settings::Flags, _call_conv: isa::CallConv) -> &MachineEnv {
612        static MACHINE_ENV: OnceLock<MachineEnv> = OnceLock::new();
613        MACHINE_ENV.get_or_init(create_reg_environment)
614    }
615
616    fn get_regs_clobbered_by_call(
617        call_conv_of_callee: isa::CallConv,
618        is_exception: bool,
619    ) -> PRegSet {
620        match call_conv_of_callee {
621            isa::CallConv::Tail if is_exception => ALL_CLOBBERS,
622            _ => DEFAULT_CLOBBERS,
623        }
624    }
625
626    fn compute_frame_layout(
627        _call_conv: isa::CallConv,
628        flags: &settings::Flags,
629        _sig: &Signature,
630        regs: &[Writable<RealReg>],
631        function_calls: FunctionCalls,
632        incoming_args_size: u32,
633        tail_args_size: u32,
634        stackslots_size: u32,
635        fixed_frame_storage_size: u32,
636        outgoing_args_size: u32,
637    ) -> FrameLayout {
638        let mut regs: Vec<Writable<RealReg>> = regs
639            .iter()
640            .cloned()
641            .filter(|r| DEFAULT_CALLEE_SAVES.contains(r.to_reg().into()))
642            .collect();
643
644        regs.sort_unstable();
645
646        // Compute clobber size.
647        let clobber_size = compute_clobber_size(&regs);
648
649        // Compute linkage frame size.
650        let setup_area_size = if flags.preserve_frame_pointers()
651            || function_calls != FunctionCalls::None
652            // The function arguments that are passed on the stack are addressed
653            // relative to the Frame Pointer.
654            || incoming_args_size > 0
655            || clobber_size > 0
656            || fixed_frame_storage_size > 0
657        {
658            16 // FP, LR
659        } else {
660            0
661        };
662
663        // Return FrameLayout structure.
664        FrameLayout {
665            word_bytes: 8,
666            incoming_args_size,
667            tail_args_size,
668            setup_area_size,
669            clobber_size,
670            fixed_frame_storage_size,
671            stackslots_size,
672            outgoing_args_size,
673            clobbered_callee_saves: regs,
674            function_calls,
675        }
676    }
677
678    fn gen_inline_probestack(
679        insts: &mut SmallInstVec<Self::I>,
680        _call_conv: isa::CallConv,
681        frame_size: u32,
682        guard_size: u32,
683    ) {
684        // Unroll at most n consecutive probes, before falling back to using a loop
685        const PROBE_MAX_UNROLL: u32 = 3;
686
687        // Calculate how many probes we need to perform. Round down, as we only
688        // need to probe whole guard_size regions we'd otherwise skip over.
689        let probe_count = frame_size / guard_size;
690        if probe_count == 0 {
691            // No probe necessary
692            return;
693        }
694
695        // Must be a caller-saved register that is not an argument.
696        let tmp = Writable::from_reg(x_reg(28)); // t3
697
698        if probe_count <= PROBE_MAX_UNROLL {
699            Self::gen_probestack_unroll(insts, tmp, guard_size, probe_count)
700        } else {
701            insts.push(Inst::StackProbeLoop {
702                guard_size,
703                probe_count,
704                tmp,
705            });
706        }
707    }
708
709    fn retval_temp_reg(_call_conv_of_callee: isa::CallConv) -> Writable<Reg> {
710        // Use x12 as a temp if needed: clobbered, not a
711        // retval.
712        Writable::from_reg(regs::x_reg(12))
713    }
714
715    fn exception_payload_regs(call_conv: isa::CallConv) -> &'static [Reg] {
716        const PAYLOAD_REGS: &'static [Reg] = &[regs::a0(), regs::a1()];
717        match call_conv {
718            isa::CallConv::SystemV | isa::CallConv::Tail => PAYLOAD_REGS,
719            _ => &[],
720        }
721    }
722}
723
724// NOTE: no V regs are callee save.
725const DEFAULT_CALLEE_SAVES: PRegSet = PRegSet::empty()
726    // X Regs
727    .with(px_reg(2))
728    .with(px_reg(8))
729    .with(px_reg(9))
730    .with(px_reg(18))
731    .with(px_reg(19))
732    .with(px_reg(20))
733    .with(px_reg(21))
734    .with(px_reg(22))
735    .with(px_reg(23))
736    .with(px_reg(24))
737    .with(px_reg(25))
738    .with(px_reg(26))
739    .with(px_reg(27))
740    // F Regs
741    .with(pf_reg(8))
742    .with(pf_reg(18))
743    .with(pf_reg(19))
744    .with(pf_reg(20))
745    .with(pf_reg(21))
746    .with(pf_reg(22))
747    .with(pf_reg(23))
748    .with(pf_reg(24))
749    .with(pf_reg(25))
750    .with(pf_reg(26))
751    .with(pf_reg(27));
752
753fn compute_clobber_size(clobbers: &[Writable<RealReg>]) -> u32 {
754    let mut clobbered_size = 0;
755    for reg in clobbers {
756        match reg.to_reg().class() {
757            RegClass::Int => {
758                clobbered_size += 8;
759            }
760            RegClass::Float => {
761                clobbered_size += 8;
762            }
763            RegClass::Vector => unimplemented!("Vector Size Clobbered"),
764        }
765    }
766    align_to(clobbered_size, 16)
767}
768
769const DEFAULT_CLOBBERS: PRegSet = PRegSet::empty()
770    .with(px_reg(1))
771    .with(px_reg(5))
772    .with(px_reg(6))
773    .with(px_reg(7))
774    .with(px_reg(10))
775    .with(px_reg(11))
776    .with(px_reg(12))
777    .with(px_reg(13))
778    .with(px_reg(14))
779    .with(px_reg(15))
780    .with(px_reg(16))
781    .with(px_reg(17))
782    .with(px_reg(28))
783    .with(px_reg(29))
784    .with(px_reg(30))
785    .with(px_reg(31))
786    // F Regs
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(9))
796    .with(pf_reg(10))
797    .with(pf_reg(11))
798    .with(pf_reg(12))
799    .with(pf_reg(13))
800    .with(pf_reg(14))
801    .with(pf_reg(15))
802    .with(pf_reg(16))
803    .with(pf_reg(17))
804    .with(pf_reg(28))
805    .with(pf_reg(29))
806    .with(pf_reg(30))
807    .with(pf_reg(31))
808    // V Regs - All vector regs get clobbered
809    .with(pv_reg(0))
810    .with(pv_reg(1))
811    .with(pv_reg(2))
812    .with(pv_reg(3))
813    .with(pv_reg(4))
814    .with(pv_reg(5))
815    .with(pv_reg(6))
816    .with(pv_reg(7))
817    .with(pv_reg(8))
818    .with(pv_reg(9))
819    .with(pv_reg(10))
820    .with(pv_reg(11))
821    .with(pv_reg(12))
822    .with(pv_reg(13))
823    .with(pv_reg(14))
824    .with(pv_reg(15))
825    .with(pv_reg(16))
826    .with(pv_reg(17))
827    .with(pv_reg(18))
828    .with(pv_reg(19))
829    .with(pv_reg(20))
830    .with(pv_reg(21))
831    .with(pv_reg(22))
832    .with(pv_reg(23))
833    .with(pv_reg(24))
834    .with(pv_reg(25))
835    .with(pv_reg(26))
836    .with(pv_reg(27))
837    .with(pv_reg(28))
838    .with(pv_reg(29))
839    .with(pv_reg(30))
840    .with(pv_reg(31));
841
842const ALL_CLOBBERS: PRegSet = PRegSet::empty()
843    // Specials: x0 is the zero register; x1 is the return address; x2 is SP.
844    .with(px_reg(3))
845    .with(px_reg(4))
846    .with(px_reg(5))
847    .with(px_reg(6))
848    .with(px_reg(7))
849    .with(px_reg(8))
850    .with(px_reg(9))
851    .with(px_reg(10))
852    .with(px_reg(11))
853    .with(px_reg(12))
854    .with(px_reg(13))
855    .with(px_reg(14))
856    .with(px_reg(15))
857    .with(px_reg(16))
858    .with(px_reg(17))
859    .with(px_reg(18))
860    .with(px_reg(19))
861    .with(px_reg(20))
862    .with(px_reg(21))
863    .with(px_reg(22))
864    .with(px_reg(23))
865    .with(px_reg(24))
866    .with(px_reg(25))
867    .with(px_reg(26))
868    .with(px_reg(27))
869    .with(px_reg(28))
870    .with(px_reg(29))
871    .with(px_reg(30))
872    .with(px_reg(31))
873    // F Regs
874    .with(pf_reg(0))
875    .with(pf_reg(1))
876    .with(pf_reg(2))
877    .with(pf_reg(3))
878    .with(pf_reg(4))
879    .with(pf_reg(5))
880    .with(pf_reg(6))
881    .with(pf_reg(7))
882    .with(pf_reg(8))
883    .with(pf_reg(9))
884    .with(pf_reg(10))
885    .with(pf_reg(11))
886    .with(pf_reg(12))
887    .with(pf_reg(13))
888    .with(pf_reg(14))
889    .with(pf_reg(15))
890    .with(pf_reg(16))
891    .with(pf_reg(17))
892    .with(pf_reg(18))
893    .with(pf_reg(19))
894    .with(pf_reg(20))
895    .with(pf_reg(21))
896    .with(pf_reg(22))
897    .with(pf_reg(23))
898    .with(pf_reg(24))
899    .with(pf_reg(25))
900    .with(pf_reg(26))
901    .with(pf_reg(27))
902    .with(pf_reg(28))
903    .with(pf_reg(29))
904    .with(pf_reg(30))
905    .with(pf_reg(31))
906    // V Regs
907    .with(pv_reg(0))
908    .with(pv_reg(1))
909    .with(pv_reg(2))
910    .with(pv_reg(3))
911    .with(pv_reg(4))
912    .with(pv_reg(5))
913    .with(pv_reg(6))
914    .with(pv_reg(7))
915    .with(pv_reg(8))
916    .with(pv_reg(9))
917    .with(pv_reg(10))
918    .with(pv_reg(11))
919    .with(pv_reg(12))
920    .with(pv_reg(13))
921    .with(pv_reg(14))
922    .with(pv_reg(15))
923    .with(pv_reg(16))
924    .with(pv_reg(17))
925    .with(pv_reg(18))
926    .with(pv_reg(19))
927    .with(pv_reg(20))
928    .with(pv_reg(21))
929    .with(pv_reg(22))
930    .with(pv_reg(23))
931    .with(pv_reg(24))
932    .with(pv_reg(25))
933    .with(pv_reg(26))
934    .with(pv_reg(27))
935    .with(pv_reg(28))
936    .with(pv_reg(29))
937    .with(pv_reg(30))
938    .with(pv_reg(31));
939
940fn create_reg_environment() -> MachineEnv {
941    // Some C Extension instructions can only use a subset of the registers.
942    // x8 - x15, f8 - f15, v8 - v15 so we should prefer to use those since
943    // they allow us to emit C instructions more often.
944    //
945    // In general the order of preference is:
946    //   1. Compressible Caller Saved registers.
947    //   2. Non-Compressible Caller Saved registers.
948    //   3. Compressible Callee Saved registers.
949    //   4. Non-Compressible Callee Saved registers.
950
951    let preferred_regs_by_class: [Vec<PReg>; 3] = {
952        let x_registers: Vec<PReg> = (10..=15).map(px_reg).collect();
953        let f_registers: Vec<PReg> = (10..=15).map(pf_reg).collect();
954        let v_registers: Vec<PReg> = (8..=15).map(pv_reg).collect();
955
956        [x_registers, f_registers, v_registers]
957    };
958
959    let non_preferred_regs_by_class: [Vec<PReg>; 3] = {
960        // x0 - x4 are special registers, so we don't want to use them.
961        // Omit x30 and x31 since they are the spilltmp registers.
962
963        // Start with the Non-Compressible Caller Saved registers.
964        let x_registers: Vec<PReg> = (5..=7)
965            .chain(16..=17)
966            .chain(28..=29)
967            // The first Callee Saved register is x9 since its Compressible
968            // Omit x8 since it's the frame pointer.
969            .chain(9..=9)
970            // The rest of the Callee Saved registers are Non-Compressible
971            .chain(18..=27)
972            .map(px_reg)
973            .collect();
974
975        // Prefer Caller Saved registers.
976        let f_registers: Vec<PReg> = (0..=7)
977            .chain(16..=17)
978            .chain(28..=31)
979            // Once those are exhausted, we should prefer f8 and f9 since they are
980            // callee saved, but compressible.
981            .chain(8..=9)
982            .chain(18..=27)
983            .map(pf_reg)
984            .collect();
985
986        let v_registers = (0..=7).chain(16..=31).map(pv_reg).collect();
987
988        [x_registers, f_registers, v_registers]
989    };
990
991    MachineEnv {
992        preferred_regs_by_class,
993        non_preferred_regs_by_class,
994        fixed_stack_slots: vec![],
995        scratch_by_class: [None, None, None],
996    }
997}
998
999impl Riscv64MachineDeps {
1000    fn gen_probestack_unroll(
1001        insts: &mut SmallInstVec<Inst>,
1002        tmp: Writable<Reg>,
1003        guard_size: u32,
1004        probe_count: u32,
1005    ) {
1006        // When manually unrolling adjust the stack pointer and then write a zero
1007        // to the stack at that offset.
1008        //
1009        // We do this because valgrind expects us to never write beyond the stack
1010        // pointer and associated redzone.
1011        // See: https://github.com/bytecodealliance/wasmtime/issues/7454
1012
1013        // Store the adjust amount in a register upfront, so we don't have to
1014        // reload it for each probe. It's worth loading this as a negative and
1015        // using an `add` instruction since we have compressed versions of `add`
1016        // but not the `sub` instruction.
1017        insts.extend(Inst::load_constant_u64(tmp, (-(guard_size as i64)) as u64));
1018
1019        for _ in 0..probe_count {
1020            insts.push(Inst::AluRRR {
1021                alu_op: AluOPRRR::Add,
1022                rd: writable_stack_reg(),
1023                rs1: stack_reg(),
1024                rs2: tmp.to_reg(),
1025            });
1026
1027            insts.push(Inst::gen_store(
1028                AMode::SPOffset(0),
1029                zero_reg(),
1030                I32,
1031                MemFlags::trusted(),
1032            ));
1033        }
1034
1035        // Restore the stack pointer to its original value
1036        insts.extend(Self::gen_sp_reg_adjust((guard_size * probe_count) as i32));
1037    }
1038}