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