Path: blob/main/crates/cranelift/src/compiler.rs
1692 views
use crate::TRAP_INTERNAL_ASSERT;1use crate::debug::DwarfSectionRelocTarget;2use crate::func_environ::FuncEnvironment;3use crate::translate::FuncTranslator;4use crate::{BuiltinFunctionSignatures, builder::LinkOptions, wasm_call_signature};5use crate::{CompiledFunction, ModuleTextBuilder, array_call_signature};6use anyhow::{Context as _, Result};7use cranelift_codegen::binemit::CodeOffset;8use cranelift_codegen::inline::InlineCommand;9use cranelift_codegen::ir::condcodes::IntCC;10use cranelift_codegen::ir::{self, InstBuilder, MemFlags, UserExternalName, UserFuncName, Value};11use cranelift_codegen::isa::{12OwnedTargetIsa, TargetIsa,13unwind::{UnwindInfo, UnwindInfoKind},14};15use cranelift_codegen::print_errors::pretty_error;16use cranelift_codegen::{CompiledCode, Context, FinalizedMachCallSite};17use cranelift_entity::PrimaryMap;18use cranelift_frontend::FunctionBuilder;19use object::write::{Object, StandardSegment, SymbolId};20use object::{RelocationEncoding, RelocationFlags, RelocationKind, SectionKind};21use std::any::Any;22use std::borrow::Cow;23use std::cmp;24use std::collections::HashMap;25use std::mem;26use std::ops::Range;27use std::path;28use std::sync::{Arc, Mutex};29use wasmparser::{FuncValidatorAllocations, FunctionBody};30use wasmtime_environ::obj::ELF_WASMTIME_EXCEPTIONS;31use wasmtime_environ::{32AddressMapSection, BuiltinFunctionIndex, CacheStore, CompileError, CompiledFunctionBody,33DefinedFuncIndex, FlagValue, FuncKey, FunctionBodyData, FunctionLoc, HostCall,34InliningCompiler, ModuleTranslation, ModuleTypesBuilder, PtrSize, StackMapSection,35StaticModuleIndex, TrapEncodingBuilder, TrapSentinel, TripleExt, Tunables, VMOffsets,36WasmFuncType, WasmValType,37};38use wasmtime_unwinder::ExceptionTableBuilder;3940#[cfg(feature = "component-model")]41mod component;4243struct IncrementalCacheContext {44#[cfg(feature = "incremental-cache")]45cache_store: Arc<dyn CacheStore>,46num_hits: usize,47num_cached: usize,48}4950/// ABI signature of functions that are generated here.51#[derive(Debug, Copy, Clone)]52#[cfg_attr(53not(feature = "component-model"),54expect(dead_code, reason = "only used with component model compiler")55)]56enum Abi {57/// The "wasm" ABI, or suitable to be a `wasm_call` field of a `VMFuncRef`.58Wasm,59/// The "array" ABI, or suitable to be an `array_call` field.60Array,61}6263struct CompilerContext {64func_translator: FuncTranslator,65codegen_context: Context,66incremental_cache_ctx: Option<IncrementalCacheContext>,67validator_allocations: FuncValidatorAllocations,68abi: Option<Abi>,69}7071impl Default for CompilerContext {72fn default() -> Self {73Self {74func_translator: FuncTranslator::new(),75codegen_context: Context::new(),76incremental_cache_ctx: None,77validator_allocations: Default::default(),78abi: None,79}80}81}8283/// A compiler that compiles a WebAssembly module with Compiler, translating84/// the Wasm to Compiler IR, optimizing it and then translating to assembly.85pub struct Compiler {86tunables: Tunables,87contexts: Mutex<Vec<CompilerContext>>,88isa: OwnedTargetIsa,89emit_debug_checks: bool,90linkopts: LinkOptions,91cache_store: Option<Arc<dyn CacheStore>>,92clif_dir: Option<path::PathBuf>,93#[cfg(feature = "wmemcheck")]94pub(crate) wmemcheck: bool,95}9697impl Drop for Compiler {98fn drop(&mut self) {99if self.cache_store.is_none() {100return;101}102103let mut num_hits = 0;104let mut num_cached = 0;105for ctx in self.contexts.lock().unwrap().iter() {106if let Some(ref cache_ctx) = ctx.incremental_cache_ctx {107num_hits += cache_ctx.num_hits;108num_cached += cache_ctx.num_cached;109}110}111112let total = num_hits + num_cached;113if num_hits + num_cached > 0 {114log::trace!(115"Incremental compilation cache stats: {}/{} = {}% (hits/lookup)\ncached: {}",116num_hits,117total,118(num_hits as f32) / (total as f32) * 100.0,119num_cached120);121}122}123}124125impl Compiler {126pub fn new(127tunables: Tunables,128isa: OwnedTargetIsa,129cache_store: Option<Arc<dyn CacheStore>>,130emit_debug_checks: bool,131linkopts: LinkOptions,132clif_dir: Option<path::PathBuf>,133wmemcheck: bool,134) -> Compiler {135let _ = wmemcheck;136Compiler {137contexts: Default::default(),138tunables,139isa,140emit_debug_checks,141linkopts,142cache_store,143clif_dir,144#[cfg(feature = "wmemcheck")]145wmemcheck,146}147}148149/// Perform an indirect call from Cranelift-generated code to native code in150/// Wasmtime itself.151///152/// For native platforms this is a simple `call_indirect` instruction but153/// for the Pulley backend this is special as it's transitioning from154/// Cranelift-generated bytecode to native code on the host. That requires a155/// special opcode in the interpreter and is modeled slightly differently in156/// Cranelift IR.157fn call_indirect_host(158&self,159builder: &mut FunctionBuilder<'_>,160hostcall: impl Into<HostCall>,161sig: ir::SigRef,162addr: Value,163args: &[Value],164) -> ir::Inst {165let signature = &builder.func.dfg.signatures[sig];166167// When calling the host we should always be using the platform's168// default calling convention since it'll be calling Rust code in169// Wasmtime itself.170assert_eq!(signature.call_conv, self.isa.default_call_conv());171172// If this target is actually pulley then the goal is to emit the custom173// `call_indirect_host` pulley opcode. That's encoded in Cranelift as a174// `call` instruction where the name is `colocated: false`. This will175// force a pulley-specific relocation to get emitted in addition to176// using the `call_indirect_host` instruction.177if self.isa.triple().is_pulley() {178let mut new_signature = signature.clone();179new_signature180.params181.insert(0, ir::AbiParam::new(self.isa.pointer_type()));182let new_sig = builder.func.import_signature(new_signature);183let key = FuncKey::PulleyHostCall(hostcall.into());184let (namespace, index) = key.into_raw_parts();185let name = ir::ExternalName::User(186builder187.func188.declare_imported_user_function(ir::UserExternalName { namespace, index }),189);190let func = builder.func.import_function(ir::ExtFuncData {191name,192signature: new_sig,193// This is the signal that a special `call_indirect_host`194// opcode is used to jump from pulley to the host.195colocated: false,196});197let mut raw_args = vec![addr];198raw_args.extend_from_slice(args);199return builder.ins().call(func, &raw_args);200}201202builder.ins().call_indirect(sig, addr, args)203}204}205206fn box_dyn_any_compiled_function(f: CompiledFunction) -> Box<dyn Any + Send + Sync> {207let b = box_dyn_any(f);208debug_assert!(b.is::<CompiledFunction>());209b210}211212fn box_dyn_any_compiler_context(ctx: Option<CompilerContext>) -> Box<dyn Any + Send + Sync> {213let b = box_dyn_any(ctx);214debug_assert!(b.is::<Option<CompilerContext>>());215b216}217218fn box_dyn_any(x: impl Any + Send + Sync) -> Box<dyn Any + Send + Sync> {219log::trace!(220"making Box<dyn Any + Send + Sync> of {}",221std::any::type_name_of_val(&x)222);223let b = Box::new(x);224let r: &(dyn Any + Sync + Send) = &*b;225log::trace!(" --> {r:#p}");226b227}228229impl wasmtime_environ::Compiler for Compiler {230fn inlining_compiler(&self) -> Option<&dyn wasmtime_environ::InliningCompiler> {231Some(self)232}233234fn compile_function(235&self,236translation: &ModuleTranslation<'_>,237key: FuncKey,238input: FunctionBodyData<'_>,239types: &ModuleTypesBuilder,240symbol: &str,241) -> Result<CompiledFunctionBody, CompileError> {242log::trace!("compiling Wasm function: {key:?} = {symbol:?}");243244let isa = &*self.isa;245let module = &translation.module;246247let (module_index, def_func_index) = key.unwrap_defined_wasm_function();248debug_assert_eq!(translation.module_index, module_index);249250let func_index = module.func_index(def_func_index);251let sig = translation.module.functions[func_index]252.signature253.unwrap_module_type_index();254let wasm_func_ty = types[sig].unwrap_func();255256let mut compiler = self.function_compiler();257258let context = &mut compiler.cx.codegen_context;259context.func.signature = wasm_call_signature(isa, wasm_func_ty, &self.tunables);260let (namespace, index) = key.into_raw_parts();261context.func.name = UserFuncName::User(UserExternalName { namespace, index });262263if self.tunables.generate_native_debuginfo {264context.func.collect_debug_info();265}266267let mut func_env = FuncEnvironment::new(self, translation, types, wasm_func_ty);268269// The `stack_limit` global value below is the implementation of stack270// overflow checks in Wasmtime.271//272// The Wasm spec defines that stack overflows will raise a trap, and273// there's also an added constraint where as an embedder you frequently274// are running host-provided code called from wasm. WebAssembly and275// native code currently share the same call stack, so Wasmtime needs to276// make sure that host-provided code will have enough call-stack277// available to it.278//279// The way that stack overflow is handled here is by adding a prologue280// check to all functions for how much native stack is remaining. The281// `VMContext` pointer is the first argument to all functions, and the282// first field of this structure is `*const VMStoreContext` and the283// third field of that is the stack limit. Note that the stack limit in284// this case means "if the stack pointer goes below this, trap". Each285// function which consumes stack space or isn't a leaf function starts286// off by loading the stack limit, checking it against the stack287// pointer, and optionally traps.288//289// This manual check allows the embedder to give wasm a relatively290// precise amount of stack allocation. Using this scheme we reserve a291// chunk of stack for wasm code relative from where wasm code was292// called. This ensures that native code called by wasm should have293// native stack space to run, and the numbers of stack spaces here294// should all be configurable for various embeddings.295//296// Note that this check is independent of each thread's stack guard page297// here. If the stack guard page is reached that's still considered an298// abort for the whole program since the runtime limits configured by299// the embedder should cause wasm to trap before it reaches that300// (ensuring the host has enough space as well for its functionality).301if !isa.triple().is_pulley() {302let vmctx = context303.func304.create_global_value(ir::GlobalValueData::VMContext);305let interrupts_ptr = context.func.create_global_value(ir::GlobalValueData::Load {306base: vmctx,307offset: i32::from(func_env.offsets.ptr.vmctx_store_context()).into(),308global_type: isa.pointer_type(),309flags: MemFlags::trusted().with_readonly(),310});311let stack_limit = context.func.create_global_value(ir::GlobalValueData::Load {312base: interrupts_ptr,313offset: i32::from(func_env.offsets.ptr.vmstore_context_stack_limit()).into(),314global_type: isa.pointer_type(),315flags: MemFlags::trusted(),316});317if self.tunables.signals_based_traps {318context.func.stack_limit = Some(stack_limit);319} else {320func_env.stack_limit_at_function_entry = Some(stack_limit);321}322}323let FunctionBodyData { validator, body } = input;324let mut validator =325validator.into_validator(mem::take(&mut compiler.cx.validator_allocations));326compiler.cx.func_translator.translate_body(327&mut validator,328body.clone(),329&mut context.func,330&mut func_env,331)?;332333if self.tunables.inlining {334compiler335.cx336.codegen_context337.legalize(isa)338.map_err(|e| CompileError::Codegen(e.to_string()))?;339}340341let timing = cranelift_codegen::timing::take_current();342log::debug!("`{symbol}` translated to CLIF in {:?}", timing.total());343log::trace!("`{symbol}` timing info\n{timing}");344345Ok(CompiledFunctionBody {346code: box_dyn_any_compiler_context(Some(compiler.cx)),347needs_gc_heap: func_env.needs_gc_heap(),348})349}350351fn compile_array_to_wasm_trampoline(352&self,353translation: &ModuleTranslation<'_>,354types: &ModuleTypesBuilder,355key: FuncKey,356symbol: &str,357) -> Result<CompiledFunctionBody, CompileError> {358log::trace!("compiling array-to-wasm trampoline: {key:?} = {symbol:?}");359360let (module_index, def_func_index) = key.unwrap_array_to_wasm_trampoline();361debug_assert_eq!(translation.module_index, module_index);362363let func_index = translation.module.func_index(def_func_index);364let sig = translation.module.functions[func_index]365.signature366.unwrap_module_type_index();367let wasm_func_ty = types[sig].unwrap_func();368369let isa = &*self.isa;370let pointer_type = isa.pointer_type();371let wasm_call_sig = wasm_call_signature(isa, wasm_func_ty, &self.tunables);372let array_call_sig = array_call_signature(isa);373374let mut compiler = self.function_compiler();375let func = ir::Function::with_name_signature(Default::default(), array_call_sig);376let (mut builder, block0) = compiler.builder(func);377378let (vmctx, caller_vmctx, values_vec_ptr, values_vec_len) = {379let params = builder.func.dfg.block_params(block0);380(params[0], params[1], params[2], params[3])381};382383// First load the actual arguments out of the array.384let mut args = self.load_values_from_array(385wasm_func_ty.params(),386&mut builder,387values_vec_ptr,388values_vec_len,389);390args.insert(0, caller_vmctx);391args.insert(0, vmctx);392393// Just before we enter Wasm, save our stack pointer.394//395// Assert that we were really given a core Wasm vmctx, since that's396// what we are assuming with our offsets below.397self.debug_assert_vmctx_kind(&mut builder, vmctx, wasmtime_environ::VMCONTEXT_MAGIC);398let offsets = VMOffsets::new(isa.pointer_bytes(), &translation.module);399let vm_store_context_offset = offsets.ptr.vmctx_store_context();400save_last_wasm_entry_fp(401&mut builder,402pointer_type,403&offsets.ptr,404vm_store_context_offset.into(),405vmctx,406);407408// Then call the Wasm function with those arguments.409let callee_key = FuncKey::DefinedWasmFunction(module_index, def_func_index);410let call = declare_and_call(&mut builder, wasm_call_sig, callee_key, &args);411let results = builder.func.dfg.inst_results(call).to_vec();412413// Then store the results back into the array.414self.store_values_to_array(415&mut builder,416wasm_func_ty.returns(),417&results,418values_vec_ptr,419values_vec_len,420);421422// At this time wasm functions always signal traps with longjmp or some423// similar sort of routine, so if we got this far that means that the424// function did not trap, so return a "true" value here to indicate that425// to satisfy the ABI of the array-call signature.426let true_return = builder.ins().iconst(ir::types::I8, 1);427builder.ins().return_(&[true_return]);428builder.finalize();429430Ok(CompiledFunctionBody {431code: box_dyn_any_compiler_context(Some(compiler.cx)),432needs_gc_heap: false,433})434}435436fn compile_wasm_to_array_trampoline(437&self,438wasm_func_ty: &WasmFuncType,439key: FuncKey,440symbol: &str,441) -> Result<CompiledFunctionBody, CompileError> {442log::trace!("compiling wasm-to-array trampoline: {key:?} = {symbol:?}");443444let isa = &*self.isa;445let pointer_type = isa.pointer_type();446let wasm_call_sig = wasm_call_signature(isa, wasm_func_ty, &self.tunables);447let array_call_sig = array_call_signature(isa);448449let mut compiler = self.function_compiler();450let func = ir::Function::with_name_signature(Default::default(), wasm_call_sig);451let (mut builder, block0) = compiler.builder(func);452453let args = builder.func.dfg.block_params(block0).to_vec();454let callee_vmctx = args[0];455let caller_vmctx = args[1];456457// We are exiting Wasm, so save our PC and FP.458//459// Assert that the caller vmctx really is a core Wasm vmctx, since460// that's what we are assuming with our offsets below.461self.debug_assert_vmctx_kind(462&mut builder,463caller_vmctx,464wasmtime_environ::VMCONTEXT_MAGIC,465);466let ptr = isa.pointer_bytes();467let vm_store_context = builder.ins().load(468pointer_type,469MemFlags::trusted(),470caller_vmctx,471i32::from(ptr.vmcontext_store_context()),472);473save_last_wasm_exit_fp_and_pc(&mut builder, pointer_type, &ptr, vm_store_context);474475// Spill all wasm arguments to the stack in `ValRaw` slots.476let (args_base, args_len) =477self.allocate_stack_array_and_spill_args(wasm_func_ty, &mut builder, &args[2..]);478let args_len = builder.ins().iconst(pointer_type, i64::from(args_len));479480// Load the actual callee out of the481// `VMArrayCallHostFuncContext::host_func`.482let ptr_size = isa.pointer_bytes();483let callee = builder.ins().load(484pointer_type,485MemFlags::trusted(),486callee_vmctx,487ptr_size.vmarray_call_host_func_context_func_ref() + ptr_size.vm_func_ref_array_call(),488);489490// Do an indirect call to the callee.491let callee_signature = builder.func.import_signature(array_call_sig);492let call = self.call_indirect_host(493&mut builder,494HostCall::ArrayCall,495callee_signature,496callee,497&[callee_vmctx, caller_vmctx, args_base, args_len],498);499let succeeded = builder.func.dfg.inst_results(call)[0];500self.raise_if_host_trapped(&mut builder, caller_vmctx, succeeded);501let results =502self.load_values_from_array(wasm_func_ty.returns(), &mut builder, args_base, args_len);503builder.ins().return_(&results);504builder.finalize();505506Ok(CompiledFunctionBody {507code: box_dyn_any_compiler_context(Some(compiler.cx)),508needs_gc_heap: false,509})510}511512fn append_code(513&self,514obj: &mut Object<'static>,515funcs: &[(String, Box<dyn Any + Send + Sync>)],516resolve_reloc: &dyn Fn(usize, FuncKey) -> usize,517) -> Result<Vec<(SymbolId, FunctionLoc)>> {518log::trace!(519"appending functions to object file: {:#?}",520funcs.iter().map(|(sym, _)| sym).collect::<Vec<_>>()521);522523let mut builder =524ModuleTextBuilder::new(obj, self, self.isa.text_section_builder(funcs.len()));525if self.linkopts.force_jump_veneers {526builder.force_veneers();527}528let mut addrs = AddressMapSection::default();529let mut traps = TrapEncodingBuilder::default();530let mut stack_maps = StackMapSection::default();531let mut exception_tables = ExceptionTableBuilder::default();532533let mut ret = Vec::with_capacity(funcs.len());534for (i, (sym, func)) in funcs.iter().enumerate() {535debug_assert!(!func.is::<Option<CompilerContext>>());536debug_assert!(func.is::<CompiledFunction>());537let func = func.downcast_ref::<CompiledFunction>().unwrap();538539let (sym_id, range) = builder.append_func(&sym, func, |idx| resolve_reloc(i, idx));540log::trace!("symbol id {sym_id:?} = {sym:?}");541542if self.tunables.generate_address_map {543let addr = func.address_map();544addrs.push(range.clone(), &addr.instructions);545}546547clif_to_env_stack_maps(548&mut stack_maps,549range.clone(),550func.buffer.user_stack_maps(),551);552553traps.push(range.clone(), &func.traps().collect::<Vec<_>>());554clif_to_env_exception_tables(555&mut exception_tables,556range.clone(),557func.buffer.call_sites(),558)?;559builder.append_padding(self.linkopts.padding_between_functions);560561let info = FunctionLoc {562start: u32::try_from(range.start).unwrap(),563length: u32::try_from(range.end - range.start).unwrap(),564};565ret.push((sym_id, info));566}567568builder.finish();569570if self.tunables.generate_address_map {571addrs.append_to(obj);572}573stack_maps.append_to(obj);574traps.append_to(obj);575576let exception_section = obj.add_section(577obj.segment_name(StandardSegment::Data).to_vec(),578ELF_WASMTIME_EXCEPTIONS.as_bytes().to_vec(),579SectionKind::ReadOnlyData,580);581exception_tables.serialize(|bytes| {582obj.append_section_data(exception_section, bytes, 1);583});584585Ok(ret)586}587588fn triple(&self) -> &target_lexicon::Triple {589self.isa.triple()590}591592fn flags(&self) -> Vec<(&'static str, FlagValue<'static>)> {593crate::clif_flags_to_wasmtime(self.isa.flags().iter())594}595596fn isa_flags(&self) -> Vec<(&'static str, FlagValue<'static>)> {597crate::clif_flags_to_wasmtime(self.isa.isa_flags())598}599600fn is_branch_protection_enabled(&self) -> bool {601self.isa.is_branch_protection_enabled()602}603604#[cfg(feature = "component-model")]605fn component_compiler(&self) -> &dyn wasmtime_environ::component::ComponentCompiler {606self607}608609fn append_dwarf<'a>(610&self,611obj: &mut Object<'_>,612translations: &'a PrimaryMap<StaticModuleIndex, ModuleTranslation<'a>>,613get_func: &'a dyn Fn(614StaticModuleIndex,615DefinedFuncIndex,616) -> (SymbolId, &'a (dyn Any + Send + Sync)),617dwarf_package_bytes: Option<&'a [u8]>,618tunables: &'a Tunables,619) -> Result<()> {620log::trace!("appending DWARF debug info");621622let get_func = move |m, f| {623let (sym, any) = get_func(m, f);624log::trace!("get_func({m:?}, {f:?}) -> ({sym:?}, {any:#p})");625debug_assert!(!any.is::<Option<CompilerContext>>());626debug_assert!(any.is::<CompiledFunction>());627(628sym,629any.downcast_ref::<CompiledFunction>().unwrap().metadata(),630)631};632633let mut compilation = crate::debug::Compilation::new(634&*self.isa,635translations,636&get_func,637dwarf_package_bytes,638tunables,639);640let dwarf_sections = crate::debug::emit_dwarf(&*self.isa, &mut compilation)641.with_context(|| "failed to emit DWARF debug information")?;642643let (debug_bodies, debug_relocs): (Vec<_>, Vec<_>) = dwarf_sections644.iter()645.map(|s| ((s.name, &s.body), (s.name, &s.relocs)))646.unzip();647let mut dwarf_sections_ids = HashMap::new();648for (name, body) in debug_bodies {649let segment = obj.segment_name(StandardSegment::Debug).to_vec();650let section_id = obj.add_section(segment, name.as_bytes().to_vec(), SectionKind::Debug);651dwarf_sections_ids.insert(name, section_id);652obj.append_section_data(section_id, &body, 1);653}654655// Write all debug data relocations.656for (name, relocs) in debug_relocs {657let section_id = *dwarf_sections_ids.get(name).unwrap();658for reloc in relocs {659let target_symbol = match reloc.target {660DwarfSectionRelocTarget::Func(id) => compilation.symbol_id(id),661DwarfSectionRelocTarget::Section(name) => {662obj.section_symbol(dwarf_sections_ids[name])663}664};665obj.add_relocation(666section_id,667object::write::Relocation {668offset: u64::from(reloc.offset),669symbol: target_symbol,670addend: i64::from(reloc.addend),671flags: RelocationFlags::Generic {672size: reloc.size << 3,673kind: RelocationKind::Absolute,674encoding: RelocationEncoding::Generic,675},676},677)?;678}679}680681Ok(())682}683684fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {685self.isa.create_systemv_cie()686}687688fn compile_wasm_to_builtin(689&self,690key: FuncKey,691symbol: &str,692) -> Result<CompiledFunctionBody, CompileError> {693log::trace!("compiling wasm-to-builtin trampoline: {key:?} = {symbol:?}");694695let isa = &*self.isa;696let ptr_size = isa.pointer_bytes();697let pointer_type = isa.pointer_type();698let sigs = BuiltinFunctionSignatures::new(self);699let builtin_func_index = key.unwrap_wasm_to_builtin_trampoline();700let wasm_sig = sigs.wasm_signature(builtin_func_index);701let host_sig = sigs.host_signature(builtin_func_index);702703let mut compiler = self.function_compiler();704let func = ir::Function::with_name_signature(Default::default(), wasm_sig.clone());705let (mut builder, block0) = compiler.builder(func);706let vmctx = builder.block_params(block0)[0];707708// Debug-assert that this is the right kind of vmctx, and then709// additionally perform the "routine of the exit trampoline" of saving710// fp/pc/etc.711self.debug_assert_vmctx_kind(&mut builder, vmctx, wasmtime_environ::VMCONTEXT_MAGIC);712let vm_store_context = builder.ins().load(713pointer_type,714MemFlags::trusted(),715vmctx,716ptr_size.vmcontext_store_context(),717);718save_last_wasm_exit_fp_and_pc(&mut builder, pointer_type, &ptr_size, vm_store_context);719720// Now it's time to delegate to the actual builtin. Forward all our own721// arguments to the libcall itself.722let args = builder.block_params(block0).to_vec();723let call = self.call_builtin(&mut builder, vmctx, &args, builtin_func_index, host_sig);724let results = builder.func.dfg.inst_results(call).to_vec();725726// Libcalls do not explicitly `longjmp` for example but instead return a727// code indicating whether they trapped or not. This means that it's the728// responsibility of the trampoline to check for an trapping return729// value and raise a trap as appropriate. With the `results` above check730// what `index` is and for each libcall that has a trapping return value731// process it here.732match builtin_func_index.trap_sentinel() {733Some(TrapSentinel::Falsy) => {734self.raise_if_host_trapped(&mut builder, vmctx, results[0]);735}736Some(TrapSentinel::NegativeTwo) => {737let ty = builder.func.dfg.value_type(results[0]);738let trapped = builder.ins().iconst(ty, -2);739let succeeded = builder.ins().icmp(IntCC::NotEqual, results[0], trapped);740self.raise_if_host_trapped(&mut builder, vmctx, succeeded);741}742Some(TrapSentinel::Negative) => {743let ty = builder.func.dfg.value_type(results[0]);744let zero = builder.ins().iconst(ty, 0);745let succeeded =746builder747.ins()748.icmp(IntCC::SignedGreaterThanOrEqual, results[0], zero);749self.raise_if_host_trapped(&mut builder, vmctx, succeeded);750}751Some(TrapSentinel::NegativeOne) => {752let ty = builder.func.dfg.value_type(results[0]);753let minus_one = builder.ins().iconst(ty, -1);754let succeeded = builder.ins().icmp(IntCC::NotEqual, results[0], minus_one);755self.raise_if_host_trapped(&mut builder, vmctx, succeeded);756}757None => {}758}759760// And finally, return all the results of this libcall.761builder.ins().return_(&results);762builder.finalize();763764Ok(CompiledFunctionBody {765code: box_dyn_any_compiler_context(Some(compiler.cx)),766needs_gc_heap: false,767})768}769770fn compiled_function_relocation_targets<'a>(771&'a self,772func: &'a dyn Any,773) -> Box<dyn Iterator<Item = FuncKey> + 'a> {774debug_assert!(!func.is::<Option<CompilerContext>>());775debug_assert!(func.is::<CompiledFunction>());776let func = func.downcast_ref::<CompiledFunction>().unwrap();777Box::new(func.relocations().map(|r| r.reloc_target))778}779}780781impl InliningCompiler for Compiler {782fn calls(783&self,784func_body: &CompiledFunctionBody,785calls: &mut wasmtime_environ::prelude::IndexSet<FuncKey>,786) -> Result<()> {787debug_assert!(!func_body.code.is::<CompiledFunction>());788debug_assert!(func_body.code.is::<Option<CompilerContext>>());789let cx = func_body790.code791.downcast_ref::<Option<CompilerContext>>()792.unwrap()793.as_ref()794.unwrap();795let func = &cx.codegen_context.func;796calls.extend(797func.params798.user_named_funcs()799.values()800.map(|name| FuncKey::from_raw_parts(name.namespace, name.index))801.filter(|key| matches!(key, FuncKey::DefinedWasmFunction(_, _))),802);803Ok(())804}805806fn size(&self, func_body: &CompiledFunctionBody) -> u32 {807debug_assert!(!func_body.code.is::<CompiledFunction>());808debug_assert!(func_body.code.is::<Option<CompilerContext>>());809let cx = func_body810.code811.downcast_ref::<Option<CompilerContext>>()812.unwrap()813.as_ref()814.unwrap();815let func = &cx.codegen_context.func;816let size = func.dfg.values().len();817u32::try_from(size).unwrap()818}819820fn inline<'a>(821&self,822func_body: &mut CompiledFunctionBody,823get_callee: &'a mut dyn FnMut(FuncKey) -> Option<&'a CompiledFunctionBody>,824) -> Result<()> {825debug_assert!(!func_body.code.is::<CompiledFunction>());826debug_assert!(func_body.code.is::<Option<CompilerContext>>());827let code = func_body828.code829.downcast_mut::<Option<CompilerContext>>()830.unwrap();831let cx = code.as_mut().unwrap();832833cx.codegen_context.inline(Inliner(get_callee))?;834return Ok(());835836struct Inliner<'a>(&'a mut dyn FnMut(FuncKey) -> Option<&'a CompiledFunctionBody>);837838impl cranelift_codegen::inline::Inline for Inliner<'_> {839fn inline(840&mut self,841caller: &ir::Function,842_call_inst: ir::Inst,843_call_opcode: ir::Opcode,844callee: ir::FuncRef,845_call_args: &[ir::Value],846) -> InlineCommand<'_> {847let callee = &caller.dfg.ext_funcs[callee].name;848let callee = match callee {849ir::ExternalName::User(callee) => *callee,850ir::ExternalName::TestCase(_)851| ir::ExternalName::LibCall(_)852| ir::ExternalName::KnownSymbol(_) => return InlineCommand::KeepCall,853};854let callee = &caller.params.user_named_funcs()[callee];855let callee = FuncKey::from_raw_parts(callee.namespace, callee.index);856match callee {857FuncKey::DefinedWasmFunction(_, _) => match (self.0)(callee) {858None => InlineCommand::KeepCall,859Some(func_body) => {860debug_assert!(!func_body.code.is::<CompiledFunction>());861debug_assert!(func_body.code.is::<Option<CompilerContext>>());862let cx = func_body863.code864.downcast_ref::<Option<CompilerContext>>()865.unwrap();866InlineCommand::Inline {867callee: Cow::Borrowed(&cx.as_ref().unwrap().codegen_context.func),868// We've already visited the callee for inlining869// due to our bottom-up approach, no need to870// visit it again.871visit_callee: false,872}873}874},875_ => InlineCommand::KeepCall,876}877}878}879}880881fn finish_compiling(882&self,883func_body: &mut CompiledFunctionBody,884input: Option<wasmparser::FunctionBody<'_>>,885symbol: &str,886) -> Result<()> {887log::trace!("finish compiling {symbol:?}");888debug_assert!(!func_body.code.is::<CompiledFunction>());889debug_assert!(func_body.code.is::<Option<CompilerContext>>());890let cx = func_body891.code892.downcast_mut::<Option<CompilerContext>>()893.unwrap()894.take()895.unwrap();896let compiler = FunctionCompiler { compiler: self, cx };897898let symbol = match compiler.cx.abi {899None => Cow::Borrowed(symbol),900Some(Abi::Wasm) => Cow::Owned(format!("{symbol}_wasm_call")),901Some(Abi::Array) => Cow::Owned(format!("{symbol}_array_call")),902};903904let compiled_func = if let Some(input) = input {905compiler.finish_with_info(Some((&input, &self.tunables)), &symbol)?906} else {907compiler.finish(&symbol)?908};909910let timing = cranelift_codegen::timing::take_current();911log::debug!("`{symbol}` compiled in {:?}", timing.total());912log::trace!("`{symbol}` timing info\n{timing}");913914func_body.code = box_dyn_any_compiled_function(compiled_func);915Ok(())916}917}918919#[cfg(feature = "incremental-cache")]920mod incremental_cache {921use super::*;922923struct CraneliftCacheStore(Arc<dyn CacheStore>);924925impl cranelift_codegen::incremental_cache::CacheKvStore for CraneliftCacheStore {926fn get(&self, key: &[u8]) -> Option<std::borrow::Cow<'_, [u8]>> {927self.0.get(key)928}929fn insert(&mut self, key: &[u8], val: Vec<u8>) {930self.0.insert(key, val);931}932}933934pub(super) fn compile_maybe_cached<'a>(935context: &'a mut Context,936isa: &dyn TargetIsa,937cache_ctx: Option<&mut IncrementalCacheContext>,938) -> Result<CompiledCode, CompileError> {939let cache_ctx = match cache_ctx {940Some(ctx) => ctx,941None => return compile_uncached(context, isa),942};943944let mut cache_store = CraneliftCacheStore(cache_ctx.cache_store.clone());945let (_compiled_code, from_cache) = context946.compile_with_cache(isa, &mut cache_store, &mut Default::default())947.map_err(|error| CompileError::Codegen(pretty_error(&error.func, error.inner)))?;948949if from_cache {950cache_ctx.num_hits += 1;951} else {952cache_ctx.num_cached += 1;953}954955Ok(context.take_compiled_code().unwrap())956}957}958959#[cfg(feature = "incremental-cache")]960use incremental_cache::*;961962#[cfg(not(feature = "incremental-cache"))]963fn compile_maybe_cached<'a>(964context: &'a mut Context,965isa: &dyn TargetIsa,966_cache_ctx: Option<&mut IncrementalCacheContext>,967) -> Result<CompiledCode, CompileError> {968compile_uncached(context, isa)969}970971fn compile_uncached<'a>(972context: &'a mut Context,973isa: &dyn TargetIsa,974) -> Result<CompiledCode, CompileError> {975context976.compile(isa, &mut Default::default())977.map_err(|error| CompileError::Codegen(pretty_error(&error.func, error.inner)))?;978Ok(context.take_compiled_code().unwrap())979}980981impl Compiler {982/// This function will allocate a stack slot suitable for storing both the983/// arguments and return values of the function, and then the arguments will984/// all be stored in this block.985///986/// `block0` must be the entry block of the function and `ty` must be the987/// Wasm function type of the trampoline.988///989/// The stack slot pointer is returned in addition to the size, in units of990/// `ValRaw`, of the stack slot.991fn allocate_stack_array_and_spill_args(992&self,993ty: &WasmFuncType,994builder: &mut FunctionBuilder,995args: &[ir::Value],996) -> (Value, u32) {997let isa = &*self.isa;998let pointer_type = isa.pointer_type();9991000// Compute the size of the values vector.1001let value_size = mem::size_of::<u128>();1002let values_vec_len = cmp::max(ty.params().len(), ty.returns().len());1003let values_vec_byte_size = u32::try_from(value_size * values_vec_len).unwrap();1004let values_vec_len = u32::try_from(values_vec_len).unwrap();10051006let slot = builder.func.create_sized_stack_slot(ir::StackSlotData::new(1007ir::StackSlotKind::ExplicitSlot,1008values_vec_byte_size,10094,1010));1011let values_vec_ptr = builder.ins().stack_addr(pointer_type, slot, 0);10121013{1014let values_vec_len = builder1015.ins()1016.iconst(ir::types::I32, i64::from(values_vec_len));1017self.store_values_to_array(builder, ty.params(), args, values_vec_ptr, values_vec_len);1018}10191020(values_vec_ptr, values_vec_len)1021}10221023/// Store values to an array in the array calling convention.1024///1025/// Used either to store arguments to the array when calling a function1026/// using the array calling convention, or used to store results to the1027/// array when implementing a function that exposes the array calling1028/// convention.1029fn store_values_to_array(1030&self,1031builder: &mut FunctionBuilder,1032types: &[WasmValType],1033values: &[Value],1034values_vec_ptr: Value,1035values_vec_capacity: Value,1036) {1037debug_assert_eq!(types.len(), values.len());1038self.debug_assert_enough_capacity_for_length(builder, types.len(), values_vec_capacity);10391040// Note that loads and stores are unconditionally done in the1041// little-endian format rather than the host's native-endianness,1042// despite this load/store being unrelated to execution in wasm itself.1043// For more details on this see the `ValRaw` type in1044// `wasmtime::runtime::vm`.1045let flags = ir::MemFlags::new()1046.with_notrap()1047.with_endianness(ir::Endianness::Little);10481049let value_size = mem::size_of::<u128>();1050for (i, val) in values.iter().copied().enumerate() {1051crate::unbarriered_store_type_at_offset(1052&mut builder.cursor(),1053flags,1054values_vec_ptr,1055i32::try_from(i * value_size).unwrap(),1056val,1057);1058}1059}10601061/// Used for loading the values of an array-call host function's value1062/// array.1063///1064/// This can be used to load arguments out of the array if the trampoline we1065/// are building exposes the array calling convention, or it can be used to1066/// load results out of the array if the trampoline we are building calls a1067/// function that uses the array calling convention.1068fn load_values_from_array(1069&self,1070types: &[WasmValType],1071builder: &mut FunctionBuilder,1072values_vec_ptr: Value,1073values_vec_capacity: Value,1074) -> Vec<ir::Value> {1075let isa = &*self.isa;1076let value_size = mem::size_of::<u128>();10771078self.debug_assert_enough_capacity_for_length(builder, types.len(), values_vec_capacity);10791080// Note that this is little-endian like `store_values_to_array` above,1081// see notes there for more information.1082let flags = MemFlags::new()1083.with_notrap()1084.with_endianness(ir::Endianness::Little);10851086let mut results = Vec::new();1087for (i, ty) in types.iter().enumerate() {1088results.push(crate::unbarriered_load_type_at_offset(1089isa,1090&mut builder.cursor(),1091*ty,1092flags,1093values_vec_ptr,1094i32::try_from(i * value_size).unwrap(),1095));1096}1097results1098}10991100fn function_compiler(&self) -> FunctionCompiler<'_> {1101let saved_context = self.contexts.lock().unwrap().pop();1102FunctionCompiler {1103compiler: self,1104cx: saved_context1105.map(|mut ctx| {1106ctx.codegen_context.clear();1107ctx1108})1109.unwrap_or_else(|| CompilerContext {1110#[cfg(feature = "incremental-cache")]1111incremental_cache_ctx: self.cache_store.as_ref().map(|cache_store| {1112IncrementalCacheContext {1113cache_store: cache_store.clone(),1114num_hits: 0,1115num_cached: 0,1116}1117}),1118..Default::default()1119}),1120}1121}11221123/// Invokes the `raise` libcall in `vmctx` if the `succeeded` value1124/// indicates if a trap happened.1125///1126/// This helper is used when the host returns back to WebAssembly. The host1127/// returns a `bool` indicating whether the call succeeded. If the call1128/// failed then Cranelift needs to unwind back to the original invocation1129/// point. The unwind right now is then implemented in Wasmtime with a1130/// `longjmp`, but one day this might be implemented differently with an1131/// unwind inside of Cranelift.1132///1133/// Additionally in the future for pulley this will emit a special trap1134/// opcode for Pulley itself to cease interpretation and exit the1135/// interpreter.1136pub fn raise_if_host_trapped(1137&self,1138builder: &mut FunctionBuilder<'_>,1139vmctx: ir::Value,1140succeeded: ir::Value,1141) {1142let trapped_block = builder.create_block();1143let continuation_block = builder.create_block();1144builder.set_cold_block(trapped_block);1145builder1146.ins()1147.brif(succeeded, continuation_block, &[], trapped_block, &[]);11481149builder.seal_block(trapped_block);1150builder.seal_block(continuation_block);11511152builder.switch_to_block(trapped_block);1153let sigs = BuiltinFunctionSignatures::new(self);1154let sig = sigs.host_signature(BuiltinFunctionIndex::raise());1155self.call_builtin(builder, vmctx, &[vmctx], BuiltinFunctionIndex::raise(), sig);1156builder.ins().trap(TRAP_INTERNAL_ASSERT);11571158builder.switch_to_block(continuation_block);1159}11601161/// Helper to load the core `builtin` from `vmctx` and invoke it with1162/// `args`.1163fn call_builtin(1164&self,1165builder: &mut FunctionBuilder<'_>,1166vmctx: ir::Value,1167args: &[ir::Value],1168builtin: BuiltinFunctionIndex,1169sig: ir::Signature,1170) -> ir::Inst {1171let isa = &*self.isa;1172let ptr_size = isa.pointer_bytes();1173let pointer_type = isa.pointer_type();11741175// Builtins are stored in an array in all `VMContext`s. First load the1176// base pointer of the array and then load the entry of the array that1177// corresponds to this builtin.1178let mem_flags = ir::MemFlags::trusted().with_readonly();1179let array_addr = builder.ins().load(1180pointer_type,1181mem_flags,1182vmctx,1183i32::from(ptr_size.vmcontext_builtin_functions()),1184);1185let body_offset = i32::try_from(builtin.index() * pointer_type.bytes()).unwrap();1186let func_addr = builder1187.ins()1188.load(pointer_type, mem_flags, array_addr, body_offset);11891190let sig = builder.func.import_signature(sig);1191self.call_indirect_host(builder, builtin, sig, func_addr, args)1192}11931194pub fn isa(&self) -> &dyn TargetIsa {1195&*self.isa1196}11971198pub fn tunables(&self) -> &Tunables {1199&self.tunables1200}12011202fn debug_assert_enough_capacity_for_length(1203&self,1204builder: &mut FunctionBuilder,1205length: usize,1206capacity: ir::Value,1207) {1208if !self.emit_debug_checks {1209return;1210}1211let enough_capacity = builder.ins().icmp_imm(1212ir::condcodes::IntCC::UnsignedGreaterThanOrEqual,1213capacity,1214ir::immediates::Imm64::new(length.try_into().unwrap()),1215);1216builder.ins().trapz(enough_capacity, TRAP_INTERNAL_ASSERT);1217}12181219fn debug_assert_vmctx_kind(1220&self,1221builder: &mut FunctionBuilder,1222vmctx: ir::Value,1223expected_vmctx_magic: u32,1224) {1225if !self.emit_debug_checks {1226return;1227}1228let magic = builder.ins().load(1229ir::types::I32,1230MemFlags::trusted().with_endianness(self.isa.endianness()),1231vmctx,12320,1233);1234let is_expected_vmctx = builder.ins().icmp_imm(1235ir::condcodes::IntCC::Equal,1236magic,1237i64::from(expected_vmctx_magic),1238);1239builder.ins().trapz(is_expected_vmctx, TRAP_INTERNAL_ASSERT);1240}1241}12421243struct FunctionCompiler<'a> {1244compiler: &'a Compiler,1245cx: CompilerContext,1246}12471248impl FunctionCompiler<'_> {1249fn builder(&mut self, func: ir::Function) -> (FunctionBuilder<'_>, ir::Block) {1250self.cx.codegen_context.func = func;1251let mut builder = FunctionBuilder::new(1252&mut self.cx.codegen_context.func,1253self.cx.func_translator.context(),1254);12551256let block0 = builder.create_block();1257builder.append_block_params_for_function_params(block0);1258builder.switch_to_block(block0);1259builder.seal_block(block0);1260(builder, block0)1261}12621263fn finish(self, symbol: &str) -> Result<CompiledFunction, CompileError> {1264self.finish_with_info(None, symbol)1265}12661267fn finish_with_info(1268mut self,1269body_and_tunables: Option<(&FunctionBody<'_>, &Tunables)>,1270symbol: &str,1271) -> Result<CompiledFunction, CompileError> {1272let context = &mut self.cx.codegen_context;1273let isa = &*self.compiler.isa;12741275// Run compilation, but don't propagate the error just yet. This'll1276// mutate `context` and the IR contained within (optionally) but it may1277// fail if the backend has a bug in it. Use `context` after this1278// finishes to optionally emit CLIF and then after that's done actually1279// propagate the error if one happened.1280let compilation_result =1281compile_maybe_cached(context, isa, self.cx.incremental_cache_ctx.as_mut());12821283if let Some(path) = &self.compiler.clif_dir {1284use std::io::Write;12851286let mut path = path.join(symbol.replace(":", "-"));1287path.set_extension("clif");12881289let mut output = std::fs::File::create(path).unwrap();1290write!(1291output,1292";; Intermediate Representation of function <{symbol}>:\n",1293)1294.unwrap();1295write!(output, "{}", context.func.display()).unwrap();1296}12971298let compiled_code = compilation_result?;12991300// Give wasm functions, user defined code, a "preferred" alignment1301// instead of the minimum alignment as this can help perf in niche1302// situations.1303let preferred_alignment = if body_and_tunables.is_some() {1304self.compiler.isa.function_alignment().preferred1305} else {130611307};13081309let alignment = compiled_code.buffer.alignment.max(preferred_alignment);1310let mut compiled_function = CompiledFunction::new(1311compiled_code.buffer.clone(),1312context.func.params.user_named_funcs().clone(),1313alignment,1314);13151316if let Some((body, tunables)) = body_and_tunables {1317let data = body.get_binary_reader();1318let offset = data.original_position();1319let len = data.bytes_remaining();1320compiled_function.set_address_map(1321offset.try_into().unwrap(),1322len.try_into().unwrap(),1323tunables.generate_address_map,1324);1325}13261327if isa.flags().unwind_info() {1328let unwind = compiled_code1329.create_unwind_info(isa)1330.map_err(|error| CompileError::Codegen(pretty_error(&context.func, error)))?;13311332if let Some(unwind_info) = unwind {1333compiled_function.set_unwind_info(unwind_info);1334}1335}13361337if body_and_tunables1338.map(|(_, t)| t.generate_native_debuginfo)1339.unwrap_or(false)1340{1341compiled_function.set_value_labels_ranges(compiled_code.value_labels_ranges.clone());13421343// DWARF debugging needs the CFA-based unwind information even on Windows.1344if !matches!(1345compiled_function.metadata().unwind_info,1346Some(UnwindInfo::SystemV(_))1347) {1348let cfa_unwind = compiled_code1349.create_unwind_info_of_kind(isa, UnwindInfoKind::SystemV)1350.map_err(|error| CompileError::Codegen(pretty_error(&context.func, error)))?;13511352if let Some(UnwindInfo::SystemV(cfa_unwind_info)) = cfa_unwind {1353compiled_function.set_cfa_unwind_info(cfa_unwind_info);1354}1355}1356}13571358compiled_function1359.set_sized_stack_slots(std::mem::take(&mut context.func.sized_stack_slots));1360self.compiler.contexts.lock().unwrap().push(self.cx);13611362Ok(compiled_function)1363}1364}13651366/// Convert from Cranelift's representation of a stack map to Wasmtime's1367/// compiler-agnostic representation.1368///1369/// Here `section` is the wasmtime data section being created and `range` is the1370/// range of the function being added. The `clif_stack_maps` entry is the raw1371/// listing of stack maps from Cranelift.1372fn clif_to_env_stack_maps(1373section: &mut StackMapSection,1374range: Range<u64>,1375clif_stack_maps: &[(CodeOffset, u32, ir::UserStackMap)],1376) {1377for (offset, frame_size, stack_map) in clif_stack_maps {1378let mut frame_offsets = Vec::new();1379for (ty, frame_offset) in stack_map.entries() {1380assert_eq!(ty, ir::types::I32);1381frame_offsets.push(frame_offset);1382}1383let code_offset = range.start + u64::from(*offset);1384assert!(code_offset < range.end);1385section.push(code_offset, *frame_size, frame_offsets.into_iter());1386}1387}13881389/// Convert from Cranelift's representation of exception handler1390/// metadata to Wasmtime's compiler-agnostic representation.1391///1392/// Here `builder` is the wasmtime-unwinder exception section being1393/// created and `range` is the range of the function being added. The1394/// `call_sites` iterator is the raw iterator over callsite metadata1395/// (including exception handlers) from Cranelift.1396fn clif_to_env_exception_tables<'a>(1397builder: &mut ExceptionTableBuilder,1398range: Range<u64>,1399call_sites: impl Iterator<Item = FinalizedMachCallSite<'a>>,1400) -> anyhow::Result<()> {1401builder.add_func(CodeOffset::try_from(range.start).unwrap(), call_sites)1402}14031404fn declare_and_call(1405builder: &mut FunctionBuilder,1406signature: ir::Signature,1407callee_key: FuncKey,1408args: &[ir::Value],1409) -> ir::Inst {1410let (namespace, index) = callee_key.into_raw_parts();1411let name = ir::ExternalName::User(1412builder1413.func1414.declare_imported_user_function(ir::UserExternalName { namespace, index }),1415);1416let signature = builder.func.import_signature(signature);1417let callee = builder.func.dfg.ext_funcs.push(ir::ExtFuncData {1418name,1419signature,1420colocated: true,1421});1422builder.ins().call(callee, &args)1423}14241425fn save_last_wasm_entry_fp(1426builder: &mut FunctionBuilder,1427pointer_type: ir::Type,1428ptr_size: &impl PtrSize,1429vm_store_context_offset: u32,1430vmctx: Value,1431) {1432// First we need to get the `VMStoreContext`.1433let vm_store_context = builder.ins().load(1434pointer_type,1435MemFlags::trusted(),1436vmctx,1437i32::try_from(vm_store_context_offset).unwrap(),1438);14391440// Then store our current stack pointer into the appropriate slot.1441let fp = builder.ins().get_frame_pointer(pointer_type);1442builder.ins().store(1443MemFlags::trusted(),1444fp,1445vm_store_context,1446ptr_size.vmstore_context_last_wasm_entry_fp(),1447);1448}14491450fn save_last_wasm_exit_fp_and_pc(1451builder: &mut FunctionBuilder,1452pointer_type: ir::Type,1453ptr: &impl PtrSize,1454limits: Value,1455) {1456// Save the trampoline FP to the limits. Exception unwind needs1457// this so that it can know the SP (bottom of frame) for the very1458// last Wasm frame.1459let trampoline_fp = builder.ins().get_frame_pointer(pointer_type);1460builder.ins().store(1461MemFlags::trusted(),1462trampoline_fp,1463limits,1464ptr.vmstore_context_last_wasm_exit_trampoline_fp(),1465);14661467// Finally save the Wasm return address to the limits.1468let wasm_pc = builder.ins().get_return_address(pointer_type);1469builder.ins().store(1470MemFlags::trusted(),1471wasm_pc,1472limits,1473ptr.vmstore_context_last_wasm_exit_pc(),1474);1475}147614771478