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        is_leaf: bool,
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            || !is_leaf
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            incoming_args_size,
666            tail_args_size,
667            setup_area_size,
668            clobber_size,
669            fixed_frame_storage_size,
670            stackslots_size,
671            outgoing_args_size,
672            clobbered_callee_saves: regs,
673        }
674    }
675
676    fn gen_inline_probestack(
677        insts: &mut SmallInstVec<Self::I>,
678        _call_conv: isa::CallConv,
679        frame_size: u32,
680        guard_size: u32,
681    ) {
682        // Unroll at most n consecutive probes, before falling back to using a loop
683        const PROBE_MAX_UNROLL: u32 = 3;
684
685        // Calculate how many probes we need to perform. Round down, as we only
686        // need to probe whole guard_size regions we'd otherwise skip over.
687        let probe_count = frame_size / guard_size;
688        if probe_count == 0 {
689            // No probe necessary
690            return;
691        }
692
693        // Must be a caller-saved register that is not an argument.
694        let tmp = Writable::from_reg(x_reg(28)); // t3
695
696        if probe_count <= PROBE_MAX_UNROLL {
697            Self::gen_probestack_unroll(insts, tmp, guard_size, probe_count)
698        } else {
699            insts.push(Inst::StackProbeLoop {
700                guard_size,
701                probe_count,
702                tmp,
703            });
704        }
705    }
706
707    fn retval_temp_reg(_call_conv_of_callee: isa::CallConv) -> Writable<Reg> {
708        // Use x12 as a temp if needed: clobbered, not a
709        // retval.
710        Writable::from_reg(regs::x_reg(12))
711    }
712
713    fn exception_payload_regs(call_conv: isa::CallConv) -> &'static [Reg] {
714        const PAYLOAD_REGS: &'static [Reg] = &[regs::a0(), regs::a1()];
715        match call_conv {
716            isa::CallConv::SystemV | isa::CallConv::Tail => PAYLOAD_REGS,
717            _ => &[],
718        }
719    }
720}
721
722// NOTE: no V regs are callee save.
723const DEFAULT_CALLEE_SAVES: PRegSet = PRegSet::empty()
724    // X Regs
725    .with(px_reg(2))
726    .with(px_reg(8))
727    .with(px_reg(9))
728    .with(px_reg(18))
729    .with(px_reg(19))
730    .with(px_reg(20))
731    .with(px_reg(21))
732    .with(px_reg(22))
733    .with(px_reg(23))
734    .with(px_reg(24))
735    .with(px_reg(25))
736    .with(px_reg(26))
737    .with(px_reg(27))
738    // F Regs
739    .with(pf_reg(8))
740    .with(pf_reg(18))
741    .with(pf_reg(19))
742    .with(pf_reg(20))
743    .with(pf_reg(21))
744    .with(pf_reg(22))
745    .with(pf_reg(23))
746    .with(pf_reg(24))
747    .with(pf_reg(25))
748    .with(pf_reg(26))
749    .with(pf_reg(27));
750
751fn compute_clobber_size(clobbers: &[Writable<RealReg>]) -> u32 {
752    let mut clobbered_size = 0;
753    for reg in clobbers {
754        match reg.to_reg().class() {
755            RegClass::Int => {
756                clobbered_size += 8;
757            }
758            RegClass::Float => {
759                clobbered_size += 8;
760            }
761            RegClass::Vector => unimplemented!("Vector Size Clobbered"),
762        }
763    }
764    align_to(clobbered_size, 16)
765}
766
767const DEFAULT_CLOBBERS: PRegSet = PRegSet::empty()
768    .with(px_reg(1))
769    .with(px_reg(5))
770    .with(px_reg(6))
771    .with(px_reg(7))
772    .with(px_reg(10))
773    .with(px_reg(11))
774    .with(px_reg(12))
775    .with(px_reg(13))
776    .with(px_reg(14))
777    .with(px_reg(15))
778    .with(px_reg(16))
779    .with(px_reg(17))
780    .with(px_reg(28))
781    .with(px_reg(29))
782    .with(px_reg(30))
783    .with(px_reg(31))
784    // F Regs
785    .with(pf_reg(0))
786    .with(pf_reg(1))
787    .with(pf_reg(2))
788    .with(pf_reg(3))
789    .with(pf_reg(4))
790    .with(pf_reg(5))
791    .with(pf_reg(6))
792    .with(pf_reg(7))
793    .with(pf_reg(9))
794    .with(pf_reg(10))
795    .with(pf_reg(11))
796    .with(pf_reg(12))
797    .with(pf_reg(13))
798    .with(pf_reg(14))
799    .with(pf_reg(15))
800    .with(pf_reg(16))
801    .with(pf_reg(17))
802    .with(pf_reg(28))
803    .with(pf_reg(29))
804    .with(pf_reg(30))
805    .with(pf_reg(31))
806    // V Regs - All vector regs get clobbered
807    .with(pv_reg(0))
808    .with(pv_reg(1))
809    .with(pv_reg(2))
810    .with(pv_reg(3))
811    .with(pv_reg(4))
812    .with(pv_reg(5))
813    .with(pv_reg(6))
814    .with(pv_reg(7))
815    .with(pv_reg(8))
816    .with(pv_reg(9))
817    .with(pv_reg(10))
818    .with(pv_reg(11))
819    .with(pv_reg(12))
820    .with(pv_reg(13))
821    .with(pv_reg(14))
822    .with(pv_reg(15))
823    .with(pv_reg(16))
824    .with(pv_reg(17))
825    .with(pv_reg(18))
826    .with(pv_reg(19))
827    .with(pv_reg(20))
828    .with(pv_reg(21))
829    .with(pv_reg(22))
830    .with(pv_reg(23))
831    .with(pv_reg(24))
832    .with(pv_reg(25))
833    .with(pv_reg(26))
834    .with(pv_reg(27))
835    .with(pv_reg(28))
836    .with(pv_reg(29))
837    .with(pv_reg(30))
838    .with(pv_reg(31));
839
840const ALL_CLOBBERS: PRegSet = PRegSet::empty()
841    // Specials: x0 is the zero register; x1 is the return address; x2 is SP.
842    .with(px_reg(3))
843    .with(px_reg(4))
844    .with(px_reg(5))
845    .with(px_reg(6))
846    .with(px_reg(7))
847    .with(px_reg(8))
848    .with(px_reg(9))
849    .with(px_reg(10))
850    .with(px_reg(11))
851    .with(px_reg(12))
852    .with(px_reg(13))
853    .with(px_reg(14))
854    .with(px_reg(15))
855    .with(px_reg(16))
856    .with(px_reg(17))
857    .with(px_reg(18))
858    .with(px_reg(19))
859    .with(px_reg(20))
860    .with(px_reg(21))
861    .with(px_reg(22))
862    .with(px_reg(23))
863    .with(px_reg(24))
864    .with(px_reg(25))
865    .with(px_reg(26))
866    .with(px_reg(27))
867    .with(px_reg(28))
868    .with(px_reg(29))
869    .with(px_reg(30))
870    .with(px_reg(31))
871    // F Regs
872    .with(pf_reg(0))
873    .with(pf_reg(1))
874    .with(pf_reg(2))
875    .with(pf_reg(3))
876    .with(pf_reg(4))
877    .with(pf_reg(5))
878    .with(pf_reg(6))
879    .with(pf_reg(7))
880    .with(pf_reg(8))
881    .with(pf_reg(9))
882    .with(pf_reg(10))
883    .with(pf_reg(11))
884    .with(pf_reg(12))
885    .with(pf_reg(13))
886    .with(pf_reg(14))
887    .with(pf_reg(15))
888    .with(pf_reg(16))
889    .with(pf_reg(17))
890    .with(pf_reg(18))
891    .with(pf_reg(19))
892    .with(pf_reg(20))
893    .with(pf_reg(21))
894    .with(pf_reg(22))
895    .with(pf_reg(23))
896    .with(pf_reg(24))
897    .with(pf_reg(25))
898    .with(pf_reg(26))
899    .with(pf_reg(27))
900    .with(pf_reg(28))
901    .with(pf_reg(29))
902    .with(pf_reg(30))
903    .with(pf_reg(31))
904    // V Regs
905    .with(pv_reg(0))
906    .with(pv_reg(1))
907    .with(pv_reg(2))
908    .with(pv_reg(3))
909    .with(pv_reg(4))
910    .with(pv_reg(5))
911    .with(pv_reg(6))
912    .with(pv_reg(7))
913    .with(pv_reg(8))
914    .with(pv_reg(9))
915    .with(pv_reg(10))
916    .with(pv_reg(11))
917    .with(pv_reg(12))
918    .with(pv_reg(13))
919    .with(pv_reg(14))
920    .with(pv_reg(15))
921    .with(pv_reg(16))
922    .with(pv_reg(17))
923    .with(pv_reg(18))
924    .with(pv_reg(19))
925    .with(pv_reg(20))
926    .with(pv_reg(21))
927    .with(pv_reg(22))
928    .with(pv_reg(23))
929    .with(pv_reg(24))
930    .with(pv_reg(25))
931    .with(pv_reg(26))
932    .with(pv_reg(27))
933    .with(pv_reg(28))
934    .with(pv_reg(29))
935    .with(pv_reg(30))
936    .with(pv_reg(31));
937
938fn create_reg_environment() -> MachineEnv {
939    // Some C Extension instructions can only use a subset of the registers.
940    // x8 - x15, f8 - f15, v8 - v15 so we should prefer to use those since
941    // they allow us to emit C instructions more often.
942    //
943    // In general the order of preference is:
944    //   1. Compressible Caller Saved registers.
945    //   2. Non-Compressible Caller Saved registers.
946    //   3. Compressible Callee Saved registers.
947    //   4. Non-Compressible Callee Saved registers.
948
949    let preferred_regs_by_class: [Vec<PReg>; 3] = {
950        let x_registers: Vec<PReg> = (10..=15).map(px_reg).collect();
951        let f_registers: Vec<PReg> = (10..=15).map(pf_reg).collect();
952        let v_registers: Vec<PReg> = (8..=15).map(pv_reg).collect();
953
954        [x_registers, f_registers, v_registers]
955    };
956
957    let non_preferred_regs_by_class: [Vec<PReg>; 3] = {
958        // x0 - x4 are special registers, so we don't want to use them.
959        // Omit x30 and x31 since they are the spilltmp registers.
960
961        // Start with the Non-Compressible Caller Saved registers.
962        let x_registers: Vec<PReg> = (5..=7)
963            .chain(16..=17)
964            .chain(28..=29)
965            // The first Callee Saved register is x9 since its Compressible
966            // Omit x8 since it's the frame pointer.
967            .chain(9..=9)
968            // The rest of the Callee Saved registers are Non-Compressible
969            .chain(18..=27)
970            .map(px_reg)
971            .collect();
972
973        // Prefer Caller Saved registers.
974        let f_registers: Vec<PReg> = (0..=7)
975            .chain(16..=17)
976            .chain(28..=31)
977            // Once those are exhausted, we should prefer f8 and f9 since they are
978            // callee saved, but compressible.
979            .chain(8..=9)
980            .chain(18..=27)
981            .map(pf_reg)
982            .collect();
983
984        let v_registers = (0..=7).chain(16..=31).map(pv_reg).collect();
985
986        [x_registers, f_registers, v_registers]
987    };
988
989    MachineEnv {
990        preferred_regs_by_class,
991        non_preferred_regs_by_class,
992        fixed_stack_slots: vec![],
993        scratch_by_class: [None, None, None],
994    }
995}
996
997impl Riscv64MachineDeps {
998    fn gen_probestack_unroll(
999        insts: &mut SmallInstVec<Inst>,
1000        tmp: Writable<Reg>,
1001        guard_size: u32,
1002        probe_count: u32,
1003    ) {
1004        // When manually unrolling adjust the stack pointer and then write a zero
1005        // to the stack at that offset.
1006        //
1007        // We do this because valgrind expects us to never write beyond the stack
1008        // pointer and associated redzone.
1009        // See: https://github.com/bytecodealliance/wasmtime/issues/7454
1010
1011        // Store the adjust amount in a register upfront, so we don't have to
1012        // reload it for each probe. It's worth loading this as a negative and
1013        // using an `add` instruction since we have compressed versions of `add`
1014        // but not the `sub` instruction.
1015        insts.extend(Inst::load_constant_u64(tmp, (-(guard_size as i64)) as u64));
1016
1017        for _ in 0..probe_count {
1018            insts.push(Inst::AluRRR {
1019                alu_op: AluOPRRR::Add,
1020                rd: writable_stack_reg(),
1021                rs1: stack_reg(),
1022                rs2: tmp.to_reg(),
1023            });
1024
1025            insts.push(Inst::gen_store(
1026                AMode::SPOffset(0),
1027                zero_reg(),
1028                I32,
1029                MemFlags::trusted(),
1030            ));
1031        }
1032
1033        // Restore the stack pointer to its original value
1034        insts.extend(Self::gen_sp_reg_adjust((guard_size * probe_count) as i32));
1035    }
1036}