Path: blob/main/cranelift/codegen/src/isa/riscv64/abi.rs
1693 views
//! Implementation of a standard Riscv64 ABI.12use crate::ir;3use crate::ir::types::*;45use crate::isa;67use crate::isa::CallConv;8use crate::isa::riscv64::inst::*;9use crate::machinst::*;1011use crate::CodegenResult;12use crate::ir::LibCall;13use crate::ir::Signature;14use crate::isa::riscv64::settings::Flags as RiscvFlags;15use crate::isa::unwind::UnwindInst;16use crate::settings;17use alloc::boxed::Box;18use alloc::vec::Vec;19use regalloc2::{MachineEnv, PReg, PRegSet};2021use smallvec::{SmallVec, smallvec};22use std::borrow::ToOwned;23use std::sync::OnceLock;2425/// Support for the Riscv64 ABI from the callee side (within a function body).26pub(crate) type Riscv64Callee = Callee<Riscv64MachineDeps>;2728/// Riscv64-specific ABI behavior. This struct just serves as an implementation29/// point for the trait; it is never actually instantiated.30pub struct Riscv64MachineDeps;3132impl IsaFlags for RiscvFlags {}3334impl RiscvFlags {35pub(crate) fn min_vec_reg_size(&self) -> u64 {36let 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 minimum47// 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];5354for (has_flag, size) in entries.into_iter() {55if !has_flag {56continue;57}5859// Due to a limitation in regalloc2, we can't support types60// larger than 1024 bytes. So limit that here.61return std::cmp::min(size, 1024);62}6364return 0;65}66}6768impl ABIMachineSpec for Riscv64MachineDeps {69type I = Inst;70type F = RiscvFlags;7172/// This is the limit for the size of argument and return-value areas on the73/// stack. We place a reasonable limit here to avoid integer overflow issues74/// with 32-bit arithmetic: for now, 128 MB.75const STACK_ARG_RET_SIZE_LIMIT: u32 = 128 * 1024 * 1024;7677fn word_bits() -> u32 {786479}8081/// Return required stack alignment in bytes.82fn stack_align(_call_conv: isa::CallConv) -> u32 {831684}8586fn compute_arg_locs(87call_conv: isa::CallConv,88flags: &settings::Flags,89params: &[ir::AbiParam],90args_or_rets: ArgsOrRets,91add_ret_area_ptr: bool,92mut args: ArgsAccumulator,93) -> CodegenResult<(u32, Option<usize>)> {94// This implements the LP64D RISC-V ABI.9596assert_ne!(97call_conv,98isa::CallConv::Winch,99"riscv64 does not support the 'winch' calling convention yet"100);101102// All registers that can be used as parameters or rets.103// both start and end are included.104let (x_start, x_end, f_start, f_end) = match args_or_rets {105ArgsOrRets::Args => (10, 17, 10, 17),106ArgsOrRets::Rets => (10, 11, 10, 11),107};108let mut next_x_reg = x_start;109let mut next_f_reg = f_start;110// Stack space.111let mut next_stack: u32 = 0;112113let ret_area_ptr = if add_ret_area_ptr {114assert!(ArgsOrRets::Args == args_or_rets);115next_x_reg += 1;116Some(ABIArg::reg(117x_reg(x_start).to_real_reg().unwrap(),118I64,119ir::ArgumentExtension::None,120ir::ArgumentPurpose::Normal,121))122} else {123None124};125126for param in params {127if let ir::ArgumentPurpose::StructArgument(_) = param.purpose {128panic!(129"StructArgument parameters are not supported on riscv64. \130Use regular pointer arguments instead."131);132}133134// Find regclass(es) of the register(s) used to store a value of this type.135let (rcs, reg_tys) = Inst::rc_for_type(param.value_type)?;136let mut slots = ABIArgSlotVec::new();137for (rc, reg_ty) in rcs.iter().zip(reg_tys.iter()) {138let next_reg = if (next_x_reg <= x_end) && *rc == RegClass::Int {139let x = Some(x_reg(next_x_reg));140next_x_reg += 1;141x142} else if (next_f_reg <= f_end) && *rc == RegClass::Float {143let x = Some(f_reg(next_f_reg));144next_f_reg += 1;145x146} else {147None148};149if let Some(reg) = next_reg {150slots.push(ABIArgSlot::Reg {151reg: reg.to_real_reg().unwrap(),152ty: *reg_ty,153extension: param.extension,154});155} else {156if args_or_rets == ArgsOrRets::Rets && !flags.enable_multi_ret_implicit_sret() {157return Err(crate::CodegenError::Unsupported(158"Too many return values to fit in registers. \159Use a StructReturn argument instead. (#9510)"160.to_owned(),161));162}163164// Compute size and 16-byte stack alignment happens165// separately after all args.166let size = reg_ty.bits() / 8;167let size = std::cmp::max(size, 8);168// Align.169debug_assert!(size.is_power_of_two());170next_stack = align_to(next_stack, size);171slots.push(ABIArgSlot::Stack {172offset: next_stack as i64,173ty: *reg_ty,174extension: param.extension,175});176next_stack += size;177}178}179args.push(ABIArg::Slots {180slots,181purpose: param.purpose,182});183}184let pos = if let Some(ret_area_ptr) = ret_area_ptr {185args.push_non_formal(ret_area_ptr);186Some(args.args().len() - 1)187} else {188None189};190191next_stack = align_to(next_stack, Self::stack_align(call_conv));192193Ok((next_stack, pos))194}195196fn gen_load_stack(mem: StackAMode, into_reg: Writable<Reg>, ty: Type) -> Inst {197Inst::gen_load(into_reg, mem.into(), ty, MemFlags::trusted())198}199200fn gen_store_stack(mem: StackAMode, from_reg: Reg, ty: Type) -> Inst {201Inst::gen_store(mem.into(), from_reg, ty, MemFlags::trusted())202}203204fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Inst {205Inst::gen_move(to_reg, from_reg, ty)206}207208fn gen_extend(209to_reg: Writable<Reg>,210from_reg: Reg,211signed: bool,212from_bits: u8,213to_bits: u8,214) -> Inst {215assert!(from_bits < to_bits);216Inst::Extend {217rd: to_reg,218rn: from_reg,219signed,220from_bits,221to_bits,222}223}224225fn get_ext_mode(226_call_conv: isa::CallConv,227specified: ir::ArgumentExtension,228) -> ir::ArgumentExtension {229specified230}231232fn gen_args(args: Vec<ArgPair>) -> Inst {233Inst::Args { args }234}235236fn gen_rets(rets: Vec<RetPair>) -> Inst {237Inst::Rets { rets }238}239240fn get_stacklimit_reg(_call_conv: isa::CallConv) -> Reg {241spilltmp_reg()242}243244fn gen_add_imm(245_call_conv: isa::CallConv,246into_reg: Writable<Reg>,247from_reg: Reg,248imm: u32,249) -> SmallInstVec<Inst> {250let mut insts = SmallInstVec::new();251if let Some(imm12) = Imm12::maybe_from_u64(imm as u64) {252insts.push(Inst::AluRRImm12 {253alu_op: AluOPRRI::Addi,254rd: into_reg,255rs: from_reg,256imm12,257});258} else {259insts.extend(Inst::load_constant_u32(260writable_spilltmp_reg2(),261imm as u64,262));263insts.push(Inst::AluRRR {264alu_op: AluOPRRR::Add,265rd: into_reg,266rs1: spilltmp_reg2(),267rs2: from_reg,268});269}270insts271}272273fn gen_stack_lower_bound_trap(limit_reg: Reg) -> SmallInstVec<Inst> {274let mut insts = SmallVec::new();275insts.push(Inst::TrapIf {276cc: IntCC::UnsignedLessThan,277rs1: stack_reg(),278rs2: limit_reg,279trap_code: ir::TrapCode::STACK_OVERFLOW,280});281insts282}283284fn gen_get_stack_addr(mem: StackAMode, into_reg: Writable<Reg>) -> Inst {285Inst::LoadAddr {286rd: into_reg,287mem: mem.into(),288}289}290291fn gen_load_base_offset(into_reg: Writable<Reg>, base: Reg, offset: i32, ty: Type) -> Inst {292let mem = AMode::RegOffset(base, offset as i64);293Inst::gen_load(into_reg, mem, ty, MemFlags::trusted())294}295296fn gen_store_base_offset(base: Reg, offset: i32, from_reg: Reg, ty: Type) -> Inst {297let mem = AMode::RegOffset(base, offset as i64);298Inst::gen_store(mem, from_reg, ty, MemFlags::trusted())299}300301fn gen_sp_reg_adjust(amount: i32) -> SmallInstVec<Inst> {302let mut insts = SmallVec::new();303304if amount == 0 {305return insts;306}307308if let Some(imm) = Imm12::maybe_from_i64(amount as i64) {309insts.push(Inst::AluRRImm12 {310alu_op: AluOPRRI::Addi,311rd: writable_stack_reg(),312rs: stack_reg(),313imm12: imm,314})315} else {316let tmp = writable_spilltmp_reg();317insts.extend(Inst::load_constant_u64(tmp, amount as i64 as u64));318insts.push(Inst::AluRRR {319alu_op: AluOPRRR::Add,320rd: writable_stack_reg(),321rs1: stack_reg(),322rs2: tmp.to_reg(),323});324}325326insts327}328329fn gen_prologue_frame_setup(330_call_conv: isa::CallConv,331flags: &settings::Flags,332_isa_flags: &RiscvFlags,333frame_layout: &FrameLayout,334) -> SmallInstVec<Inst> {335let mut insts = SmallVec::new();336337if frame_layout.setup_area_size > 0 {338// add sp,sp,-16 ;; alloc stack space for fp.339// sd ra,8(sp) ;; save ra.340// sd fp,0(sp) ;; store old fp.341// mv fp,sp ;; set fp to sp.342insts.extend(Self::gen_sp_reg_adjust(-16));343insts.push(Inst::gen_store(344AMode::SPOffset(8),345link_reg(),346I64,347MemFlags::trusted(),348));349insts.push(Inst::gen_store(350AMode::SPOffset(0),351fp_reg(),352I64,353MemFlags::trusted(),354));355356if flags.unwind_info() {357insts.push(Inst::Unwind {358inst: UnwindInst::PushFrameRegs {359offset_upward_to_caller_sp: frame_layout.setup_area_size,360},361});362}363insts.push(Inst::Mov {364rd: writable_fp_reg(),365rm: stack_reg(),366ty: I64,367});368}369370insts371}372/// reverse of gen_prologue_frame_setup.373fn gen_epilogue_frame_restore(374call_conv: isa::CallConv,375_flags: &settings::Flags,376_isa_flags: &RiscvFlags,377frame_layout: &FrameLayout,378) -> SmallInstVec<Inst> {379let mut insts = SmallVec::new();380381if frame_layout.setup_area_size > 0 {382insts.push(Inst::gen_load(383writable_link_reg(),384AMode::SPOffset(8),385I64,386MemFlags::trusted(),387));388insts.push(Inst::gen_load(389writable_fp_reg(),390AMode::SPOffset(0),391I64,392MemFlags::trusted(),393));394insts.extend(Self::gen_sp_reg_adjust(16));395}396397if call_conv == isa::CallConv::Tail && frame_layout.tail_args_size > 0 {398insts.extend(Self::gen_sp_reg_adjust(399frame_layout.tail_args_size.try_into().unwrap(),400));401}402403insts404}405406fn gen_return(407_call_conv: isa::CallConv,408_isa_flags: &RiscvFlags,409_frame_layout: &FrameLayout,410) -> SmallInstVec<Inst> {411smallvec![Inst::Ret {}]412}413414fn gen_probestack(insts: &mut SmallInstVec<Self::I>, frame_size: u32) {415insts.extend(Inst::load_constant_u32(writable_a0(), frame_size as u64));416let mut info = CallInfo::empty(417ExternalName::LibCall(LibCall::Probestack),418CallConv::SystemV,419);420info.uses.push(CallArgPair {421vreg: a0(),422preg: a0(),423});424insts.push(Inst::Call {425info: Box::new(info),426});427}428429fn gen_clobber_save(430_call_conv: isa::CallConv,431flags: &settings::Flags,432frame_layout: &FrameLayout,433) -> SmallVec<[Inst; 16]> {434let mut insts = SmallVec::new();435let setup_frame = frame_layout.setup_area_size > 0;436437let incoming_args_diff = frame_layout.tail_args_size - frame_layout.incoming_args_size;438if incoming_args_diff > 0 {439// Decrement SP by the amount of additional incoming argument space we need440insts.extend(Self::gen_sp_reg_adjust(-(incoming_args_diff as i32)));441442if setup_frame {443// Write the lr position on the stack again, as it hasn't changed since it was444// pushed in `gen_prologue_frame_setup`445insts.push(Inst::gen_store(446AMode::SPOffset(8),447link_reg(),448I64,449MemFlags::trusted(),450));451insts.push(Inst::gen_load(452writable_fp_reg(),453AMode::SPOffset(i64::from(incoming_args_diff)),454I64,455MemFlags::trusted(),456));457insts.push(Inst::gen_store(458AMode::SPOffset(0),459fp_reg(),460I64,461MemFlags::trusted(),462));463464// Finally, sync the frame pointer with SP465insts.push(Inst::gen_move(writable_fp_reg(), stack_reg(), I64));466}467}468469if flags.unwind_info() && setup_frame {470// The *unwind* frame (but not the actual frame) starts at the471// clobbers, just below the saved FP/LR pair.472insts.push(Inst::Unwind {473inst: UnwindInst::DefineNewFrame {474offset_downward_to_clobbers: frame_layout.clobber_size,475offset_upward_to_caller_sp: frame_layout.setup_area_size,476},477});478}479480// Adjust the stack pointer downward for clobbers, the function fixed481// frame (spillslots and storage slots), and outgoing arguments.482let stack_size = frame_layout.clobber_size483+ frame_layout.fixed_frame_storage_size484+ frame_layout.outgoing_args_size;485486// Store each clobbered register in order at offsets from SP,487// placing them above the fixed frame slots.488if stack_size > 0 {489insts.extend(Self::gen_sp_reg_adjust(-(stack_size as i32)));490491let mut cur_offset = 8;492for reg in &frame_layout.clobbered_callee_saves {493let r_reg = reg.to_reg();494let ty = match r_reg.class() {495RegClass::Int => I64,496RegClass::Float => F64,497RegClass::Vector => unimplemented!("Vector Clobber Saves"),498};499insts.push(Inst::gen_store(500AMode::SPOffset((stack_size - cur_offset) as i64),501Reg::from(reg.to_reg()),502ty,503MemFlags::trusted(),504));505506if flags.unwind_info() {507insts.push(Inst::Unwind {508inst: UnwindInst::SaveReg {509clobber_offset: frame_layout.clobber_size - cur_offset,510reg: r_reg,511},512});513}514515cur_offset += 8516}517}518insts519}520521fn gen_clobber_restore(522_call_conv: isa::CallConv,523_flags: &settings::Flags,524frame_layout: &FrameLayout,525) -> SmallVec<[Inst; 16]> {526let mut insts = SmallVec::new();527528let stack_size = frame_layout.clobber_size529+ frame_layout.fixed_frame_storage_size530+ frame_layout.outgoing_args_size;531532let mut cur_offset = 8;533for reg in &frame_layout.clobbered_callee_saves {534let rreg = reg.to_reg();535let ty = match rreg.class() {536RegClass::Int => I64,537RegClass::Float => F64,538RegClass::Vector => unimplemented!("Vector Clobber Restores"),539};540insts.push(Inst::gen_load(541reg.map(Reg::from),542AMode::SPOffset(i64::from(stack_size - cur_offset)),543ty,544MemFlags::trusted(),545));546cur_offset += 8547}548549if stack_size > 0 {550insts.extend(Self::gen_sp_reg_adjust(stack_size as i32));551}552553insts554}555556fn gen_memcpy<F: FnMut(Type) -> Writable<Reg>>(557call_conv: isa::CallConv,558dst: Reg,559src: Reg,560size: usize,561mut alloc_tmp: F,562) -> SmallVec<[Self::I; 8]> {563let mut insts = SmallVec::new();564let arg0 = Writable::from_reg(x_reg(10));565let arg1 = Writable::from_reg(x_reg(11));566let arg2 = Writable::from_reg(x_reg(12));567let tmp = alloc_tmp(Self::word_type());568insts.extend(Inst::load_constant_u64(tmp, size as u64));569insts.push(Inst::Call {570info: Box::new(CallInfo {571dest: ExternalName::LibCall(LibCall::Memcpy),572uses: smallvec![573CallArgPair {574vreg: dst,575preg: arg0.to_reg()576},577CallArgPair {578vreg: src,579preg: arg1.to_reg()580},581CallArgPair {582vreg: tmp.to_reg(),583preg: arg2.to_reg()584}585],586defs: smallvec![],587clobbers: Self::get_regs_clobbered_by_call(call_conv, false),588caller_conv: call_conv,589callee_conv: call_conv,590callee_pop_size: 0,591try_call_info: None,592}),593});594insts595}596597fn get_number_of_spillslots_for_value(598rc: RegClass,599_target_vector_bytes: u32,600isa_flags: &RiscvFlags,601) -> u32 {602// We allocate in terms of 8-byte slots.603match rc {604RegClass::Int => 1,605RegClass::Float => 1,606RegClass::Vector => (isa_flags.min_vec_reg_size() / 8) as u32,607}608}609610fn get_machine_env(_flags: &settings::Flags, _call_conv: isa::CallConv) -> &MachineEnv {611static MACHINE_ENV: OnceLock<MachineEnv> = OnceLock::new();612MACHINE_ENV.get_or_init(create_reg_environment)613}614615fn get_regs_clobbered_by_call(616call_conv_of_callee: isa::CallConv,617is_exception: bool,618) -> PRegSet {619match call_conv_of_callee {620isa::CallConv::Tail if is_exception => ALL_CLOBBERS,621_ => DEFAULT_CLOBBERS,622}623}624625fn compute_frame_layout(626_call_conv: isa::CallConv,627flags: &settings::Flags,628_sig: &Signature,629regs: &[Writable<RealReg>],630function_calls: FunctionCalls,631incoming_args_size: u32,632tail_args_size: u32,633stackslots_size: u32,634fixed_frame_storage_size: u32,635outgoing_args_size: u32,636) -> FrameLayout {637let mut regs: Vec<Writable<RealReg>> = regs638.iter()639.cloned()640.filter(|r| DEFAULT_CALLEE_SAVES.contains(r.to_reg().into()))641.collect();642643regs.sort_unstable();644645// Compute clobber size.646let clobber_size = compute_clobber_size(®s);647648// Compute linkage frame size.649let setup_area_size = if flags.preserve_frame_pointers()650|| function_calls != FunctionCalls::None651// The function arguments that are passed on the stack are addressed652// relative to the Frame Pointer.653|| incoming_args_size > 0654|| clobber_size > 0655|| fixed_frame_storage_size > 0656{65716 // FP, LR658} else {6590660};661662// Return FrameLayout structure.663FrameLayout {664word_bytes: 8,665incoming_args_size,666tail_args_size,667setup_area_size,668clobber_size,669fixed_frame_storage_size,670stackslots_size,671outgoing_args_size,672clobbered_callee_saves: regs,673function_calls,674}675}676677fn gen_inline_probestack(678insts: &mut SmallInstVec<Self::I>,679_call_conv: isa::CallConv,680frame_size: u32,681guard_size: u32,682) {683// Unroll at most n consecutive probes, before falling back to using a loop684const PROBE_MAX_UNROLL: u32 = 3;685686// Calculate how many probes we need to perform. Round down, as we only687// need to probe whole guard_size regions we'd otherwise skip over.688let probe_count = frame_size / guard_size;689if probe_count == 0 {690// No probe necessary691return;692}693694// Must be a caller-saved register that is not an argument.695let tmp = Writable::from_reg(x_reg(28)); // t3696697if probe_count <= PROBE_MAX_UNROLL {698Self::gen_probestack_unroll(insts, tmp, guard_size, probe_count)699} else {700insts.push(Inst::StackProbeLoop {701guard_size,702probe_count,703tmp,704});705}706}707708fn retval_temp_reg(_call_conv_of_callee: isa::CallConv) -> Writable<Reg> {709// Use x12 as a temp if needed: clobbered, not a710// retval.711Writable::from_reg(regs::x_reg(12))712}713714fn exception_payload_regs(call_conv: isa::CallConv) -> &'static [Reg] {715const PAYLOAD_REGS: &'static [Reg] = &[regs::a0(), regs::a1()];716match call_conv {717isa::CallConv::SystemV | isa::CallConv::Tail => PAYLOAD_REGS,718_ => &[],719}720}721}722723// NOTE: no V regs are callee save.724const DEFAULT_CALLEE_SAVES: PRegSet = PRegSet::empty()725// X Regs726.with(px_reg(2))727.with(px_reg(8))728.with(px_reg(9))729.with(px_reg(18))730.with(px_reg(19))731.with(px_reg(20))732.with(px_reg(21))733.with(px_reg(22))734.with(px_reg(23))735.with(px_reg(24))736.with(px_reg(25))737.with(px_reg(26))738.with(px_reg(27))739// F Regs740.with(pf_reg(8))741.with(pf_reg(18))742.with(pf_reg(19))743.with(pf_reg(20))744.with(pf_reg(21))745.with(pf_reg(22))746.with(pf_reg(23))747.with(pf_reg(24))748.with(pf_reg(25))749.with(pf_reg(26))750.with(pf_reg(27));751752fn compute_clobber_size(clobbers: &[Writable<RealReg>]) -> u32 {753let mut clobbered_size = 0;754for reg in clobbers {755match reg.to_reg().class() {756RegClass::Int => {757clobbered_size += 8;758}759RegClass::Float => {760clobbered_size += 8;761}762RegClass::Vector => unimplemented!("Vector Size Clobbered"),763}764}765align_to(clobbered_size, 16)766}767768const DEFAULT_CLOBBERS: PRegSet = PRegSet::empty()769.with(px_reg(1))770.with(px_reg(5))771.with(px_reg(6))772.with(px_reg(7))773.with(px_reg(10))774.with(px_reg(11))775.with(px_reg(12))776.with(px_reg(13))777.with(px_reg(14))778.with(px_reg(15))779.with(px_reg(16))780.with(px_reg(17))781.with(px_reg(28))782.with(px_reg(29))783.with(px_reg(30))784.with(px_reg(31))785// F Regs786.with(pf_reg(0))787.with(pf_reg(1))788.with(pf_reg(2))789.with(pf_reg(3))790.with(pf_reg(4))791.with(pf_reg(5))792.with(pf_reg(6))793.with(pf_reg(7))794.with(pf_reg(9))795.with(pf_reg(10))796.with(pf_reg(11))797.with(pf_reg(12))798.with(pf_reg(13))799.with(pf_reg(14))800.with(pf_reg(15))801.with(pf_reg(16))802.with(pf_reg(17))803.with(pf_reg(28))804.with(pf_reg(29))805.with(pf_reg(30))806.with(pf_reg(31))807// V Regs - All vector regs get clobbered808.with(pv_reg(0))809.with(pv_reg(1))810.with(pv_reg(2))811.with(pv_reg(3))812.with(pv_reg(4))813.with(pv_reg(5))814.with(pv_reg(6))815.with(pv_reg(7))816.with(pv_reg(8))817.with(pv_reg(9))818.with(pv_reg(10))819.with(pv_reg(11))820.with(pv_reg(12))821.with(pv_reg(13))822.with(pv_reg(14))823.with(pv_reg(15))824.with(pv_reg(16))825.with(pv_reg(17))826.with(pv_reg(18))827.with(pv_reg(19))828.with(pv_reg(20))829.with(pv_reg(21))830.with(pv_reg(22))831.with(pv_reg(23))832.with(pv_reg(24))833.with(pv_reg(25))834.with(pv_reg(26))835.with(pv_reg(27))836.with(pv_reg(28))837.with(pv_reg(29))838.with(pv_reg(30))839.with(pv_reg(31));840841const ALL_CLOBBERS: PRegSet = PRegSet::empty()842// Specials: x0 is the zero register; x1 is the return address; x2 is SP.843.with(px_reg(3))844.with(px_reg(4))845.with(px_reg(5))846.with(px_reg(6))847.with(px_reg(7))848.with(px_reg(8))849.with(px_reg(9))850.with(px_reg(10))851.with(px_reg(11))852.with(px_reg(12))853.with(px_reg(13))854.with(px_reg(14))855.with(px_reg(15))856.with(px_reg(16))857.with(px_reg(17))858.with(px_reg(18))859.with(px_reg(19))860.with(px_reg(20))861.with(px_reg(21))862.with(px_reg(22))863.with(px_reg(23))864.with(px_reg(24))865.with(px_reg(25))866.with(px_reg(26))867.with(px_reg(27))868.with(px_reg(28))869.with(px_reg(29))870.with(px_reg(30))871.with(px_reg(31))872// F Regs873.with(pf_reg(0))874.with(pf_reg(1))875.with(pf_reg(2))876.with(pf_reg(3))877.with(pf_reg(4))878.with(pf_reg(5))879.with(pf_reg(6))880.with(pf_reg(7))881.with(pf_reg(8))882.with(pf_reg(9))883.with(pf_reg(10))884.with(pf_reg(11))885.with(pf_reg(12))886.with(pf_reg(13))887.with(pf_reg(14))888.with(pf_reg(15))889.with(pf_reg(16))890.with(pf_reg(17))891.with(pf_reg(18))892.with(pf_reg(19))893.with(pf_reg(20))894.with(pf_reg(21))895.with(pf_reg(22))896.with(pf_reg(23))897.with(pf_reg(24))898.with(pf_reg(25))899.with(pf_reg(26))900.with(pf_reg(27))901.with(pf_reg(28))902.with(pf_reg(29))903.with(pf_reg(30))904.with(pf_reg(31))905// V Regs906.with(pv_reg(0))907.with(pv_reg(1))908.with(pv_reg(2))909.with(pv_reg(3))910.with(pv_reg(4))911.with(pv_reg(5))912.with(pv_reg(6))913.with(pv_reg(7))914.with(pv_reg(8))915.with(pv_reg(9))916.with(pv_reg(10))917.with(pv_reg(11))918.with(pv_reg(12))919.with(pv_reg(13))920.with(pv_reg(14))921.with(pv_reg(15))922.with(pv_reg(16))923.with(pv_reg(17))924.with(pv_reg(18))925.with(pv_reg(19))926.with(pv_reg(20))927.with(pv_reg(21))928.with(pv_reg(22))929.with(pv_reg(23))930.with(pv_reg(24))931.with(pv_reg(25))932.with(pv_reg(26))933.with(pv_reg(27))934.with(pv_reg(28))935.with(pv_reg(29))936.with(pv_reg(30))937.with(pv_reg(31));938939fn create_reg_environment() -> MachineEnv {940// Some C Extension instructions can only use a subset of the registers.941// x8 - x15, f8 - f15, v8 - v15 so we should prefer to use those since942// they allow us to emit C instructions more often.943//944// In general the order of preference is:945// 1. Compressible Caller Saved registers.946// 2. Non-Compressible Caller Saved registers.947// 3. Compressible Callee Saved registers.948// 4. Non-Compressible Callee Saved registers.949950let preferred_regs_by_class: [Vec<PReg>; 3] = {951let x_registers: Vec<PReg> = (10..=15).map(px_reg).collect();952let f_registers: Vec<PReg> = (10..=15).map(pf_reg).collect();953let v_registers: Vec<PReg> = (8..=15).map(pv_reg).collect();954955[x_registers, f_registers, v_registers]956};957958let non_preferred_regs_by_class: [Vec<PReg>; 3] = {959// x0 - x4 are special registers, so we don't want to use them.960// Omit x30 and x31 since they are the spilltmp registers.961962// Start with the Non-Compressible Caller Saved registers.963let x_registers: Vec<PReg> = (5..=7)964.chain(16..=17)965.chain(28..=29)966// The first Callee Saved register is x9 since its Compressible967// Omit x8 since it's the frame pointer.968.chain(9..=9)969// The rest of the Callee Saved registers are Non-Compressible970.chain(18..=27)971.map(px_reg)972.collect();973974// Prefer Caller Saved registers.975let f_registers: Vec<PReg> = (0..=7)976.chain(16..=17)977.chain(28..=31)978// Once those are exhausted, we should prefer f8 and f9 since they are979// callee saved, but compressible.980.chain(8..=9)981.chain(18..=27)982.map(pf_reg)983.collect();984985let v_registers = (0..=7).chain(16..=31).map(pv_reg).collect();986987[x_registers, f_registers, v_registers]988};989990MachineEnv {991preferred_regs_by_class,992non_preferred_regs_by_class,993fixed_stack_slots: vec![],994scratch_by_class: [None, None, None],995}996}997998impl Riscv64MachineDeps {999fn gen_probestack_unroll(1000insts: &mut SmallInstVec<Inst>,1001tmp: Writable<Reg>,1002guard_size: u32,1003probe_count: u32,1004) {1005// When manually unrolling adjust the stack pointer and then write a zero1006// to the stack at that offset.1007//1008// We do this because valgrind expects us to never write beyond the stack1009// pointer and associated redzone.1010// See: https://github.com/bytecodealliance/wasmtime/issues/745410111012// Store the adjust amount in a register upfront, so we don't have to1013// reload it for each probe. It's worth loading this as a negative and1014// using an `add` instruction since we have compressed versions of `add`1015// but not the `sub` instruction.1016insts.extend(Inst::load_constant_u64(tmp, (-(guard_size as i64)) as u64));10171018for _ in 0..probe_count {1019insts.push(Inst::AluRRR {1020alu_op: AluOPRRR::Add,1021rd: writable_stack_reg(),1022rs1: stack_reg(),1023rs2: tmp.to_reg(),1024});10251026insts.push(Inst::gen_store(1027AMode::SPOffset(0),1028zero_reg(),1029I32,1030MemFlags::trusted(),1031));1032}10331034// Restore the stack pointer to its original value1035insts.extend(Self::gen_sp_reg_adjust((guard_size * probe_count) as i32));1036}1037}103810391040