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        _location: ABIArgLocation,
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            cmp: IntegerCompare {
278                kind: IntCC::UnsignedLessThan,
279                rs1: stack_reg(),
280                rs2: limit_reg,
281            },
282            trap_code: ir::TrapCode::STACK_OVERFLOW,
283        });
284        insts
285    }
286
287    fn gen_get_stack_addr(mem: StackAMode, into_reg: Writable<Reg>) -> Inst {
288        Inst::LoadAddr {
289            rd: into_reg,
290            mem: mem.into(),
291        }
292    }
293
294    fn gen_load_base_offset(into_reg: Writable<Reg>, base: Reg, offset: i32, ty: Type) -> Inst {
295        let mem = AMode::RegOffset(base, offset as i64);
296        Inst::gen_load(into_reg, mem, ty, MemFlagsData::trusted())
297    }
298
299    fn gen_store_base_offset(base: Reg, offset: i32, from_reg: Reg, ty: Type) -> Inst {
300        let mem = AMode::RegOffset(base, offset as i64);
301        Inst::gen_store(mem, from_reg, ty, MemFlagsData::trusted())
302    }
303
304    fn gen_sp_reg_adjust(amount: i32) -> SmallInstVec<Inst> {
305        let mut insts = SmallVec::new();
306
307        if amount == 0 {
308            return insts;
309        }
310
311        if let Some(imm) = Imm12::maybe_from_i64(amount as i64) {
312            insts.push(Inst::AluRRImm12 {
313                alu_op: AluOPRRI::Addi,
314                rd: writable_stack_reg(),
315                rs: stack_reg(),
316                imm12: imm,
317            })
318        } else {
319            let tmp = writable_spilltmp_reg();
320            insts.extend(Inst::load_constant_u64(tmp, amount as i64 as u64));
321            insts.push(Inst::AluRRR {
322                alu_op: AluOPRRR::Add,
323                rd: writable_stack_reg(),
324                rs1: stack_reg(),
325                rs2: tmp.to_reg(),
326            });
327        }
328
329        insts
330    }
331
332    fn gen_prologue_frame_setup(
333        _call_conv: isa::CallConv,
334        flags: &settings::Flags,
335        _isa_flags: &RiscvFlags,
336        frame_layout: &FrameLayout,
337    ) -> SmallInstVec<Inst> {
338        let mut insts = SmallVec::new();
339
340        if frame_layout.setup_area_size > 0 {
341            // add  sp,sp,-16    ;; alloc stack space for fp.
342            // sd   ra,8(sp)     ;; save ra.
343            // sd   fp,0(sp)     ;; store old fp.
344            // mv   fp,sp        ;; set fp to sp.
345            insts.extend(Self::gen_sp_reg_adjust(-16));
346            insts.push(Inst::gen_store(
347                AMode::SPOffset(8),
348                link_reg(),
349                I64,
350                MemFlagsData::trusted(),
351            ));
352            insts.push(Inst::gen_store(
353                AMode::SPOffset(0),
354                fp_reg(),
355                I64,
356                MemFlagsData::trusted(),
357            ));
358
359            if flags.unwind_info() {
360                insts.push(Inst::Unwind {
361                    inst: UnwindInst::PushFrameRegs {
362                        offset_upward_to_caller_sp: frame_layout.setup_area_size,
363                    },
364                });
365            }
366            insts.push(Inst::Mov {
367                rd: writable_fp_reg(),
368                rm: stack_reg(),
369                ty: I64,
370            });
371        }
372
373        insts
374    }
375    /// reverse of gen_prologue_frame_setup.
376    fn gen_epilogue_frame_restore(
377        call_conv: isa::CallConv,
378        _flags: &settings::Flags,
379        _isa_flags: &RiscvFlags,
380        frame_layout: &FrameLayout,
381    ) -> SmallInstVec<Inst> {
382        let mut insts = SmallVec::new();
383
384        if frame_layout.setup_area_size > 0 {
385            insts.push(Inst::gen_load(
386                writable_link_reg(),
387                AMode::SPOffset(8),
388                I64,
389                MemFlagsData::trusted(),
390            ));
391            insts.push(Inst::gen_load(
392                writable_fp_reg(),
393                AMode::SPOffset(0),
394                I64,
395                MemFlagsData::trusted(),
396            ));
397            insts.extend(Self::gen_sp_reg_adjust(16));
398        }
399
400        if call_conv == isa::CallConv::Tail && frame_layout.tail_args_size > 0 {
401            insts.extend(Self::gen_sp_reg_adjust(
402                frame_layout.tail_args_size.try_into().unwrap(),
403            ));
404        }
405
406        insts
407    }
408
409    fn gen_return(
410        _call_conv: isa::CallConv,
411        _isa_flags: &RiscvFlags,
412        _frame_layout: &FrameLayout,
413    ) -> SmallInstVec<Inst> {
414        smallvec![Inst::Ret {}]
415    }
416
417    fn gen_probestack(insts: &mut SmallInstVec<Self::I>, frame_size: u32) {
418        insts.extend(Inst::load_constant_u32(writable_a0(), frame_size as u64));
419        let mut info = CallInfo::empty(
420            ExternalName::LibCall(LibCall::Probestack),
421            CallConv::SystemV,
422        );
423        info.uses.push(CallArgPair {
424            vreg: a0(),
425            preg: a0(),
426        });
427        insts.push(Inst::Call {
428            info: Box::new(info),
429        });
430    }
431
432    fn gen_clobber_save(
433        _call_conv: isa::CallConv,
434        flags: &settings::Flags,
435        frame_layout: &FrameLayout,
436    ) -> SmallVec<[Inst; 16]> {
437        let mut insts = SmallVec::new();
438        let setup_frame = frame_layout.setup_area_size > 0;
439
440        if flags.unwind_info() && setup_frame {
441            // The *unwind* frame (but not the actual frame) starts at the
442            // clobbers, just below the saved FP/LR pair.
443            //
444            // This is emitted before the incoming argument area is resized
445            // below so that the CFA is defined in terms of FP while the saved
446            // FP/LR pair is being moved: FP keeps pointing at the original pair
447            // until it has been copied, and then points at the new copy.
448            insts.push(Inst::Unwind {
449                inst: UnwindInst::DefineNewFrame {
450                    offset_downward_to_clobbers: frame_layout.clobber_size,
451                    offset_upward_to_caller_sp: frame_layout.setup_area_size,
452                },
453            });
454        }
455
456        let incoming_args_diff = frame_layout.tail_args_size - frame_layout.incoming_args_size;
457        if incoming_args_diff > 0 {
458            // Decrement SP by the amount of additional incoming argument space we need
459            insts.extend(Self::gen_sp_reg_adjust(-(incoming_args_diff as i32)));
460
461            if setup_frame {
462                // Write the lr position on the stack again, as it hasn't changed since it was
463                // pushed in `gen_prologue_frame_setup`
464                insts.push(Inst::gen_store(
465                    AMode::SPOffset(8),
466                    link_reg(),
467                    I64,
468                    MemFlagsData::trusted(),
469                ));
470                // Copy the saved frame pointer down. This uses a temporary
471                // rather than FP itself so that FP keeps pointing at a valid
472                // frame record until the copy is complete.
473                insts.push(Inst::gen_load(
474                    writable_spilltmp_reg(),
475                    AMode::SPOffset(i64::from(incoming_args_diff)),
476                    I64,
477                    MemFlagsData::trusted(),
478                ));
479                insts.push(Inst::gen_store(
480                    AMode::SPOffset(0),
481                    spilltmp_reg(),
482                    I64,
483                    MemFlagsData::trusted(),
484                ));
485
486                // Finally, sync the frame pointer with SP
487                insts.push(Inst::gen_move(writable_fp_reg(), stack_reg(), I64));
488            }
489        }
490
491        // Adjust the stack pointer downward for clobbers, the function fixed
492        // frame (spillslots and storage slots), and outgoing arguments.
493        let stack_size = frame_layout.clobber_size
494            + frame_layout.fixed_frame_storage_size
495            + frame_layout.outgoing_args_size;
496
497        // Store each clobbered register in order at offsets from SP,
498        // placing them above the fixed frame slots.
499        if stack_size > 0 {
500            insts.extend(Self::gen_sp_reg_adjust(-(stack_size as i32)));
501
502            let mut cur_offset = 0;
503            for reg in &frame_layout.clobbered_callee_saves {
504                let r_reg = reg.to_reg();
505                let ty = match r_reg.class() {
506                    RegClass::Int => I64,
507                    RegClass::Float => F64,
508                    RegClass::Vector => I8X16,
509                };
510                cur_offset = align_to(cur_offset, ty.bytes());
511                insts.push(Inst::gen_store(
512                    AMode::SPOffset(i64::from(stack_size - cur_offset - ty.bytes())),
513                    Reg::from(reg.to_reg()),
514                    ty,
515                    MemFlagsData::trusted(),
516                ));
517
518                if flags.unwind_info() {
519                    insts.push(Inst::Unwind {
520                        inst: UnwindInst::SaveReg {
521                            clobber_offset: frame_layout.clobber_size - cur_offset - ty.bytes(),
522                            reg: r_reg,
523                        },
524                    });
525                }
526
527                cur_offset += ty.bytes();
528                assert!(cur_offset <= stack_size);
529            }
530        }
531        insts
532    }
533
534    fn gen_clobber_restore(
535        _call_conv: isa::CallConv,
536        _flags: &settings::Flags,
537        frame_layout: &FrameLayout,
538    ) -> SmallVec<[Inst; 16]> {
539        let mut insts = SmallVec::new();
540
541        let stack_size = frame_layout.clobber_size
542            + frame_layout.fixed_frame_storage_size
543            + frame_layout.outgoing_args_size;
544        let mut cur_offset = 0;
545
546        for reg in &frame_layout.clobbered_callee_saves {
547            let rreg = reg.to_reg();
548            let ty = match rreg.class() {
549                RegClass::Int => I64,
550                RegClass::Float => F64,
551                RegClass::Vector => I8X16,
552            };
553            cur_offset = align_to(cur_offset, ty.bytes());
554            insts.push(Inst::gen_load(
555                reg.map(Reg::from),
556                AMode::SPOffset(i64::from(stack_size - cur_offset - ty.bytes())),
557                ty,
558                MemFlagsData::trusted(),
559            ));
560            cur_offset += ty.bytes();
561        }
562
563        if stack_size > 0 {
564            insts.extend(Self::gen_sp_reg_adjust(stack_size as i32));
565        }
566
567        insts
568    }
569
570    fn gen_memcpy<F: FnMut(Type) -> Writable<Reg>>(
571        call_conv: isa::CallConv,
572        dst: Reg,
573        src: Reg,
574        size: usize,
575        mut alloc_tmp: F,
576    ) -> SmallVec<[Self::I; 8]> {
577        let mut insts = SmallVec::new();
578        let arg0 = Writable::from_reg(x_reg(10));
579        let arg1 = Writable::from_reg(x_reg(11));
580        let arg2 = Writable::from_reg(x_reg(12));
581        let tmp = alloc_tmp(Self::word_type());
582        insts.extend(Inst::load_constant_u64(tmp, size as u64));
583        insts.push(Inst::Call {
584            info: Box::new(CallInfo {
585                dest: ExternalName::LibCall(LibCall::Memcpy),
586                uses: smallvec![
587                    CallArgPair {
588                        vreg: dst,
589                        preg: arg0.to_reg()
590                    },
591                    CallArgPair {
592                        vreg: src,
593                        preg: arg1.to_reg()
594                    },
595                    CallArgPair {
596                        vreg: tmp.to_reg(),
597                        preg: arg2.to_reg()
598                    }
599                ],
600                defs: smallvec![],
601                clobbers: Self::get_regs_clobbered_by_call(call_conv, false),
602                caller_conv: call_conv,
603                callee_conv: call_conv,
604                callee_pop_size: 0,
605                try_call_info: None,
606                patchable: false,
607            }),
608        });
609        insts
610    }
611
612    fn get_number_of_spillslots_for_value(
613        rc: RegClass,
614        _target_vector_bytes: u32,
615        isa_flags: &RiscvFlags,
616    ) -> u32 {
617        // We allocate in terms of 8-byte slots.
618        match rc {
619            RegClass::Int => 1,
620            RegClass::Float => 1,
621            RegClass::Vector => (isa_flags.min_vec_reg_size() / 8) as u32,
622        }
623    }
624
625    fn get_machine_env(_flags: &settings::Flags, _call_conv: isa::CallConv) -> &MachineEnv {
626        static MACHINE_ENV: MachineEnv = create_reg_environment();
627        &MACHINE_ENV
628    }
629
630    fn get_regs_clobbered_by_call(
631        call_conv_of_callee: isa::CallConv,
632        is_exception: bool,
633    ) -> PRegSet {
634        match call_conv_of_callee {
635            isa::CallConv::Tail if is_exception => ALL_CLOBBERS,
636            // Note that "PreserveAll" actually preserves nothing at
637            // the callsite if used for a `try_call`, because the
638            // unwinder ABI for `try_call`s is still "no clobbered
639            // register restores" for this ABI (so as to work with
640            // Wasmtime).
641            isa::CallConv::PreserveAll if is_exception => ALL_CLOBBERS,
642            isa::CallConv::PreserveAll => NO_CLOBBERS,
643            _ => DEFAULT_CLOBBERS,
644        }
645    }
646
647    fn compute_frame_layout(
648        call_conv: isa::CallConv,
649        flags: &settings::Flags,
650        _sig: &Signature,
651        regs: &[Writable<RealReg>],
652        function_calls: FunctionCalls,
653        incoming_args_size: u32,
654        tail_args_size: u32,
655        stackslots_size: u32,
656        fixed_frame_storage_size: u32,
657        outgoing_args_size: u32,
658    ) -> FrameLayout {
659        let is_callee_saved = |reg: &Writable<RealReg>| match call_conv {
660            isa::CallConv::PreserveAll => true,
661            _ => DEFAULT_CALLEE_SAVES.contains(reg.to_reg().into()),
662        };
663        let mut regs: Vec<Writable<RealReg>> =
664            regs.iter().cloned().filter(is_callee_saved).collect();
665
666        regs.sort_unstable();
667
668        // Compute clobber size.
669        let clobber_size = compute_clobber_size(&regs);
670
671        // Compute linkage frame size.
672        let setup_area_size = if flags.preserve_frame_pointers()
673            || function_calls != FunctionCalls::None
674            // The function arguments that are passed on the stack are addressed
675            // relative to the Frame Pointer.
676            || incoming_args_size > 0
677            || clobber_size > 0
678            || fixed_frame_storage_size > 0
679        {
680            16 // FP, LR
681        } else {
682            0
683        };
684
685        // Return FrameLayout structure.
686        FrameLayout {
687            word_bytes: 8,
688            incoming_args_size,
689            tail_args_size,
690            setup_area_size,
691            clobber_size,
692            fixed_frame_storage_size,
693            stackslots_size,
694            outgoing_args_size,
695            clobbered_callee_saves: regs,
696            function_calls,
697        }
698    }
699
700    fn gen_inline_probestack(
701        insts: &mut SmallInstVec<Self::I>,
702        call_conv: isa::CallConv,
703        frame_size: u32,
704        guard_size: u32,
705    ) {
706        // Unroll at most n consecutive probes, before falling back to using a loop
707        const PROBE_MAX_UNROLL: u32 = 3;
708
709        // Calculate how many probes we need to perform. Round down, as we only
710        // need to probe whole guard_size regions we'd otherwise skip over.
711        let probe_count = frame_size / guard_size;
712        if probe_count == 0 {
713            // No probe necessary
714            return;
715        }
716
717        // Probes run before any registers are saved, and a `preserve_all`
718        // function must not clobber any register. The probe loop needs both
719        // non-allocatable spill temporaries in addition to `tmp`, so always
720        // use the unrolled probes, which only need `tmp`, and use the second
721        // spill temporary for it (the first is used to restore SP).
722        if call_conv == isa::CallConv::PreserveAll {
723            Self::gen_probestack_unroll(insts, writable_spilltmp_reg2(), guard_size, probe_count);
724            return;
725        }
726
727        // Must be a caller-saved register that is not an argument.
728        let tmp = Writable::from_reg(x_reg(28)); // t3
729
730        if probe_count <= PROBE_MAX_UNROLL {
731            Self::gen_probestack_unroll(insts, tmp, guard_size, probe_count)
732        } else {
733            insts.push(Inst::StackProbeLoop {
734                guard_size,
735                probe_count,
736                tmp,
737            });
738        }
739    }
740
741    fn retval_temp_reg(_call_conv_of_callee: isa::CallConv) -> Writable<Reg> {
742        // Use x12 as a temp if needed: clobbered, not a
743        // retval.
744        Writable::from_reg(regs::x_reg(12))
745    }
746
747    fn exception_payload_regs(call_conv: isa::CallConv) -> &'static [Reg] {
748        const PAYLOAD_REGS: &'static [Reg] = &[regs::a0(), regs::a1()];
749        match call_conv {
750            isa::CallConv::SystemV | isa::CallConv::Tail | isa::CallConv::PreserveAll => {
751                PAYLOAD_REGS
752            }
753            _ => &[],
754        }
755    }
756}
757
758// NOTE: no V regs are callee save.
759const DEFAULT_CALLEE_SAVES: PRegSet = PRegSet::empty()
760    // X Regs
761    .with(px_reg(2))
762    .with(px_reg(8))
763    .with(px_reg(9))
764    .with(px_reg(18))
765    .with(px_reg(19))
766    .with(px_reg(20))
767    .with(px_reg(21))
768    .with(px_reg(22))
769    .with(px_reg(23))
770    .with(px_reg(24))
771    .with(px_reg(25))
772    .with(px_reg(26))
773    .with(px_reg(27))
774    // F Regs
775    .with(pf_reg(8))
776    .with(pf_reg(18))
777    .with(pf_reg(19))
778    .with(pf_reg(20))
779    .with(pf_reg(21))
780    .with(pf_reg(22))
781    .with(pf_reg(23))
782    .with(pf_reg(24))
783    .with(pf_reg(25))
784    .with(pf_reg(26))
785    .with(pf_reg(27));
786
787fn compute_clobber_size(clobbers: &[Writable<RealReg>]) -> u32 {
788    let mut clobbered_size = 0;
789    for reg in clobbers {
790        match reg.to_reg().class() {
791            RegClass::Int => {
792                clobbered_size += 8;
793            }
794            RegClass::Float => {
795                clobbered_size += 8;
796            }
797            RegClass::Vector => {
798                clobbered_size = align_to(clobbered_size, 16);
799                clobbered_size += 16;
800            }
801        }
802    }
803    align_to(clobbered_size, 16)
804}
805
806const DEFAULT_CLOBBERS: PRegSet = PRegSet::empty()
807    .with(px_reg(1))
808    .with(px_reg(5))
809    .with(px_reg(6))
810    .with(px_reg(7))
811    .with(px_reg(10))
812    .with(px_reg(11))
813    .with(px_reg(12))
814    .with(px_reg(13))
815    .with(px_reg(14))
816    .with(px_reg(15))
817    .with(px_reg(16))
818    .with(px_reg(17))
819    .with(px_reg(28))
820    .with(px_reg(29))
821    .with(px_reg(30))
822    .with(px_reg(31))
823    // F Regs
824    .with(pf_reg(0))
825    .with(pf_reg(1))
826    .with(pf_reg(2))
827    .with(pf_reg(3))
828    .with(pf_reg(4))
829    .with(pf_reg(5))
830    .with(pf_reg(6))
831    .with(pf_reg(7))
832    .with(pf_reg(9))
833    .with(pf_reg(10))
834    .with(pf_reg(11))
835    .with(pf_reg(12))
836    .with(pf_reg(13))
837    .with(pf_reg(14))
838    .with(pf_reg(15))
839    .with(pf_reg(16))
840    .with(pf_reg(17))
841    .with(pf_reg(28))
842    .with(pf_reg(29))
843    .with(pf_reg(30))
844    .with(pf_reg(31))
845    // V Regs - All vector regs get clobbered
846    .with(pv_reg(0))
847    .with(pv_reg(1))
848    .with(pv_reg(2))
849    .with(pv_reg(3))
850    .with(pv_reg(4))
851    .with(pv_reg(5))
852    .with(pv_reg(6))
853    .with(pv_reg(7))
854    .with(pv_reg(8))
855    .with(pv_reg(9))
856    .with(pv_reg(10))
857    .with(pv_reg(11))
858    .with(pv_reg(12))
859    .with(pv_reg(13))
860    .with(pv_reg(14))
861    .with(pv_reg(15))
862    .with(pv_reg(16))
863    .with(pv_reg(17))
864    .with(pv_reg(18))
865    .with(pv_reg(19))
866    .with(pv_reg(20))
867    .with(pv_reg(21))
868    .with(pv_reg(22))
869    .with(pv_reg(23))
870    .with(pv_reg(24))
871    .with(pv_reg(25))
872    .with(pv_reg(26))
873    .with(pv_reg(27))
874    .with(pv_reg(28))
875    .with(pv_reg(29))
876    .with(pv_reg(30))
877    .with(pv_reg(31));
878
879const ALL_CLOBBERS: PRegSet = PRegSet::empty()
880    // Specials: x0 is the zero register; x1 is the return address; x2 is SP.
881    .with(px_reg(3))
882    .with(px_reg(4))
883    .with(px_reg(5))
884    .with(px_reg(6))
885    .with(px_reg(7))
886    .with(px_reg(8))
887    .with(px_reg(9))
888    .with(px_reg(10))
889    .with(px_reg(11))
890    .with(px_reg(12))
891    .with(px_reg(13))
892    .with(px_reg(14))
893    .with(px_reg(15))
894    .with(px_reg(16))
895    .with(px_reg(17))
896    .with(px_reg(18))
897    .with(px_reg(19))
898    .with(px_reg(20))
899    .with(px_reg(21))
900    .with(px_reg(22))
901    .with(px_reg(23))
902    .with(px_reg(24))
903    .with(px_reg(25))
904    .with(px_reg(26))
905    .with(px_reg(27))
906    .with(px_reg(28))
907    .with(px_reg(29))
908    .with(px_reg(30))
909    .with(px_reg(31))
910    // F Regs
911    .with(pf_reg(0))
912    .with(pf_reg(1))
913    .with(pf_reg(2))
914    .with(pf_reg(3))
915    .with(pf_reg(4))
916    .with(pf_reg(5))
917    .with(pf_reg(6))
918    .with(pf_reg(7))
919    .with(pf_reg(8))
920    .with(pf_reg(9))
921    .with(pf_reg(10))
922    .with(pf_reg(11))
923    .with(pf_reg(12))
924    .with(pf_reg(13))
925    .with(pf_reg(14))
926    .with(pf_reg(15))
927    .with(pf_reg(16))
928    .with(pf_reg(17))
929    .with(pf_reg(18))
930    .with(pf_reg(19))
931    .with(pf_reg(20))
932    .with(pf_reg(21))
933    .with(pf_reg(22))
934    .with(pf_reg(23))
935    .with(pf_reg(24))
936    .with(pf_reg(25))
937    .with(pf_reg(26))
938    .with(pf_reg(27))
939    .with(pf_reg(28))
940    .with(pf_reg(29))
941    .with(pf_reg(30))
942    .with(pf_reg(31))
943    // V Regs
944    .with(pv_reg(0))
945    .with(pv_reg(1))
946    .with(pv_reg(2))
947    .with(pv_reg(3))
948    .with(pv_reg(4))
949    .with(pv_reg(5))
950    .with(pv_reg(6))
951    .with(pv_reg(7))
952    .with(pv_reg(8))
953    .with(pv_reg(9))
954    .with(pv_reg(10))
955    .with(pv_reg(11))
956    .with(pv_reg(12))
957    .with(pv_reg(13))
958    .with(pv_reg(14))
959    .with(pv_reg(15))
960    .with(pv_reg(16))
961    .with(pv_reg(17))
962    .with(pv_reg(18))
963    .with(pv_reg(19))
964    .with(pv_reg(20))
965    .with(pv_reg(21))
966    .with(pv_reg(22))
967    .with(pv_reg(23))
968    .with(pv_reg(24))
969    .with(pv_reg(25))
970    .with(pv_reg(26))
971    .with(pv_reg(27))
972    .with(pv_reg(28))
973    .with(pv_reg(29))
974    .with(pv_reg(30))
975    .with(pv_reg(31));
976
977const NO_CLOBBERS: PRegSet = PRegSet::empty();
978
979const fn create_reg_environment() -> MachineEnv {
980    // Some C Extension instructions can only use a subset of the registers.
981    // x8 - x15, f8 - f15, v8 - v15 so we should prefer to use those since
982    // they allow us to emit C instructions more often.
983    //
984    // In general the order of preference is:
985    //   1. Compressible Caller Saved registers.
986    //   2. Non-Compressible Caller Saved registers.
987    //   3. Compressible Callee Saved registers.
988    //   4. Non-Compressible Callee Saved registers.
989
990    let preferred_regs_by_class: [PRegSet; 3] = [
991        PRegSet::empty()
992            .with(px_reg(10))
993            .with(px_reg(11))
994            .with(px_reg(12))
995            .with(px_reg(13))
996            .with(px_reg(14))
997            .with(px_reg(15)),
998        PRegSet::empty()
999            .with(pf_reg(10))
1000            .with(pf_reg(11))
1001            .with(pf_reg(12))
1002            .with(pf_reg(13))
1003            .with(pf_reg(14))
1004            .with(pf_reg(15)),
1005        PRegSet::empty()
1006            .with(pv_reg(8))
1007            .with(pv_reg(9))
1008            .with(pv_reg(10))
1009            .with(pv_reg(11))
1010            .with(pv_reg(12))
1011            .with(pv_reg(13))
1012            .with(pv_reg(14))
1013            .with(pv_reg(15)),
1014    ];
1015
1016    let non_preferred_regs_by_class: [PRegSet; 3] = [
1017        // x0 - x4 are special registers, so we don't want to use them.
1018        // Omit x30 and x31 since they are the spilltmp registers.
1019        PRegSet::empty()
1020            .with(px_reg(5))
1021            .with(px_reg(6))
1022            .with(px_reg(7))
1023            // Start with the Non-Compressible Caller Saved registers.
1024            .with(px_reg(16))
1025            .with(px_reg(17))
1026            .with(px_reg(28))
1027            .with(px_reg(29))
1028            // The first Callee Saved register is x9 since its Compressible
1029            // Omit x8 since it's the frame pointer.
1030            .with(px_reg(9))
1031            // The rest of the Callee Saved registers are Non-Compressible
1032            .with(px_reg(18))
1033            .with(px_reg(19))
1034            .with(px_reg(20))
1035            .with(px_reg(21))
1036            .with(px_reg(22))
1037            .with(px_reg(23))
1038            .with(px_reg(24))
1039            .with(px_reg(25))
1040            .with(px_reg(26))
1041            .with(px_reg(27)),
1042        // Prefer Caller Saved registers.
1043        PRegSet::empty()
1044            .with(pf_reg(0))
1045            .with(pf_reg(1))
1046            .with(pf_reg(2))
1047            .with(pf_reg(3))
1048            .with(pf_reg(4))
1049            .with(pf_reg(5))
1050            .with(pf_reg(6))
1051            .with(pf_reg(7))
1052            .with(pf_reg(16))
1053            .with(pf_reg(17))
1054            .with(pf_reg(28))
1055            .with(pf_reg(29))
1056            .with(pf_reg(30))
1057            .with(pf_reg(31))
1058            // Once those are exhausted, we should prefer f8 and f9 since they are
1059            // callee saved, but compressible.
1060            .with(pf_reg(8))
1061            .with(pf_reg(9))
1062            .with(pf_reg(18))
1063            .with(pf_reg(19))
1064            .with(pf_reg(20))
1065            .with(pf_reg(21))
1066            .with(pf_reg(22))
1067            .with(pf_reg(23))
1068            .with(pf_reg(24))
1069            .with(pf_reg(25))
1070            .with(pf_reg(26))
1071            .with(pf_reg(27)),
1072        PRegSet::empty()
1073            .with(pv_reg(0))
1074            .with(pv_reg(1))
1075            .with(pv_reg(2))
1076            .with(pv_reg(3))
1077            .with(pv_reg(4))
1078            .with(pv_reg(5))
1079            .with(pv_reg(6))
1080            .with(pv_reg(7))
1081            .with(pv_reg(16))
1082            .with(pv_reg(17))
1083            .with(pv_reg(18))
1084            .with(pv_reg(19))
1085            .with(pv_reg(20))
1086            .with(pv_reg(21))
1087            .with(pv_reg(22))
1088            .with(pv_reg(23))
1089            .with(pv_reg(24))
1090            .with(pv_reg(25))
1091            .with(pv_reg(26))
1092            .with(pv_reg(27))
1093            .with(pv_reg(28))
1094            .with(pv_reg(29))
1095            .with(pv_reg(30))
1096            .with(pv_reg(31)),
1097    ];
1098
1099    MachineEnv {
1100        preferred_regs_by_class,
1101        non_preferred_regs_by_class,
1102        fixed_stack_slots: vec![],
1103        scratch_by_class: [None, None, None],
1104    }
1105}
1106
1107impl Riscv64MachineDeps {
1108    fn gen_probestack_unroll(
1109        insts: &mut SmallInstVec<Inst>,
1110        tmp: Writable<Reg>,
1111        guard_size: u32,
1112        probe_count: u32,
1113    ) {
1114        // When manually unrolling adjust the stack pointer and then write a zero
1115        // to the stack at that offset.
1116        //
1117        // We do this because valgrind expects us to never write beyond the stack
1118        // pointer and associated redzone.
1119        // See: https://github.com/bytecodealliance/wasmtime/issues/7454
1120
1121        // Store the adjust amount in a register upfront, so we don't have to
1122        // reload it for each probe. It's worth loading this as a negative and
1123        // using an `add` instruction since we have compressed versions of `add`
1124        // but not the `sub` instruction.
1125        insts.extend(Inst::load_constant_u64(tmp, (-(guard_size as i64)) as u64));
1126
1127        for _ in 0..probe_count {
1128            insts.push(Inst::AluRRR {
1129                alu_op: AluOPRRR::Add,
1130                rd: writable_stack_reg(),
1131                rs1: stack_reg(),
1132                rs2: tmp.to_reg(),
1133            });
1134
1135            insts.push(Inst::gen_store(
1136                AMode::SPOffset(0),
1137                zero_reg(),
1138                I32,
1139                MemFlagsData::trusted(),
1140            ));
1141        }
1142
1143        // Restore the stack pointer to its original value
1144        insts.extend(Self::gen_sp_reg_adjust((guard_size * probe_count) as i32));
1145    }
1146}