Path: blob/main/cranelift/codegen/src/isa/riscv64/abi.rs
3090 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 alloc::borrow::ToOwned;22use smallvec::{SmallVec, smallvec};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 core::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 = core::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 = 0;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 => I8X16,498};499cur_offset = align_to(cur_offset, ty.bytes());500insts.push(Inst::gen_store(501AMode::SPOffset(i64::from(stack_size - cur_offset - ty.bytes())),502Reg::from(reg.to_reg()),503ty,504MemFlags::trusted(),505));506507if flags.unwind_info() {508insts.push(Inst::Unwind {509inst: UnwindInst::SaveReg {510clobber_offset: frame_layout.clobber_size - cur_offset - ty.bytes(),511reg: r_reg,512},513});514}515516cur_offset += ty.bytes();517assert!(cur_offset <= stack_size);518}519}520insts521}522523fn gen_clobber_restore(524_call_conv: isa::CallConv,525_flags: &settings::Flags,526frame_layout: &FrameLayout,527) -> SmallVec<[Inst; 16]> {528let mut insts = SmallVec::new();529530let stack_size = frame_layout.clobber_size531+ frame_layout.fixed_frame_storage_size532+ frame_layout.outgoing_args_size;533let mut cur_offset = 0;534535for reg in &frame_layout.clobbered_callee_saves {536let rreg = reg.to_reg();537let ty = match rreg.class() {538RegClass::Int => I64,539RegClass::Float => F64,540RegClass::Vector => I8X16,541};542cur_offset = align_to(cur_offset, ty.bytes());543insts.push(Inst::gen_load(544reg.map(Reg::from),545AMode::SPOffset(i64::from(stack_size - cur_offset - ty.bytes())),546ty,547MemFlags::trusted(),548));549cur_offset += ty.bytes();550}551552if stack_size > 0 {553insts.extend(Self::gen_sp_reg_adjust(stack_size as i32));554}555556insts557}558559fn gen_memcpy<F: FnMut(Type) -> Writable<Reg>>(560call_conv: isa::CallConv,561dst: Reg,562src: Reg,563size: usize,564mut alloc_tmp: F,565) -> SmallVec<[Self::I; 8]> {566let mut insts = SmallVec::new();567let arg0 = Writable::from_reg(x_reg(10));568let arg1 = Writable::from_reg(x_reg(11));569let arg2 = Writable::from_reg(x_reg(12));570let tmp = alloc_tmp(Self::word_type());571insts.extend(Inst::load_constant_u64(tmp, size as u64));572insts.push(Inst::Call {573info: Box::new(CallInfo {574dest: ExternalName::LibCall(LibCall::Memcpy),575uses: smallvec![576CallArgPair {577vreg: dst,578preg: arg0.to_reg()579},580CallArgPair {581vreg: src,582preg: arg1.to_reg()583},584CallArgPair {585vreg: tmp.to_reg(),586preg: arg2.to_reg()587}588],589defs: smallvec![],590clobbers: Self::get_regs_clobbered_by_call(call_conv, false),591caller_conv: call_conv,592callee_conv: call_conv,593callee_pop_size: 0,594try_call_info: None,595patchable: false,596}),597});598insts599}600601fn get_number_of_spillslots_for_value(602rc: RegClass,603_target_vector_bytes: u32,604isa_flags: &RiscvFlags,605) -> u32 {606// We allocate in terms of 8-byte slots.607match rc {608RegClass::Int => 1,609RegClass::Float => 1,610RegClass::Vector => (isa_flags.min_vec_reg_size() / 8) as u32,611}612}613614fn get_machine_env(_flags: &settings::Flags, _call_conv: isa::CallConv) -> &MachineEnv {615static MACHINE_ENV: OnceLock<MachineEnv> = OnceLock::new();616MACHINE_ENV.get_or_init(create_reg_environment)617}618619fn get_regs_clobbered_by_call(620call_conv_of_callee: isa::CallConv,621is_exception: bool,622) -> PRegSet {623match call_conv_of_callee {624isa::CallConv::Tail if is_exception => ALL_CLOBBERS,625// Note that "PreserveAll" actually preserves nothing at626// the callsite if used for a `try_call`, because the627// unwinder ABI for `try_call`s is still "no clobbered628// register restores" for this ABI (so as to work with629// Wasmtime).630isa::CallConv::PreserveAll if is_exception => ALL_CLOBBERS,631isa::CallConv::PreserveAll => NO_CLOBBERS,632_ => DEFAULT_CLOBBERS,633}634}635636fn compute_frame_layout(637call_conv: isa::CallConv,638flags: &settings::Flags,639_sig: &Signature,640regs: &[Writable<RealReg>],641function_calls: FunctionCalls,642incoming_args_size: u32,643tail_args_size: u32,644stackslots_size: u32,645fixed_frame_storage_size: u32,646outgoing_args_size: u32,647) -> FrameLayout {648let is_callee_saved = |reg: &Writable<RealReg>| match call_conv {649isa::CallConv::PreserveAll => true,650_ => DEFAULT_CALLEE_SAVES.contains(reg.to_reg().into()),651};652let mut regs: Vec<Writable<RealReg>> =653regs.iter().cloned().filter(is_callee_saved).collect();654655regs.sort_unstable();656657// Compute clobber size.658let clobber_size = compute_clobber_size(®s);659660// Compute linkage frame size.661let setup_area_size = if flags.preserve_frame_pointers()662|| function_calls != FunctionCalls::None663// The function arguments that are passed on the stack are addressed664// relative to the Frame Pointer.665|| incoming_args_size > 0666|| clobber_size > 0667|| fixed_frame_storage_size > 0668{66916 // FP, LR670} else {6710672};673674// Return FrameLayout structure.675FrameLayout {676word_bytes: 8,677incoming_args_size,678tail_args_size,679setup_area_size,680clobber_size,681fixed_frame_storage_size,682stackslots_size,683outgoing_args_size,684clobbered_callee_saves: regs,685function_calls,686}687}688689fn gen_inline_probestack(690insts: &mut SmallInstVec<Self::I>,691_call_conv: isa::CallConv,692frame_size: u32,693guard_size: u32,694) {695// Unroll at most n consecutive probes, before falling back to using a loop696const PROBE_MAX_UNROLL: u32 = 3;697698// Calculate how many probes we need to perform. Round down, as we only699// need to probe whole guard_size regions we'd otherwise skip over.700let probe_count = frame_size / guard_size;701if probe_count == 0 {702// No probe necessary703return;704}705706// Must be a caller-saved register that is not an argument.707let tmp = Writable::from_reg(x_reg(28)); // t3708709if probe_count <= PROBE_MAX_UNROLL {710Self::gen_probestack_unroll(insts, tmp, guard_size, probe_count)711} else {712insts.push(Inst::StackProbeLoop {713guard_size,714probe_count,715tmp,716});717}718}719720fn retval_temp_reg(_call_conv_of_callee: isa::CallConv) -> Writable<Reg> {721// Use x12 as a temp if needed: clobbered, not a722// retval.723Writable::from_reg(regs::x_reg(12))724}725726fn exception_payload_regs(call_conv: isa::CallConv) -> &'static [Reg] {727const PAYLOAD_REGS: &'static [Reg] = &[regs::a0(), regs::a1()];728match call_conv {729isa::CallConv::SystemV | isa::CallConv::Tail | isa::CallConv::PreserveAll => {730PAYLOAD_REGS731}732_ => &[],733}734}735}736737// NOTE: no V regs are callee save.738const DEFAULT_CALLEE_SAVES: PRegSet = PRegSet::empty()739// X Regs740.with(px_reg(2))741.with(px_reg(8))742.with(px_reg(9))743.with(px_reg(18))744.with(px_reg(19))745.with(px_reg(20))746.with(px_reg(21))747.with(px_reg(22))748.with(px_reg(23))749.with(px_reg(24))750.with(px_reg(25))751.with(px_reg(26))752.with(px_reg(27))753// F Regs754.with(pf_reg(8))755.with(pf_reg(18))756.with(pf_reg(19))757.with(pf_reg(20))758.with(pf_reg(21))759.with(pf_reg(22))760.with(pf_reg(23))761.with(pf_reg(24))762.with(pf_reg(25))763.with(pf_reg(26))764.with(pf_reg(27));765766fn compute_clobber_size(clobbers: &[Writable<RealReg>]) -> u32 {767let mut clobbered_size = 0;768for reg in clobbers {769match reg.to_reg().class() {770RegClass::Int => {771clobbered_size += 8;772}773RegClass::Float => {774clobbered_size += 8;775}776RegClass::Vector => {777clobbered_size = align_to(clobbered_size, 16);778clobbered_size += 16;779}780}781}782align_to(clobbered_size, 16)783}784785const DEFAULT_CLOBBERS: PRegSet = PRegSet::empty()786.with(px_reg(1))787.with(px_reg(5))788.with(px_reg(6))789.with(px_reg(7))790.with(px_reg(10))791.with(px_reg(11))792.with(px_reg(12))793.with(px_reg(13))794.with(px_reg(14))795.with(px_reg(15))796.with(px_reg(16))797.with(px_reg(17))798.with(px_reg(28))799.with(px_reg(29))800.with(px_reg(30))801.with(px_reg(31))802// F Regs803.with(pf_reg(0))804.with(pf_reg(1))805.with(pf_reg(2))806.with(pf_reg(3))807.with(pf_reg(4))808.with(pf_reg(5))809.with(pf_reg(6))810.with(pf_reg(7))811.with(pf_reg(9))812.with(pf_reg(10))813.with(pf_reg(11))814.with(pf_reg(12))815.with(pf_reg(13))816.with(pf_reg(14))817.with(pf_reg(15))818.with(pf_reg(16))819.with(pf_reg(17))820.with(pf_reg(28))821.with(pf_reg(29))822.with(pf_reg(30))823.with(pf_reg(31))824// V Regs - All vector regs get clobbered825.with(pv_reg(0))826.with(pv_reg(1))827.with(pv_reg(2))828.with(pv_reg(3))829.with(pv_reg(4))830.with(pv_reg(5))831.with(pv_reg(6))832.with(pv_reg(7))833.with(pv_reg(8))834.with(pv_reg(9))835.with(pv_reg(10))836.with(pv_reg(11))837.with(pv_reg(12))838.with(pv_reg(13))839.with(pv_reg(14))840.with(pv_reg(15))841.with(pv_reg(16))842.with(pv_reg(17))843.with(pv_reg(18))844.with(pv_reg(19))845.with(pv_reg(20))846.with(pv_reg(21))847.with(pv_reg(22))848.with(pv_reg(23))849.with(pv_reg(24))850.with(pv_reg(25))851.with(pv_reg(26))852.with(pv_reg(27))853.with(pv_reg(28))854.with(pv_reg(29))855.with(pv_reg(30))856.with(pv_reg(31));857858const ALL_CLOBBERS: PRegSet = PRegSet::empty()859// Specials: x0 is the zero register; x1 is the return address; x2 is SP.860.with(px_reg(3))861.with(px_reg(4))862.with(px_reg(5))863.with(px_reg(6))864.with(px_reg(7))865.with(px_reg(8))866.with(px_reg(9))867.with(px_reg(10))868.with(px_reg(11))869.with(px_reg(12))870.with(px_reg(13))871.with(px_reg(14))872.with(px_reg(15))873.with(px_reg(16))874.with(px_reg(17))875.with(px_reg(18))876.with(px_reg(19))877.with(px_reg(20))878.with(px_reg(21))879.with(px_reg(22))880.with(px_reg(23))881.with(px_reg(24))882.with(px_reg(25))883.with(px_reg(26))884.with(px_reg(27))885.with(px_reg(28))886.with(px_reg(29))887.with(px_reg(30))888.with(px_reg(31))889// F Regs890.with(pf_reg(0))891.with(pf_reg(1))892.with(pf_reg(2))893.with(pf_reg(3))894.with(pf_reg(4))895.with(pf_reg(5))896.with(pf_reg(6))897.with(pf_reg(7))898.with(pf_reg(8))899.with(pf_reg(9))900.with(pf_reg(10))901.with(pf_reg(11))902.with(pf_reg(12))903.with(pf_reg(13))904.with(pf_reg(14))905.with(pf_reg(15))906.with(pf_reg(16))907.with(pf_reg(17))908.with(pf_reg(18))909.with(pf_reg(19))910.with(pf_reg(20))911.with(pf_reg(21))912.with(pf_reg(22))913.with(pf_reg(23))914.with(pf_reg(24))915.with(pf_reg(25))916.with(pf_reg(26))917.with(pf_reg(27))918.with(pf_reg(28))919.with(pf_reg(29))920.with(pf_reg(30))921.with(pf_reg(31))922// V Regs923.with(pv_reg(0))924.with(pv_reg(1))925.with(pv_reg(2))926.with(pv_reg(3))927.with(pv_reg(4))928.with(pv_reg(5))929.with(pv_reg(6))930.with(pv_reg(7))931.with(pv_reg(8))932.with(pv_reg(9))933.with(pv_reg(10))934.with(pv_reg(11))935.with(pv_reg(12))936.with(pv_reg(13))937.with(pv_reg(14))938.with(pv_reg(15))939.with(pv_reg(16))940.with(pv_reg(17))941.with(pv_reg(18))942.with(pv_reg(19))943.with(pv_reg(20))944.with(pv_reg(21))945.with(pv_reg(22))946.with(pv_reg(23))947.with(pv_reg(24))948.with(pv_reg(25))949.with(pv_reg(26))950.with(pv_reg(27))951.with(pv_reg(28))952.with(pv_reg(29))953.with(pv_reg(30))954.with(pv_reg(31));955956const NO_CLOBBERS: PRegSet = PRegSet::empty();957958fn create_reg_environment() -> MachineEnv {959// Some C Extension instructions can only use a subset of the registers.960// x8 - x15, f8 - f15, v8 - v15 so we should prefer to use those since961// they allow us to emit C instructions more often.962//963// In general the order of preference is:964// 1. Compressible Caller Saved registers.965// 2. Non-Compressible Caller Saved registers.966// 3. Compressible Callee Saved registers.967// 4. Non-Compressible Callee Saved registers.968969let preferred_regs_by_class: [Vec<PReg>; 3] = {970let x_registers: Vec<PReg> = (10..=15).map(px_reg).collect();971let f_registers: Vec<PReg> = (10..=15).map(pf_reg).collect();972let v_registers: Vec<PReg> = (8..=15).map(pv_reg).collect();973974[x_registers, f_registers, v_registers]975};976977let non_preferred_regs_by_class: [Vec<PReg>; 3] = {978// x0 - x4 are special registers, so we don't want to use them.979// Omit x30 and x31 since they are the spilltmp registers.980981// Start with the Non-Compressible Caller Saved registers.982let x_registers: Vec<PReg> = (5..=7)983.chain(16..=17)984.chain(28..=29)985// The first Callee Saved register is x9 since its Compressible986// Omit x8 since it's the frame pointer.987.chain(9..=9)988// The rest of the Callee Saved registers are Non-Compressible989.chain(18..=27)990.map(px_reg)991.collect();992993// Prefer Caller Saved registers.994let f_registers: Vec<PReg> = (0..=7)995.chain(16..=17)996.chain(28..=31)997// Once those are exhausted, we should prefer f8 and f9 since they are998// callee saved, but compressible.999.chain(8..=9)1000.chain(18..=27)1001.map(pf_reg)1002.collect();10031004let v_registers = (0..=7).chain(16..=31).map(pv_reg).collect();10051006[x_registers, f_registers, v_registers]1007};10081009MachineEnv {1010preferred_regs_by_class,1011non_preferred_regs_by_class,1012fixed_stack_slots: vec![],1013scratch_by_class: [None, None, None],1014}1015}10161017impl Riscv64MachineDeps {1018fn gen_probestack_unroll(1019insts: &mut SmallInstVec<Inst>,1020tmp: Writable<Reg>,1021guard_size: u32,1022probe_count: u32,1023) {1024// When manually unrolling adjust the stack pointer and then write a zero1025// to the stack at that offset.1026//1027// We do this because valgrind expects us to never write beyond the stack1028// pointer and associated redzone.1029// See: https://github.com/bytecodealliance/wasmtime/issues/745410301031// Store the adjust amount in a register upfront, so we don't have to1032// reload it for each probe. It's worth loading this as a negative and1033// using an `add` instruction since we have compressed versions of `add`1034// but not the `sub` instruction.1035insts.extend(Inst::load_constant_u64(tmp, (-(guard_size as i64)) as u64));10361037for _ in 0..probe_count {1038insts.push(Inst::AluRRR {1039alu_op: AluOPRRR::Add,1040rd: writable_stack_reg(),1041rs1: stack_reg(),1042rs2: tmp.to_reg(),1043});10441045insts.push(Inst::gen_store(1046AMode::SPOffset(0),1047zero_reg(),1048I32,1049MemFlags::trusted(),1050));1051}10521053// Restore the stack pointer to its original value1054insts.extend(Self::gen_sp_reg_adjust((guard_size * probe_count) as i32));1055}1056}105710581059