Skip to main content

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