1 use crate::TRAP_INTERNAL_ASSERT;
2 use crate::debug::DwarfSectionRelocTarget;
3 use crate::func_environ::FuncEnvironment;
4 use crate::translate::FuncTranslator;
5 use crate::{BuiltinFunctionSignatures, builder::LinkOptions, wasm_call_signature};
6 use crate::{CompiledFunction, ModuleTextBuilder, array_call_signature};
7 use cranelift_codegen::binemit::CodeOffset;
8 use cranelift_codegen::inline::InlineCommand;
9 use cranelift_codegen::ir::condcodes::IntCC;
10 use cranelift_codegen::ir::{self, InstBuilder, MemFlags, UserExternalName, UserFuncName, Value};
11 use cranelift_codegen::isa::CallConv;
12 use cranelift_codegen::isa::{
13     OwnedTargetIsa, TargetIsa,
14     unwind::{UnwindInfo, UnwindInfoKind},
15 };
16 use cranelift_codegen::print_errors::pretty_error;
17 use cranelift_codegen::{
18     CompiledCode, Context, FinalizedMachCallSite, MachBufferDebugTagList, MachBufferFrameLayout,
19     MachDebugTagPos,
20 };
21 use cranelift_entity::PrimaryMap;
22 use cranelift_frontend::FunctionBuilder;
23 use object::write::{Object, StandardSegment, SymbolId};
24 use object::{RelocationEncoding, RelocationFlags, RelocationKind, SectionKind};
25 use std::any::Any;
26 use std::borrow::Cow;
27 use std::cmp;
28 use std::collections::HashMap;
29 use std::mem;
30 use std::ops::Range;
31 use std::path;
32 use std::sync::{Arc, Mutex};
33 use wasmparser::{FuncValidatorAllocations, FunctionBody};
34 use wasmtime_environ::error::{Context as _, Result};
35 use wasmtime_environ::obj::{ELF_WASMTIME_EXCEPTIONS, ELF_WASMTIME_FRAMES};
36 use wasmtime_environ::{
37     Abi, AddressMapSection, BuiltinFunctionIndex, CacheStore, CompileError, CompiledFunctionBody,
38     DefinedFuncIndex, FlagValue, FrameInstPos, FrameStackShape, FrameStateSlotBuilder,
39     FrameTableBuilder, FuncKey, FunctionBodyData, FunctionLoc, HostCall, InliningCompiler,
40     ModulePC, ModuleTranslation, ModuleTypesBuilder, PtrSize, StackMapSection, StaticModuleIndex,
41     TrapEncodingBuilder, TrapSentinel, TripleExt, Tunables, WasmFuncType, WasmValType, prelude::*,
42 };
43 use wasmtime_unwinder::ExceptionTableBuilder;
44 
45 #[cfg(feature = "component-model")]
46 mod component;
47 
48 struct IncrementalCacheContext {
49     #[cfg(feature = "incremental-cache")]
50     cache_store: Arc<dyn CacheStore>,
51     num_hits: usize,
52     num_cached: usize,
53 }
54 
55 struct CompilerContext {
56     func_translator: FuncTranslator,
57     codegen_context: Context,
58     incremental_cache_ctx: Option<IncrementalCacheContext>,
59     validator_allocations: FuncValidatorAllocations,
60     debug_slot_descriptor: Option<FrameStateSlotBuilder>,
61     abi: Option<Abi>,
62 }
63 
64 impl Default for CompilerContext {
default() -> Self65     fn default() -> Self {
66         Self {
67             func_translator: FuncTranslator::new(),
68             codegen_context: Context::new(),
69             incremental_cache_ctx: None,
70             validator_allocations: Default::default(),
71             debug_slot_descriptor: None,
72             abi: None,
73         }
74     }
75 }
76 
77 /// A compiler that compiles a WebAssembly module with Compiler, translating
78 /// the Wasm to Compiler IR, optimizing it and then translating to assembly.
79 pub struct Compiler {
80     tunables: Tunables,
81     contexts: Mutex<Vec<CompilerContext>>,
82     isa: OwnedTargetIsa,
83     emit_debug_checks: bool,
84     linkopts: LinkOptions,
85     cache_store: Option<Arc<dyn CacheStore>>,
86     clif_dir: Option<path::PathBuf>,
87     #[cfg(feature = "wmemcheck")]
88     pub(crate) wmemcheck: bool,
89 }
90 
91 impl Drop for Compiler {
drop(&mut self)92     fn drop(&mut self) {
93         if self.cache_store.is_none() {
94             return;
95         }
96 
97         let mut num_hits = 0;
98         let mut num_cached = 0;
99         for ctx in self.contexts.lock().unwrap().iter() {
100             if let Some(ref cache_ctx) = ctx.incremental_cache_ctx {
101                 num_hits += cache_ctx.num_hits;
102                 num_cached += cache_ctx.num_cached;
103             }
104         }
105 
106         let total = num_hits + num_cached;
107         if num_hits + num_cached > 0 {
108             log::trace!(
109                 "Incremental compilation cache stats: {}/{} = {}% (hits/lookup)\ncached: {}",
110                 num_hits,
111                 total,
112                 (num_hits as f32) / (total as f32) * 100.0,
113                 num_cached
114             );
115         }
116     }
117 }
118 
119 impl Compiler {
new( tunables: Tunables, isa: OwnedTargetIsa, cache_store: Option<Arc<dyn CacheStore>>, emit_debug_checks: bool, linkopts: LinkOptions, clif_dir: Option<path::PathBuf>, wmemcheck: bool, ) -> Compiler120     pub fn new(
121         tunables: Tunables,
122         isa: OwnedTargetIsa,
123         cache_store: Option<Arc<dyn CacheStore>>,
124         emit_debug_checks: bool,
125         linkopts: LinkOptions,
126         clif_dir: Option<path::PathBuf>,
127         wmemcheck: bool,
128     ) -> Compiler {
129         let _ = wmemcheck;
130         Compiler {
131             contexts: Default::default(),
132             tunables,
133             isa,
134             emit_debug_checks,
135             linkopts,
136             cache_store,
137             clif_dir,
138             #[cfg(feature = "wmemcheck")]
139             wmemcheck,
140         }
141     }
142 
143     /// Perform an indirect call from Cranelift-generated code to native code in
144     /// Wasmtime itself.
145     ///
146     /// For native platforms this is a simple `call_indirect` instruction but
147     /// for the Pulley backend this is special as it's transitioning from
148     /// Cranelift-generated bytecode to native code on the host. That requires a
149     /// special opcode in the interpreter and is modeled slightly differently in
150     /// Cranelift IR.
call_indirect_host( &self, builder: &mut FunctionBuilder<'_>, hostcall: impl Into<HostCall>, sig: ir::SigRef, addr: Value, args: &[Value], ) -> ir::Inst151     fn call_indirect_host(
152         &self,
153         builder: &mut FunctionBuilder<'_>,
154         hostcall: impl Into<HostCall>,
155         sig: ir::SigRef,
156         addr: Value,
157         args: &[Value],
158     ) -> ir::Inst {
159         let signature = &builder.func.dfg.signatures[sig];
160 
161         // When calling the host we should always be using the platform's
162         // default calling convention since it'll be calling Rust code in
163         // Wasmtime itself.
164         assert_eq!(signature.call_conv, self.isa.default_call_conv());
165 
166         // If this target is actually pulley then the goal is to emit the custom
167         // `call_indirect_host` pulley opcode. That's encoded in Cranelift as a
168         // `call` instruction where the name is `colocated: false`. This will
169         // force a pulley-specific relocation to get emitted in addition to
170         // using the `call_indirect_host` instruction.
171         if self.isa.triple().is_pulley() {
172             let mut new_signature = signature.clone();
173             new_signature
174                 .params
175                 .insert(0, ir::AbiParam::new(self.isa.pointer_type()));
176             let new_sig = builder.func.import_signature(new_signature);
177             let key = FuncKey::PulleyHostCall(hostcall.into());
178             let (namespace, index) = key.into_raw_parts();
179             let name = ir::ExternalName::User(
180                 builder
181                     .func
182                     .declare_imported_user_function(ir::UserExternalName { namespace, index }),
183             );
184             let func = builder.func.import_function(ir::ExtFuncData {
185                 name,
186                 signature: new_sig,
187                 // This is the signal that a special `call_indirect_host`
188                 // opcode is used to jump from pulley to the host.
189                 colocated: false,
190                 patchable: false,
191             });
192             let mut raw_args = vec![addr];
193             raw_args.extend_from_slice(args);
194             return builder.ins().call(func, &raw_args);
195         }
196 
197         builder.ins().call_indirect(sig, addr, args)
198     }
199 }
200 
box_dyn_any_compiled_function(f: CompiledFunction) -> Box<dyn Any + Send + Sync>201 fn box_dyn_any_compiled_function(f: CompiledFunction) -> Box<dyn Any + Send + Sync> {
202     let b = box_dyn_any(f);
203     debug_assert!(b.is::<CompiledFunction>());
204     b
205 }
206 
box_dyn_any_compiler_context(ctx: Option<CompilerContext>) -> Box<dyn Any + Send + Sync>207 fn box_dyn_any_compiler_context(ctx: Option<CompilerContext>) -> Box<dyn Any + Send + Sync> {
208     let b = box_dyn_any(ctx);
209     debug_assert!(b.is::<Option<CompilerContext>>());
210     b
211 }
212 
box_dyn_any(x: impl Any + Send + Sync) -> Box<dyn Any + Send + Sync>213 fn box_dyn_any(x: impl Any + Send + Sync) -> Box<dyn Any + Send + Sync> {
214     log::trace!(
215         "making Box<dyn Any + Send + Sync> of {}",
216         std::any::type_name_of_val(&x)
217     );
218     let b = Box::new(x);
219     let r: &(dyn Any + Sync + Send) = &*b;
220     log::trace!("  --> {r:#p}");
221     b
222 }
223 
224 impl wasmtime_environ::Compiler for Compiler {
inlining_compiler(&self) -> Option<&dyn wasmtime_environ::InliningCompiler>225     fn inlining_compiler(&self) -> Option<&dyn wasmtime_environ::InliningCompiler> {
226         Some(self)
227     }
228 
compile_function( &self, translation: &ModuleTranslation<'_>, key: FuncKey, input: FunctionBodyData<'_>, types: &ModuleTypesBuilder, symbol: &str, ) -> Result<CompiledFunctionBody, CompileError>229     fn compile_function(
230         &self,
231         translation: &ModuleTranslation<'_>,
232         key: FuncKey,
233         input: FunctionBodyData<'_>,
234         types: &ModuleTypesBuilder,
235         symbol: &str,
236     ) -> Result<CompiledFunctionBody, CompileError> {
237         log::trace!("compiling Wasm function: {key:?} = {symbol:?}");
238 
239         let isa = &*self.isa;
240         let module = &translation.module;
241 
242         let (module_index, def_func_index) = key.unwrap_defined_wasm_function();
243         debug_assert_eq!(translation.module_index(), module_index);
244 
245         let func_index = module.func_index(def_func_index);
246         let sig = translation.module.functions[func_index]
247             .signature
248             .unwrap_module_type_index();
249         let wasm_func_ty = types[sig].unwrap_func();
250 
251         let mut compiler = self.function_compiler();
252 
253         let context = &mut compiler.cx.codegen_context;
254         context.func.signature = wasm_call_signature(isa, wasm_func_ty, &self.tunables);
255         let (namespace, index) = key.into_raw_parts();
256         context.func.name = UserFuncName::User(UserExternalName { namespace, index });
257 
258         if self.tunables.debug_native {
259             context.func.collect_debug_info();
260         }
261 
262         let mut func_env = FuncEnvironment::new(self, translation, types, wasm_func_ty, key);
263 
264         // The `stack_limit` global value below is the implementation of stack
265         // overflow checks in Wasmtime.
266         //
267         // The Wasm spec defines that stack overflows will raise a trap, and
268         // there's also an added constraint where as an embedder you frequently
269         // are running host-provided code called from wasm. WebAssembly and
270         // native code currently share the same call stack, so Wasmtime needs to
271         // make sure that host-provided code will have enough call-stack
272         // available to it.
273         //
274         // The way that stack overflow is handled here is by adding a prologue
275         // check to all functions for how much native stack is remaining. The
276         // `VMContext` pointer is the first argument to all functions, and the
277         // first field of this structure is `*const VMStoreContext` and the
278         // third field of that is the stack limit. Note that the stack limit in
279         // this case means "if the stack pointer goes below this, trap". Each
280         // function which consumes stack space or isn't a leaf function starts
281         // off by loading the stack limit, checking it against the stack
282         // pointer, and optionally traps.
283         //
284         // This manual check allows the embedder to give wasm a relatively
285         // precise amount of stack allocation. Using this scheme we reserve a
286         // chunk of stack for wasm code relative from where wasm code was
287         // called. This ensures that native code called by wasm should have
288         // native stack space to run, and the numbers of stack spaces here
289         // should all be configurable for various embeddings.
290         //
291         // Note that this check is independent of each thread's stack guard page
292         // here. If the stack guard page is reached that's still considered an
293         // abort for the whole program since the runtime limits configured by
294         // the embedder should cause wasm to trap before it reaches that
295         // (ensuring the host has enough space as well for its functionality).
296         if !isa.triple().is_pulley() {
297             let vmctx = context
298                 .func
299                 .create_global_value(ir::GlobalValueData::VMContext);
300             let interrupts_ptr = context.func.create_global_value(ir::GlobalValueData::Load {
301                 base: vmctx,
302                 offset: i32::from(func_env.offsets.ptr.vmctx_store_context()).into(),
303                 global_type: isa.pointer_type(),
304                 flags: MemFlags::trusted().with_readonly(),
305             });
306             let stack_limit = context.func.create_global_value(ir::GlobalValueData::Load {
307                 base: interrupts_ptr,
308                 offset: i32::from(func_env.offsets.ptr.vmstore_context_stack_limit()).into(),
309                 global_type: isa.pointer_type(),
310                 flags: MemFlags::trusted(),
311             });
312             if self.tunables.signals_based_traps {
313                 context.func.stack_limit = Some(stack_limit);
314             } else {
315                 func_env.stack_limit_at_function_entry = Some(stack_limit);
316             }
317         }
318         let FunctionBodyData { validator, body } = input;
319         let mut validator =
320             validator.into_validator(mem::take(&mut compiler.cx.validator_allocations));
321         compiler.cx.func_translator.translate_body(
322             &mut validator,
323             body.clone(),
324             &mut context.func,
325             &mut func_env,
326         )?;
327 
328         if self.tunables.inlining {
329             compiler
330                 .cx
331                 .codegen_context
332                 .legalize(isa)
333                 .map_err(|e| CompileError::Codegen(e.to_string()))?;
334         }
335 
336         let needs_gc_heap = func_env.needs_gc_heap();
337 
338         if let Some((_, slot_builder)) = func_env.state_slot {
339             compiler.cx.debug_slot_descriptor = Some(slot_builder);
340         }
341 
342         let timing = cranelift_codegen::timing::take_current();
343         log::debug!("`{symbol}` translated to CLIF in {:?}", timing.total());
344         log::trace!("`{symbol}` timing info\n{timing}");
345 
346         Ok(CompiledFunctionBody {
347             code: box_dyn_any_compiler_context(Some(compiler.cx)),
348             needs_gc_heap,
349         })
350     }
351 
compile_array_to_wasm_trampoline( &self, translation: &ModuleTranslation<'_>, types: &ModuleTypesBuilder, key: FuncKey, symbol: &str, ) -> Result<CompiledFunctionBody, CompileError>352     fn compile_array_to_wasm_trampoline(
353         &self,
354         translation: &ModuleTranslation<'_>,
355         types: &ModuleTypesBuilder,
356         key: FuncKey,
357         symbol: &str,
358     ) -> Result<CompiledFunctionBody, CompileError> {
359         let (module_index, def_func_index) = key.unwrap_array_to_wasm_trampoline();
360         let func_index = translation.module.func_index(def_func_index);
361         let sig = translation.module.functions[func_index]
362             .signature
363             .unwrap_module_type_index();
364         self.array_to_wasm_trampoline(
365             key,
366             FuncKey::DefinedWasmFunction(module_index, def_func_index),
367             types[sig].unwrap_func(),
368             symbol,
369             self.isa.pointer_bytes().vmctx_store_context().into(),
370             wasmtime_environ::VMCONTEXT_MAGIC,
371         )
372     }
373 
compile_wasm_to_array_trampoline( &self, wasm_func_ty: &WasmFuncType, key: FuncKey, symbol: &str, ) -> Result<CompiledFunctionBody, CompileError>374     fn compile_wasm_to_array_trampoline(
375         &self,
376         wasm_func_ty: &WasmFuncType,
377         key: FuncKey,
378         symbol: &str,
379     ) -> Result<CompiledFunctionBody, CompileError> {
380         log::trace!("compiling wasm-to-array trampoline: {key:?} = {symbol:?}");
381 
382         let isa = &*self.isa;
383         let pointer_type = isa.pointer_type();
384         let wasm_call_sig = wasm_call_signature(isa, wasm_func_ty, &self.tunables);
385         let array_call_sig = array_call_signature(isa);
386 
387         let mut compiler = self.function_compiler();
388         let func = ir::Function::with_name_signature(key_to_name(key), wasm_call_sig);
389         let (mut builder, block0) = compiler.builder(func);
390 
391         let args = builder.func.dfg.block_params(block0).to_vec();
392         let callee_vmctx = args[0];
393         let caller_vmctx = args[1];
394 
395         // We are exiting Wasm, so save our PC and FP.
396         //
397         // Assert that the caller vmctx really is a core Wasm vmctx, since
398         // that's what we are assuming with our offsets below.
399         self.debug_assert_vmctx_kind(
400             &mut builder,
401             caller_vmctx,
402             wasmtime_environ::VMCONTEXT_MAGIC,
403         );
404         let ptr = isa.pointer_bytes();
405         let vm_store_context = builder.ins().load(
406             pointer_type,
407             MemFlags::trusted(),
408             caller_vmctx,
409             i32::from(ptr.vmcontext_store_context()),
410         );
411         save_last_wasm_exit_fp_and_pc(&mut builder, pointer_type, &ptr, vm_store_context);
412 
413         // Spill all wasm arguments to the stack in `ValRaw` slots.
414         let (args_base, args_len) =
415             self.allocate_stack_array_and_spill_args(wasm_func_ty, &mut builder, &args[2..]);
416         let args_len = builder.ins().iconst(pointer_type, i64::from(args_len));
417 
418         // Load the actual callee out of the
419         // `VMArrayCallHostFuncContext::host_func`.
420         let ptr_size = isa.pointer_bytes();
421         let callee = builder.ins().load(
422             pointer_type,
423             MemFlags::trusted(),
424             callee_vmctx,
425             ptr_size.vmarray_call_host_func_context_func_ref() + ptr_size.vm_func_ref_array_call(),
426         );
427 
428         // Do an indirect call to the callee.
429         let callee_signature = builder.func.import_signature(array_call_sig);
430         let call = self.call_indirect_host(
431             &mut builder,
432             HostCall::ArrayCall,
433             callee_signature,
434             callee,
435             &[callee_vmctx, caller_vmctx, args_base, args_len],
436         );
437 
438         // Increment the "execution version" on the VMStoreContext if
439         // guest debugging is enabled.
440         if self.tunables.debug_guest {
441             let vmstore_ctx_ptr = builder.ins().load(
442                 pointer_type,
443                 MemFlags::trusted().with_readonly(),
444                 caller_vmctx,
445                 i32::from(ptr_size.vmctx_store_context()),
446             );
447             let old_version = builder.ins().load(
448                 ir::types::I64,
449                 MemFlags::trusted(),
450                 vmstore_ctx_ptr,
451                 i32::from(ptr_size.vmstore_context_execution_version()),
452             );
453             let new_version = builder.ins().iadd_imm(old_version, 1);
454             builder.ins().store(
455                 MemFlags::trusted(),
456                 new_version,
457                 vmstore_ctx_ptr,
458                 i32::from(ptr_size.vmstore_context_execution_version()),
459             );
460         }
461 
462         // Invoke `raise` if the callee (host) returned an error.
463         let succeeded = builder.func.dfg.inst_results(call)[0];
464         self.raise_if_host_trapped(&mut builder, caller_vmctx, succeeded);
465 
466         // Return results from the array as native return values.
467         let results =
468             self.load_values_from_array(wasm_func_ty.results(), &mut builder, args_base, args_len);
469         builder.ins().return_(&results);
470         builder.finalize();
471 
472         Ok(CompiledFunctionBody {
473             code: box_dyn_any_compiler_context(Some(compiler.cx)),
474             needs_gc_heap: false,
475         })
476     }
477 
append_code( &self, obj: &mut Object<'static>, funcs: &[(String, FuncKey, Box<dyn Any + Send + Sync>)], resolve_reloc: &dyn Fn(usize, FuncKey) -> usize, ) -> Result<Vec<(SymbolId, FunctionLoc)>>478     fn append_code(
479         &self,
480         obj: &mut Object<'static>,
481         funcs: &[(String, FuncKey, Box<dyn Any + Send + Sync>)],
482         resolve_reloc: &dyn Fn(usize, FuncKey) -> usize,
483     ) -> Result<Vec<(SymbolId, FunctionLoc)>> {
484         log::trace!(
485             "appending functions to object file: {:#?}",
486             funcs.iter().map(|(sym, _, _)| sym).collect::<Vec<_>>()
487         );
488 
489         let mut builder =
490             ModuleTextBuilder::new(obj, self, self.isa.text_section_builder(funcs.len()));
491         if self.linkopts.force_jump_veneers {
492             builder.force_veneers();
493         }
494         let mut addrs = AddressMapSection::default();
495         let mut traps = TrapEncodingBuilder::default();
496         let mut stack_maps = StackMapSection::default();
497         let mut exception_tables = ExceptionTableBuilder::default();
498         let mut frame_tables = FrameTableBuilder::default();
499 
500         let funcs = funcs
501             .iter()
502             .map(|(sym, key, func)| {
503                 debug_assert!(!func.is::<Option<CompilerContext>>());
504                 debug_assert!(func.is::<CompiledFunction>());
505                 let func = func.downcast_ref::<CompiledFunction>().unwrap();
506                 (sym, *key, func)
507             })
508             .collect::<Vec<_>>();
509 
510         let mut frame_descriptors = HashMap::new();
511         if self.tunables.debug_guest {
512             for (_, key, func) in &funcs {
513                 frame_descriptors.insert(
514                     *key,
515                     func.debug_slot_descriptor
516                         .as_ref()
517                         .map(|builder| builder.serialize())
518                         .unwrap_or_else(|| vec![]),
519                 );
520             }
521         }
522 
523         let mut breakpoint_table: Vec<(ModulePC, Range<u32>)> = Vec::new();
524         let mut nop_units = None;
525 
526         let mut ret = Vec::with_capacity(funcs.len());
527         for (i, (sym, _key, func)) in funcs.iter().enumerate() {
528             let (sym_id, range) = builder.append_func(&sym, func, |idx| resolve_reloc(i, idx));
529             log::trace!("symbol id {sym_id:?} = {sym:?}");
530 
531             if self.tunables.generate_address_map {
532                 let addr = func.address_map();
533                 addrs.push(range.clone(), &addr.instructions);
534             }
535 
536             clif_to_env_stack_maps(
537                 &mut stack_maps,
538                 range.clone(),
539                 func.buffer.user_stack_maps(),
540             );
541 
542             traps.push(range.clone(), &func.traps().collect::<Vec<_>>());
543             clif_to_env_exception_tables(
544                 &mut exception_tables,
545                 range.clone(),
546                 func.buffer.call_sites(),
547             )?;
548             if self.tunables.debug_guest
549                 && let Some(frame_layout) = func.buffer.frame_layout()
550             {
551                 clif_to_env_frame_tables(
552                     &mut frame_tables,
553                     range.clone(),
554                     func.buffer.debug_tags(),
555                     frame_layout,
556                     &frame_descriptors,
557                 )?;
558             }
559             if self.tunables.debug_guest {
560                 clif_to_env_breakpoints(
561                     range.clone(),
562                     func.breakpoint_patches(),
563                     &mut breakpoint_table,
564                 )?;
565                 nop_units.get_or_insert_with(|| func.buffer.nop_units.clone());
566             }
567             builder.append_padding(self.linkopts.padding_between_functions);
568 
569             let info = FunctionLoc {
570                 start: u32::try_from(range.start).unwrap(),
571                 length: u32::try_from(range.end - range.start).unwrap(),
572             };
573             ret.push((sym_id, info));
574         }
575 
576         // Sort breakpoints by Wasm PC now. Note that the same Wasm PC
577         // may appear in multiple functions (due to inlining) so it is
578         // only now that we can aggregate all appearances of a PC
579         // together for breakpoint metadata indexed by that PC.
580         breakpoint_table.sort_by_key(|(wasm_pc, _text_range)| *wasm_pc);
581 
582         builder.finish(|text| {
583             if !breakpoint_table.is_empty() {
584                 let nop_units = nop_units.as_ref().unwrap();
585                 let fill_with_nops = |mut slice: &mut [u8]| {
586                     while !slice.is_empty() {
587                         let nop_unit = nop_units
588                             .iter()
589                             .rev()
590                             .find(|u| u.len() <= slice.len())
591                             .expect("no NOP is small enough for remaining slice");
592                         let (nop_sized_chunk, rest) = slice.split_at_mut(nop_unit.len());
593                         nop_sized_chunk.copy_from_slice(&nop_unit);
594                         slice = rest;
595                     }
596                 };
597 
598                 for (wasm_pc, text_range) in &breakpoint_table {
599                     let start = usize::try_from(text_range.start).unwrap();
600                     let end = usize::try_from(text_range.end).unwrap();
601                     let text = &mut text[start..end];
602                     frame_tables.add_breakpoint_patch(*wasm_pc, text_range.start, text);
603                     fill_with_nops(text);
604                 }
605             }
606         });
607 
608         if self.tunables.generate_address_map {
609             addrs.append_to(obj);
610         }
611         stack_maps.append_to(obj);
612         traps.append_to(obj);
613 
614         let exception_section = obj.add_section(
615             obj.segment_name(StandardSegment::Data).to_vec(),
616             ELF_WASMTIME_EXCEPTIONS.as_bytes().to_vec(),
617             SectionKind::ReadOnlyData,
618         );
619         exception_tables.serialize(|bytes| {
620             obj.append_section_data(exception_section, bytes, 1);
621         });
622 
623         if self.tunables.debug_guest {
624             let frame_table_section = obj.add_section(
625                 obj.segment_name(StandardSegment::Data).to_vec(),
626                 ELF_WASMTIME_FRAMES.as_bytes().to_vec(),
627                 SectionKind::ReadOnlyData,
628             );
629             frame_tables.serialize(|bytes| {
630                 obj.append_section_data(frame_table_section, bytes, 1);
631             });
632         }
633 
634         Ok(ret)
635     }
636 
triple(&self) -> &target_lexicon::Triple637     fn triple(&self) -> &target_lexicon::Triple {
638         self.isa.triple()
639     }
640 
flags(&self) -> Vec<(&'static str, FlagValue<'static>)>641     fn flags(&self) -> Vec<(&'static str, FlagValue<'static>)> {
642         crate::clif_flags_to_wasmtime(self.isa.flags().iter())
643     }
644 
isa_flags(&self) -> Vec<(&'static str, FlagValue<'static>)>645     fn isa_flags(&self) -> Vec<(&'static str, FlagValue<'static>)> {
646         crate::clif_flags_to_wasmtime(self.isa.isa_flags())
647     }
648 
is_branch_protection_enabled(&self) -> bool649     fn is_branch_protection_enabled(&self) -> bool {
650         self.isa.is_branch_protection_enabled()
651     }
652 
653     #[cfg(feature = "component-model")]
component_compiler(&self) -> &dyn wasmtime_environ::component::ComponentCompiler654     fn component_compiler(&self) -> &dyn wasmtime_environ::component::ComponentCompiler {
655         self
656     }
657 
append_dwarf<'a>( &self, obj: &mut Object<'_>, translations: &'a PrimaryMap<StaticModuleIndex, ModuleTranslation<'a>>, get_func: &'a dyn Fn( StaticModuleIndex, DefinedFuncIndex, ) -> (SymbolId, &'a (dyn Any + Send + Sync)), dwarf_package_bytes: Option<&'a [u8]>, tunables: &'a Tunables, ) -> Result<()>658     fn append_dwarf<'a>(
659         &self,
660         obj: &mut Object<'_>,
661         translations: &'a PrimaryMap<StaticModuleIndex, ModuleTranslation<'a>>,
662         get_func: &'a dyn Fn(
663             StaticModuleIndex,
664             DefinedFuncIndex,
665         ) -> (SymbolId, &'a (dyn Any + Send + Sync)),
666         dwarf_package_bytes: Option<&'a [u8]>,
667         tunables: &'a Tunables,
668     ) -> Result<()> {
669         log::trace!("appending DWARF debug info");
670 
671         let get_func = move |m, f| {
672             let (sym, any) = get_func(m, f);
673             log::trace!("get_func({m:?}, {f:?}) -> ({sym:?}, {any:#p})");
674             debug_assert!(!any.is::<Option<CompilerContext>>());
675             debug_assert!(any.is::<CompiledFunction>());
676             (
677                 sym,
678                 any.downcast_ref::<CompiledFunction>().unwrap().metadata(),
679             )
680         };
681 
682         let mut compilation = crate::debug::Compilation::new(
683             &*self.isa,
684             translations,
685             &get_func,
686             dwarf_package_bytes,
687             tunables,
688         );
689         let dwarf_sections = crate::debug::emit_dwarf(&*self.isa, &mut compilation)
690             .with_context(|| "failed to emit DWARF debug information")?;
691 
692         let (debug_bodies, debug_relocs): (Vec<_>, Vec<_>) = dwarf_sections
693             .iter()
694             .map(|s| ((s.name, &s.body), (s.name, &s.relocs)))
695             .unzip();
696         let mut dwarf_sections_ids = HashMap::new();
697         for (name, body) in debug_bodies {
698             let segment = obj.segment_name(StandardSegment::Debug).to_vec();
699             let section_id = obj.add_section(segment, name.as_bytes().to_vec(), SectionKind::Debug);
700             dwarf_sections_ids.insert(name, section_id);
701             obj.append_section_data(section_id, &body, 1);
702         }
703 
704         // Write all debug data relocations.
705         for (name, relocs) in debug_relocs {
706             let section_id = *dwarf_sections_ids.get(name).unwrap();
707             for reloc in relocs {
708                 let target_symbol = match reloc.target {
709                     DwarfSectionRelocTarget::Func(id) => compilation.symbol_id(id),
710                     DwarfSectionRelocTarget::Section(name) => {
711                         obj.section_symbol(dwarf_sections_ids[name])
712                     }
713                 };
714                 obj.add_relocation(
715                     section_id,
716                     object::write::Relocation {
717                         offset: u64::from(reloc.offset),
718                         symbol: target_symbol,
719                         addend: i64::from(reloc.addend),
720                         flags: RelocationFlags::Generic {
721                             size: reloc.size << 3,
722                             kind: RelocationKind::Absolute,
723                             encoding: RelocationEncoding::Generic,
724                         },
725                     },
726                 )?;
727             }
728         }
729 
730         Ok(())
731     }
732 
create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry>733     fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
734         self.isa.create_systemv_cie()
735     }
736 
compile_wasm_to_builtin( &self, key: FuncKey, symbol: &str, ) -> Result<CompiledFunctionBody, CompileError>737     fn compile_wasm_to_builtin(
738         &self,
739         key: FuncKey,
740         symbol: &str,
741     ) -> Result<CompiledFunctionBody, CompileError> {
742         log::trace!("compiling wasm-to-builtin trampoline: {key:?} = {symbol:?}");
743 
744         let isa = &*self.isa;
745         let ptr_size = isa.pointer_bytes();
746         let pointer_type = isa.pointer_type();
747         let sigs = BuiltinFunctionSignatures::new(self);
748 
749         let (builtin_func_index, wasm_sig) = match key {
750             FuncKey::WasmToBuiltinTrampoline(builtin) => (builtin, sigs.wasm_signature(builtin)),
751             FuncKey::PatchableToBuiltinTrampoline(builtin) => {
752                 let mut sig = sigs.wasm_signature(builtin);
753                 // Patchable functions cannot return anything. We
754                 // raise any errors that occur below so this is fine.
755                 sig.returns.clear();
756                 sig.call_conv = CallConv::PreserveAll;
757                 (builtin, sig)
758             }
759             _ => unreachable!(),
760         };
761         let host_sig = sigs.host_signature(builtin_func_index);
762 
763         let mut compiler = self.function_compiler();
764         let func = ir::Function::with_name_signature(key_to_name(key), wasm_sig.clone());
765         let (mut builder, block0) = compiler.builder(func);
766         let vmctx = builder.block_params(block0)[0];
767 
768         // Debug-assert that this is the right kind of vmctx, and then
769         // additionally perform the "routine of the exit trampoline" of saving
770         // fp/pc/etc.
771         self.debug_assert_vmctx_kind(&mut builder, vmctx, wasmtime_environ::VMCONTEXT_MAGIC);
772         let vm_store_context = builder.ins().load(
773             pointer_type,
774             MemFlags::trusted(),
775             vmctx,
776             ptr_size.vmcontext_store_context(),
777         );
778         save_last_wasm_exit_fp_and_pc(&mut builder, pointer_type, &ptr_size, vm_store_context);
779 
780         // Now it's time to delegate to the actual builtin. Forward all our own
781         // arguments to the libcall itself.
782         let args = builder.block_params(block0).to_vec();
783         let call = self.call_builtin(&mut builder, vmctx, &args, builtin_func_index, host_sig);
784         let results = builder.func.dfg.inst_results(call).to_vec();
785 
786         // Libcalls do not explicitly jump/raise on traps but instead return a
787         // code indicating whether they trapped or not. This means that it's the
788         // responsibility of the trampoline to check for an trapping return
789         // value and raise a trap as appropriate. With the `results` above check
790         // what `index` is and for each libcall that has a trapping return value
791         // process it here.
792         match builtin_func_index.trap_sentinel() {
793             Some(TrapSentinel::Falsy) => {
794                 self.raise_if_host_trapped(&mut builder, vmctx, results[0]);
795             }
796             Some(TrapSentinel::NegativeTwo) => {
797                 let ty = builder.func.dfg.value_type(results[0]);
798                 let trapped = builder.ins().iconst(ty, -2);
799                 let succeeded = builder.ins().icmp(IntCC::NotEqual, results[0], trapped);
800                 self.raise_if_host_trapped(&mut builder, vmctx, succeeded);
801             }
802             Some(TrapSentinel::Negative) => {
803                 let ty = builder.func.dfg.value_type(results[0]);
804                 let zero = builder.ins().iconst(ty, 0);
805                 let succeeded =
806                     builder
807                         .ins()
808                         .icmp(IntCC::SignedGreaterThanOrEqual, results[0], zero);
809                 self.raise_if_host_trapped(&mut builder, vmctx, succeeded);
810             }
811             Some(TrapSentinel::NegativeOne) => {
812                 let ty = builder.func.dfg.value_type(results[0]);
813                 let minus_one = builder.ins().iconst(ty, -1);
814                 let succeeded = builder.ins().icmp(IntCC::NotEqual, results[0], minus_one);
815                 self.raise_if_host_trapped(&mut builder, vmctx, succeeded);
816             }
817             None => {}
818         }
819 
820         // And finally, return all the results of this libcall.
821         if !wasm_sig.returns.is_empty() {
822             builder.ins().return_(&results);
823         } else {
824             builder.ins().return_(&[]);
825         }
826         builder.finalize();
827 
828         Ok(CompiledFunctionBody {
829             code: box_dyn_any_compiler_context(Some(compiler.cx)),
830             needs_gc_heap: false,
831         })
832     }
833 
compiled_function_relocation_targets<'a>( &'a self, func: &'a dyn Any, ) -> Box<dyn Iterator<Item = FuncKey> + 'a>834     fn compiled_function_relocation_targets<'a>(
835         &'a self,
836         func: &'a dyn Any,
837     ) -> Box<dyn Iterator<Item = FuncKey> + 'a> {
838         debug_assert!(!func.is::<Option<CompilerContext>>());
839         debug_assert!(func.is::<CompiledFunction>());
840         let func = func.downcast_ref::<CompiledFunction>().unwrap();
841         Box::new(func.relocations().map(|r| r.reloc_target))
842     }
843 }
844 
845 impl InliningCompiler for Compiler {
calls(&self, func_body: &CompiledFunctionBody, calls: &mut IndexSet<FuncKey>) -> Result<()>846     fn calls(&self, func_body: &CompiledFunctionBody, calls: &mut IndexSet<FuncKey>) -> Result<()> {
847         debug_assert!(!func_body.code.is::<CompiledFunction>());
848         debug_assert!(func_body.code.is::<Option<CompilerContext>>());
849         let cx = func_body
850             .code
851             .downcast_ref::<Option<CompilerContext>>()
852             .unwrap()
853             .as_ref()
854             .unwrap();
855         let func = &cx.codegen_context.func;
856         calls.extend(
857             func.params
858                 .user_named_funcs()
859                 .values()
860                 .map(|name| FuncKey::from_raw_parts(name.namespace, name.index))
861                 .filter(|key| match key {
862                     FuncKey::DefinedWasmFunction(..) => true,
863                     #[cfg(feature = "component-model")]
864                     FuncKey::UnsafeIntrinsic(..) => true,
865                     _ => false,
866                 }),
867         );
868         Ok(())
869     }
870 
size(&self, func_body: &CompiledFunctionBody) -> u32871     fn size(&self, func_body: &CompiledFunctionBody) -> u32 {
872         debug_assert!(!func_body.code.is::<CompiledFunction>());
873         debug_assert!(func_body.code.is::<Option<CompilerContext>>());
874         let cx = func_body
875             .code
876             .downcast_ref::<Option<CompilerContext>>()
877             .unwrap()
878             .as_ref()
879             .unwrap();
880         let func = &cx.codegen_context.func;
881         let size = func.dfg.values().len();
882         u32::try_from(size).unwrap()
883     }
884 
inline<'a>( &self, func_body: &mut CompiledFunctionBody, get_callee: &'a mut dyn FnMut(FuncKey) -> Option<&'a CompiledFunctionBody>, ) -> Result<()>885     fn inline<'a>(
886         &self,
887         func_body: &mut CompiledFunctionBody,
888         get_callee: &'a mut dyn FnMut(FuncKey) -> Option<&'a CompiledFunctionBody>,
889     ) -> Result<()> {
890         debug_assert!(!func_body.code.is::<CompiledFunction>());
891         debug_assert!(func_body.code.is::<Option<CompilerContext>>());
892         let code = func_body
893             .code
894             .downcast_mut::<Option<CompilerContext>>()
895             .unwrap();
896         let cx = code.as_mut().unwrap();
897 
898         cx.codegen_context.inline(Inliner(get_callee))?;
899         return Ok(());
900 
901         struct Inliner<'a>(&'a mut dyn FnMut(FuncKey) -> Option<&'a CompiledFunctionBody>);
902 
903         impl cranelift_codegen::inline::Inline for Inliner<'_> {
904             fn inline(
905                 &mut self,
906                 caller: &ir::Function,
907                 _call_inst: ir::Inst,
908                 _call_opcode: ir::Opcode,
909                 callee: ir::FuncRef,
910                 _call_args: &[ir::Value],
911             ) -> InlineCommand<'_> {
912                 let callee = &caller.dfg.ext_funcs[callee].name;
913                 let callee = match callee {
914                     ir::ExternalName::User(callee) => *callee,
915                     ir::ExternalName::TestCase(_)
916                     | ir::ExternalName::LibCall(_)
917                     | ir::ExternalName::KnownSymbol(_) => return InlineCommand::KeepCall,
918                 };
919                 let callee = &caller.params.user_named_funcs()[callee];
920                 let callee = FuncKey::from_raw_parts(callee.namespace, callee.index);
921                 match callee {
922                     FuncKey::DefinedWasmFunction(..) => {}
923                     #[cfg(feature = "component-model")]
924                     FuncKey::UnsafeIntrinsic(..) => {}
925                     _ => return InlineCommand::KeepCall,
926                 }
927                 match (self.0)(callee) {
928                     None => InlineCommand::KeepCall,
929                     Some(func_body) => {
930                         debug_assert!(!func_body.code.is::<CompiledFunction>());
931                         debug_assert!(func_body.code.is::<Option<CompilerContext>>());
932                         let cx = func_body
933                             .code
934                             .downcast_ref::<Option<CompilerContext>>()
935                             .unwrap();
936                         InlineCommand::Inline {
937                             callee: Cow::Borrowed(&cx.as_ref().unwrap().codegen_context.func),
938                             // We've already visited the callee for inlining
939                             // due to our bottom-up approach, no need to
940                             // visit it again.
941                             visit_callee: false,
942                         }
943                     }
944                 }
945             }
946         }
947     }
948 
finish_compiling( &self, func_body: &mut CompiledFunctionBody, input: Option<wasmparser::FunctionBody<'_>>, symbol: &str, ) -> Result<()>949     fn finish_compiling(
950         &self,
951         func_body: &mut CompiledFunctionBody,
952         input: Option<wasmparser::FunctionBody<'_>>,
953         symbol: &str,
954     ) -> Result<()> {
955         log::trace!("finish compiling {symbol:?}");
956         debug_assert!(!func_body.code.is::<CompiledFunction>());
957         debug_assert!(func_body.code.is::<Option<CompilerContext>>());
958         let cx = func_body
959             .code
960             .downcast_mut::<Option<CompilerContext>>()
961             .unwrap()
962             .take()
963             .unwrap();
964         let compiler = FunctionCompiler { compiler: self, cx };
965 
966         let symbol = match compiler.cx.abi {
967             None => Cow::Borrowed(symbol),
968             Some(Abi::Wasm) => Cow::Owned(format!("{symbol}_wasm_call")),
969             Some(Abi::Array) => Cow::Owned(format!("{symbol}_array_call")),
970             Some(Abi::Patchable) => Cow::Owned(format!("{symbol}_patchable_call")),
971         };
972 
973         let compiled_func = if let Some(input) = input {
974             compiler.finish_with_info(Some((&input, &self.tunables)), &symbol)?
975         } else {
976             compiler.finish(&symbol)?
977         };
978 
979         let timing = cranelift_codegen::timing::take_current();
980         log::debug!("`{symbol}` compiled in {:?}", timing.total());
981         log::trace!("`{symbol}` timing info\n{timing}");
982 
983         func_body.code = box_dyn_any_compiled_function(compiled_func);
984         Ok(())
985     }
986 }
987 
988 #[cfg(feature = "incremental-cache")]
989 mod incremental_cache {
990     use super::*;
991 
992     struct CraneliftCacheStore(Arc<dyn CacheStore>);
993 
994     impl cranelift_codegen::incremental_cache::CacheKvStore for CraneliftCacheStore {
get(&self, key: &[u8]) -> Option<std::borrow::Cow<'_, [u8]>>995         fn get(&self, key: &[u8]) -> Option<std::borrow::Cow<'_, [u8]>> {
996             self.0.get(key)
997         }
insert(&mut self, key: &[u8], val: Vec<u8>)998         fn insert(&mut self, key: &[u8], val: Vec<u8>) {
999             self.0.insert(key, val);
1000         }
1001     }
1002 
compile_maybe_cached<'a>( context: &'a mut Context, isa: &dyn TargetIsa, cache_ctx: Option<&mut IncrementalCacheContext>, ) -> Result<CompiledCode, CompileError>1003     pub(super) fn compile_maybe_cached<'a>(
1004         context: &'a mut Context,
1005         isa: &dyn TargetIsa,
1006         cache_ctx: Option<&mut IncrementalCacheContext>,
1007     ) -> Result<CompiledCode, CompileError> {
1008         let cache_ctx = match cache_ctx {
1009             Some(ctx) => ctx,
1010             None => return compile_uncached(context, isa),
1011         };
1012 
1013         let mut cache_store = CraneliftCacheStore(cache_ctx.cache_store.clone());
1014         let (_compiled_code, from_cache) = context
1015             .compile_with_cache(isa, &mut cache_store, &mut Default::default())
1016             .map_err(|error| CompileError::Codegen(pretty_error(&error.func, error.inner)))?;
1017 
1018         if from_cache {
1019             cache_ctx.num_hits += 1;
1020         } else {
1021             cache_ctx.num_cached += 1;
1022         }
1023 
1024         Ok(context.take_compiled_code().unwrap())
1025     }
1026 }
1027 
1028 #[cfg(feature = "incremental-cache")]
1029 use incremental_cache::*;
1030 
1031 #[cfg(not(feature = "incremental-cache"))]
compile_maybe_cached<'a>( context: &'a mut Context, isa: &dyn TargetIsa, _cache_ctx: Option<&mut IncrementalCacheContext>, ) -> Result<CompiledCode, CompileError>1032 fn compile_maybe_cached<'a>(
1033     context: &'a mut Context,
1034     isa: &dyn TargetIsa,
1035     _cache_ctx: Option<&mut IncrementalCacheContext>,
1036 ) -> Result<CompiledCode, CompileError> {
1037     compile_uncached(context, isa)
1038 }
1039 
compile_uncached<'a>( context: &'a mut Context, isa: &dyn TargetIsa, ) -> Result<CompiledCode, CompileError>1040 fn compile_uncached<'a>(
1041     context: &'a mut Context,
1042     isa: &dyn TargetIsa,
1043 ) -> Result<CompiledCode, CompileError> {
1044     context
1045         .compile(isa, &mut Default::default())
1046         .map_err(|error| CompileError::Codegen(pretty_error(&error.func, error.inner)))?;
1047     Ok(context.take_compiled_code().unwrap())
1048 }
1049 
1050 impl Compiler {
1051     /// This function will allocate a stack slot suitable for storing both the
1052     /// arguments and return values of the function, and then the arguments will
1053     /// all be stored in this block.
1054     ///
1055     /// `block0` must be the entry block of the function and `ty` must be the
1056     /// Wasm function type of the trampoline.
1057     ///
1058     /// The stack slot pointer is returned in addition to the size, in units of
1059     /// `ValRaw`, of the stack slot.
allocate_stack_array_and_spill_args( &self, ty: &WasmFuncType, builder: &mut FunctionBuilder, args: &[ir::Value], ) -> (Value, u32)1060     fn allocate_stack_array_and_spill_args(
1061         &self,
1062         ty: &WasmFuncType,
1063         builder: &mut FunctionBuilder,
1064         args: &[ir::Value],
1065     ) -> (Value, u32) {
1066         let isa = &*self.isa;
1067         let pointer_type = isa.pointer_type();
1068 
1069         // Compute the size of the values vector.
1070         let value_size = mem::size_of::<u128>();
1071         let values_vec_len = cmp::max(ty.params().len(), ty.results().len());
1072         let values_vec_byte_size = u32::try_from(value_size * values_vec_len).unwrap();
1073         let values_vec_len = u32::try_from(values_vec_len).unwrap();
1074 
1075         let slot = builder.func.create_sized_stack_slot(ir::StackSlotData::new(
1076             ir::StackSlotKind::ExplicitSlot,
1077             values_vec_byte_size,
1078             4,
1079         ));
1080         let values_vec_ptr = builder.ins().stack_addr(pointer_type, slot, 0);
1081 
1082         {
1083             let values_vec_len = builder
1084                 .ins()
1085                 .iconst(ir::types::I32, i64::from(values_vec_len));
1086             self.store_values_to_array(builder, ty.params(), args, values_vec_ptr, values_vec_len);
1087         }
1088 
1089         (values_vec_ptr, values_vec_len)
1090     }
1091 
1092     /// Store values to an array in the array calling convention.
1093     ///
1094     /// Used either to store arguments to the array when calling a function
1095     /// using the array calling convention, or used to store results to the
1096     /// array when implementing a function that exposes the array calling
1097     /// convention.
store_values_to_array( &self, builder: &mut FunctionBuilder, types: &[WasmValType], values: &[Value], values_vec_ptr: Value, values_vec_capacity: Value, )1098     fn store_values_to_array(
1099         &self,
1100         builder: &mut FunctionBuilder,
1101         types: &[WasmValType],
1102         values: &[Value],
1103         values_vec_ptr: Value,
1104         values_vec_capacity: Value,
1105     ) {
1106         debug_assert_eq!(types.len(), values.len());
1107         self.debug_assert_enough_capacity_for_length(builder, types.len(), values_vec_capacity);
1108 
1109         // Note that loads and stores are unconditionally done in the
1110         // little-endian format rather than the host's native-endianness,
1111         // despite this load/store being unrelated to execution in Wasm itself.
1112         // For more details on this see the `ValRaw` type in
1113         // `wasmtime::runtime::vm`.
1114         let flags = ir::MemFlags::new()
1115             .with_notrap()
1116             .with_endianness(ir::Endianness::Little);
1117 
1118         let value_size = mem::size_of::<u128>();
1119         for (i, val) in values.iter().copied().enumerate() {
1120             crate::unbarriered_store_type_at_offset(
1121                 &mut builder.cursor(),
1122                 flags,
1123                 values_vec_ptr,
1124                 i32::try_from(i * value_size).unwrap(),
1125                 val,
1126             );
1127         }
1128     }
1129 
1130     /// Used for loading the values of an array-call host function's value
1131     /// array.
1132     ///
1133     /// This can be used to load arguments out of the array if the trampoline we
1134     /// are building exposes the array calling convention, or it can be used to
1135     /// load results out of the array if the trampoline we are building calls a
1136     /// function that uses the array calling convention.
load_values_from_array( &self, types: &[WasmValType], builder: &mut FunctionBuilder, values_vec_ptr: Value, values_vec_capacity: Value, ) -> Vec<ir::Value>1137     fn load_values_from_array(
1138         &self,
1139         types: &[WasmValType],
1140         builder: &mut FunctionBuilder,
1141         values_vec_ptr: Value,
1142         values_vec_capacity: Value,
1143     ) -> Vec<ir::Value> {
1144         let isa = &*self.isa;
1145         let value_size = mem::size_of::<u128>();
1146 
1147         self.debug_assert_enough_capacity_for_length(builder, types.len(), values_vec_capacity);
1148 
1149         // Note that this is little-endian like `store_values_to_array` above,
1150         // see notes there for more information.
1151         let flags = MemFlags::new()
1152             .with_notrap()
1153             .with_endianness(ir::Endianness::Little);
1154 
1155         let mut results = Vec::new();
1156         for (i, ty) in types.iter().enumerate() {
1157             results.push(crate::unbarriered_load_type_at_offset(
1158                 isa,
1159                 &mut builder.cursor(),
1160                 *ty,
1161                 flags,
1162                 values_vec_ptr,
1163                 i32::try_from(i * value_size).unwrap(),
1164             ));
1165         }
1166         results
1167     }
1168 
function_compiler(&self) -> FunctionCompiler<'_>1169     fn function_compiler(&self) -> FunctionCompiler<'_> {
1170         let saved_context = self.contexts.lock().unwrap().pop();
1171         FunctionCompiler {
1172             compiler: self,
1173             cx: saved_context
1174                 .map(|mut ctx| {
1175                     ctx.codegen_context.clear();
1176                     ctx
1177                 })
1178                 .unwrap_or_else(|| CompilerContext {
1179                     #[cfg(feature = "incremental-cache")]
1180                     incremental_cache_ctx: self.cache_store.as_ref().map(|cache_store| {
1181                         IncrementalCacheContext {
1182                             cache_store: cache_store.clone(),
1183                             num_hits: 0,
1184                             num_cached: 0,
1185                         }
1186                     }),
1187                     ..Default::default()
1188                 }),
1189         }
1190     }
1191 
1192     /// Invokes the `raise` libcall in `vmctx` if the `succeeded` value
1193     /// indicates if a trap happened.
1194     ///
1195     /// This helper is used when the host returns back to WebAssembly. The host
1196     /// returns a `bool` indicating whether the call succeeded. If the call
1197     /// failed then Cranelift needs to unwind back to the original invocation
1198     /// point. The unwind right now is then implemented in Wasmtime with an
1199     /// exceptional resume, one day this might be implemented differently with
1200     /// an unwind inside of Cranelift.
1201     ///
1202     /// Additionally in the future for pulley this will emit a special trap
1203     /// opcode for Pulley itself to cease interpretation and exit the
1204     /// interpreter.
raise_if_host_trapped( &self, builder: &mut FunctionBuilder<'_>, vmctx: ir::Value, succeeded: ir::Value, )1205     pub fn raise_if_host_trapped(
1206         &self,
1207         builder: &mut FunctionBuilder<'_>,
1208         vmctx: ir::Value,
1209         succeeded: ir::Value,
1210     ) {
1211         let trapped_block = builder.create_block();
1212         let continuation_block = builder.create_block();
1213         builder.set_cold_block(trapped_block);
1214         builder
1215             .ins()
1216             .brif(succeeded, continuation_block, &[], trapped_block, &[]);
1217 
1218         builder.seal_block(trapped_block);
1219         builder.seal_block(continuation_block);
1220 
1221         builder.switch_to_block(trapped_block);
1222         let sigs = BuiltinFunctionSignatures::new(self);
1223         let sig = sigs.host_signature(BuiltinFunctionIndex::raise());
1224         self.call_builtin(builder, vmctx, &[vmctx], BuiltinFunctionIndex::raise(), sig);
1225         builder.ins().trap(TRAP_INTERNAL_ASSERT);
1226 
1227         builder.switch_to_block(continuation_block);
1228     }
1229 
1230     /// Helper to load the core `builtin` from `vmctx` and invoke it with
1231     /// `args`.
call_builtin( &self, builder: &mut FunctionBuilder<'_>, vmctx: ir::Value, args: &[ir::Value], builtin: BuiltinFunctionIndex, sig: ir::Signature, ) -> ir::Inst1232     fn call_builtin(
1233         &self,
1234         builder: &mut FunctionBuilder<'_>,
1235         vmctx: ir::Value,
1236         args: &[ir::Value],
1237         builtin: BuiltinFunctionIndex,
1238         sig: ir::Signature,
1239     ) -> ir::Inst {
1240         let isa = &*self.isa;
1241         let ptr_size = isa.pointer_bytes();
1242         let pointer_type = isa.pointer_type();
1243 
1244         // Builtins are stored in an array in all `VMContext`s. First load the
1245         // base pointer of the array and then load the entry of the array that
1246         // corresponds to this builtin.
1247         let mem_flags = ir::MemFlags::trusted().with_readonly();
1248         let array_addr = builder.ins().load(
1249             pointer_type,
1250             mem_flags,
1251             vmctx,
1252             i32::from(ptr_size.vmcontext_builtin_functions()),
1253         );
1254         let body_offset = i32::try_from(builtin.index() * pointer_type.bytes()).unwrap();
1255         let func_addr = builder
1256             .ins()
1257             .load(pointer_type, mem_flags, array_addr, body_offset);
1258 
1259         let sig = builder.func.import_signature(sig);
1260         self.call_indirect_host(builder, builtin, sig, func_addr, args)
1261     }
1262 
isa(&self) -> &dyn TargetIsa1263     pub fn isa(&self) -> &dyn TargetIsa {
1264         &*self.isa
1265     }
1266 
tunables(&self) -> &Tunables1267     pub fn tunables(&self) -> &Tunables {
1268         &self.tunables
1269     }
1270 
debug_assert_enough_capacity_for_length( &self, builder: &mut FunctionBuilder, length: usize, capacity: ir::Value, )1271     fn debug_assert_enough_capacity_for_length(
1272         &self,
1273         builder: &mut FunctionBuilder,
1274         length: usize,
1275         capacity: ir::Value,
1276     ) {
1277         if !self.emit_debug_checks {
1278             return;
1279         }
1280         let enough_capacity = builder.ins().icmp_imm(
1281             ir::condcodes::IntCC::UnsignedGreaterThanOrEqual,
1282             capacity,
1283             ir::immediates::Imm64::new(length.try_into().unwrap()),
1284         );
1285         builder.ins().trapz(enough_capacity, TRAP_INTERNAL_ASSERT);
1286     }
1287 
debug_assert_vmctx_kind( &self, builder: &mut FunctionBuilder, vmctx: ir::Value, expected_vmctx_magic: u32, )1288     fn debug_assert_vmctx_kind(
1289         &self,
1290         builder: &mut FunctionBuilder,
1291         vmctx: ir::Value,
1292         expected_vmctx_magic: u32,
1293     ) {
1294         if !self.emit_debug_checks {
1295             return;
1296         }
1297         let magic = builder.ins().load(
1298             ir::types::I32,
1299             MemFlags::trusted().with_endianness(self.isa.endianness()),
1300             vmctx,
1301             0,
1302         );
1303         let is_expected_vmctx = builder.ins().icmp_imm(
1304             ir::condcodes::IntCC::Equal,
1305             magic,
1306             i64::from(expected_vmctx_magic),
1307         );
1308         builder.ins().trapz(is_expected_vmctx, TRAP_INTERNAL_ASSERT);
1309     }
1310 
array_to_wasm_trampoline( &self, trampoline_key: FuncKey, callee_key: FuncKey, callee_sig: &WasmFuncType, symbol: &str, vm_store_context_offset: u32, expected_vmctx_magic: u32, ) -> Result<CompiledFunctionBody, CompileError>1311     fn array_to_wasm_trampoline(
1312         &self,
1313         trampoline_key: FuncKey,
1314         callee_key: FuncKey,
1315         callee_sig: &WasmFuncType,
1316         symbol: &str,
1317         vm_store_context_offset: u32,
1318         expected_vmctx_magic: u32,
1319     ) -> Result<CompiledFunctionBody, CompileError> {
1320         log::trace!("compiling array-to-wasm trampoline: {trampoline_key:?} = {symbol:?}");
1321 
1322         let isa = &*self.isa;
1323         let pointer_type = isa.pointer_type();
1324         let wasm_call_sig = wasm_call_signature(isa, callee_sig, &self.tunables);
1325         let array_call_sig = array_call_signature(isa);
1326 
1327         let mut compiler = self.function_compiler();
1328         let func = ir::Function::with_name_signature(key_to_name(trampoline_key), array_call_sig);
1329         let (mut builder, block0) = compiler.builder(func);
1330 
1331         let try_call_block = builder.create_block();
1332         builder.ins().jump(try_call_block, []);
1333         builder.switch_to_block(try_call_block);
1334 
1335         let (vmctx, caller_vmctx, values_vec_ptr, values_vec_len) = {
1336             let params = builder.func.dfg.block_params(block0);
1337             (params[0], params[1], params[2], params[3])
1338         };
1339 
1340         // First load the actual arguments out of the array.
1341         let mut args = self.load_values_from_array(
1342             callee_sig.params(),
1343             &mut builder,
1344             values_vec_ptr,
1345             values_vec_len,
1346         );
1347         args.insert(0, caller_vmctx);
1348         args.insert(0, vmctx);
1349 
1350         // Just before we enter Wasm, save our context information.
1351         //
1352         // Assert that we were really given a core Wasm vmctx, since that's
1353         // what we are assuming with our offsets below.
1354         self.debug_assert_vmctx_kind(&mut builder, vmctx, expected_vmctx_magic);
1355         save_last_wasm_entry_context(
1356             &mut builder,
1357             pointer_type,
1358             &self.isa.pointer_bytes(),
1359             vm_store_context_offset,
1360             vmctx,
1361             try_call_block,
1362         );
1363 
1364         // Create the invocation of wasm, which is notably done with a
1365         // `try_call` with an exception handler that's used to handle traps.
1366         let normal_return = builder.create_block();
1367         let exceptional_return = builder.create_block();
1368         let normal_return_values = wasm_call_sig
1369             .returns
1370             .iter()
1371             .map(|ty| {
1372                 builder
1373                     .func
1374                     .dfg
1375                     .append_block_param(normal_return, ty.value_type)
1376             })
1377             .collect::<Vec<_>>();
1378 
1379         // Then call the Wasm function with those arguments.
1380         let signature = builder.func.import_signature(wasm_call_sig.clone());
1381         let callee = {
1382             let (namespace, index) = callee_key.into_raw_parts();
1383             let name = ir::ExternalName::User(
1384                 builder
1385                     .func
1386                     .declare_imported_user_function(ir::UserExternalName { namespace, index }),
1387             );
1388             builder.func.dfg.ext_funcs.push(ir::ExtFuncData {
1389                 name,
1390                 signature,
1391                 colocated: true,
1392                 patchable: false,
1393             })
1394         };
1395 
1396         let dfg = &mut builder.func.dfg;
1397         let exception_table = dfg.exception_tables.push(ir::ExceptionTableData::new(
1398             signature,
1399             ir::BlockCall::new(
1400                 normal_return,
1401                 (0..wasm_call_sig.returns.len())
1402                     .map(|i| ir::BlockArg::TryCallRet(i.try_into().unwrap())),
1403                 &mut dfg.value_lists,
1404             ),
1405             [ir::ExceptionTableItem::Default(ir::BlockCall::new(
1406                 exceptional_return,
1407                 None,
1408                 &mut dfg.value_lists,
1409             ))],
1410         ));
1411         builder.ins().try_call(callee, &args, exception_table);
1412 
1413         builder.seal_block(try_call_block);
1414         builder.seal_block(normal_return);
1415         builder.seal_block(exceptional_return);
1416 
1417         // On the normal return path store all the results in the array we were
1418         // provided and return "true" for "returned successfully".
1419         builder.switch_to_block(normal_return);
1420         self.store_values_to_array(
1421             &mut builder,
1422             callee_sig.results(),
1423             &normal_return_values,
1424             values_vec_ptr,
1425             values_vec_len,
1426         );
1427         let true_return = builder.ins().iconst(ir::types::I8, 1);
1428         builder.ins().return_(&[true_return]);
1429 
1430         // On the exceptional return path just return "false" for "did not
1431         // succeed". Note that register restoration is part of the `try_call`
1432         // and handler implementation.
1433         builder.switch_to_block(exceptional_return);
1434         let false_return = builder.ins().iconst(ir::types::I8, 0);
1435         builder.ins().return_(&[false_return]);
1436 
1437         builder.finalize();
1438 
1439         Ok(CompiledFunctionBody {
1440             code: box_dyn_any_compiler_context(Some(compiler.cx)),
1441             needs_gc_heap: false,
1442         })
1443     }
1444 }
1445 
1446 struct FunctionCompiler<'a> {
1447     compiler: &'a Compiler,
1448     cx: CompilerContext,
1449 }
1450 
1451 impl FunctionCompiler<'_> {
builder(&mut self, func: ir::Function) -> (FunctionBuilder<'_>, ir::Block)1452     fn builder(&mut self, func: ir::Function) -> (FunctionBuilder<'_>, ir::Block) {
1453         self.cx.codegen_context.func = func;
1454         let mut builder = FunctionBuilder::new(
1455             &mut self.cx.codegen_context.func,
1456             self.cx.func_translator.context(),
1457         );
1458 
1459         let block0 = builder.create_block();
1460         builder.append_block_params_for_function_params(block0);
1461         builder.switch_to_block(block0);
1462         builder.ensure_inserted_block();
1463         builder.seal_block(block0);
1464         (builder, block0)
1465     }
1466 
finish(self, symbol: &str) -> Result<CompiledFunction, CompileError>1467     fn finish(self, symbol: &str) -> Result<CompiledFunction, CompileError> {
1468         self.finish_with_info(None, symbol)
1469     }
1470 
finish_with_info( mut self, body_and_tunables: Option<(&FunctionBody<'_>, &Tunables)>, symbol: &str, ) -> Result<CompiledFunction, CompileError>1471     fn finish_with_info(
1472         mut self,
1473         body_and_tunables: Option<(&FunctionBody<'_>, &Tunables)>,
1474         symbol: &str,
1475     ) -> Result<CompiledFunction, CompileError> {
1476         let context = &mut self.cx.codegen_context;
1477         let isa = &*self.compiler.isa;
1478 
1479         // Run compilation, but don't propagate the error just yet. This'll
1480         // mutate `context` and the IR contained within (optionally) but it may
1481         // fail if the backend has a bug in it. Use `context` after this
1482         // finishes to optionally emit CLIF and then after that's done actually
1483         // propagate the error if one happened.
1484         let compilation_result =
1485             compile_maybe_cached(context, isa, self.cx.incremental_cache_ctx.as_mut());
1486 
1487         if let Some(path) = &self.compiler.clif_dir {
1488             use std::io::Write;
1489 
1490             let mut path = path.join(symbol.replace(":", "-"));
1491             path.set_extension("clif");
1492 
1493             let mut output = std::fs::File::create(path).unwrap();
1494             write!(
1495                 output,
1496                 ";; Intermediate Representation of function <{symbol}>:\n",
1497             )
1498             .unwrap();
1499             write!(output, "{}", context.func.display()).unwrap();
1500         }
1501 
1502         let compiled_code = compilation_result?;
1503 
1504         // Give wasm functions, user defined code, a "preferred" alignment
1505         // instead of the minimum alignment as this can help perf in niche
1506         // situations.
1507         let preferred_alignment = if body_and_tunables.is_some() {
1508             self.compiler.isa.function_alignment().preferred
1509         } else {
1510             1
1511         };
1512 
1513         let alignment = compiled_code.buffer.alignment.max(preferred_alignment);
1514         let mut compiled_function = CompiledFunction::new(
1515             compiled_code.buffer.clone(),
1516             context.func.params.user_named_funcs().clone(),
1517             alignment,
1518         );
1519 
1520         if let Some((body, tunables)) = body_and_tunables {
1521             let data = body.get_binary_reader();
1522             let offset = data.original_position();
1523             let len = data.bytes_remaining();
1524             compiled_function.set_address_map(
1525                 offset.try_into().unwrap(),
1526                 len.try_into().unwrap(),
1527                 tunables.generate_address_map,
1528             );
1529         }
1530 
1531         if isa.flags().unwind_info() {
1532             let unwind = compiled_code
1533                 .create_unwind_info(isa)
1534                 .map_err(|error| CompileError::Codegen(pretty_error(&context.func, error)))?;
1535 
1536             if let Some(unwind_info) = unwind {
1537                 compiled_function.set_unwind_info(unwind_info);
1538             }
1539         }
1540 
1541         if let Some(builder) = self.cx.debug_slot_descriptor.take() {
1542             compiled_function.debug_slot_descriptor = Some(builder);
1543         }
1544 
1545         if body_and_tunables
1546             .map(|(_, t)| t.debug_native)
1547             .unwrap_or(false)
1548         {
1549             compiled_function.set_value_labels_ranges(compiled_code.value_labels_ranges.clone());
1550 
1551             // DWARF debugging needs the CFA-based unwind information even on Windows.
1552             if !matches!(
1553                 compiled_function.metadata().unwind_info,
1554                 Some(UnwindInfo::SystemV(_))
1555             ) {
1556                 let cfa_unwind = compiled_code
1557                     .create_unwind_info_of_kind(isa, UnwindInfoKind::SystemV)
1558                     .map_err(|error| CompileError::Codegen(pretty_error(&context.func, error)))?;
1559 
1560                 if let Some(UnwindInfo::SystemV(cfa_unwind_info)) = cfa_unwind {
1561                     compiled_function.set_cfa_unwind_info(cfa_unwind_info);
1562                 }
1563             }
1564         }
1565 
1566         self.compiler.contexts.lock().unwrap().push(self.cx);
1567 
1568         Ok(compiled_function)
1569     }
1570 }
1571 
1572 /// Convert from Cranelift's representation of a stack map to Wasmtime's
1573 /// compiler-agnostic representation.
1574 ///
1575 /// Here `section` is the wasmtime data section being created and `range` is the
1576 /// range of the function being added. The `clif_stack_maps` entry is the raw
1577 /// listing of stack maps from Cranelift.
clif_to_env_stack_maps( section: &mut StackMapSection, range: Range<u64>, clif_stack_maps: &[(CodeOffset, u32, ir::UserStackMap)], )1578 fn clif_to_env_stack_maps(
1579     section: &mut StackMapSection,
1580     range: Range<u64>,
1581     clif_stack_maps: &[(CodeOffset, u32, ir::UserStackMap)],
1582 ) {
1583     for (offset, frame_size, stack_map) in clif_stack_maps {
1584         let mut frame_offsets = Vec::new();
1585         for (ty, frame_offset) in stack_map.entries() {
1586             assert_eq!(ty, ir::types::I32);
1587             frame_offsets.push(frame_offset);
1588         }
1589         let code_offset = range.start + u64::from(*offset);
1590         assert!(code_offset < range.end);
1591         section.push(code_offset, *frame_size, frame_offsets.into_iter());
1592     }
1593 }
1594 
1595 /// Convert from Cranelift's representation of exception handler
1596 /// metadata to Wasmtime's compiler-agnostic representation.
1597 ///
1598 /// Here `builder` is the wasmtime-unwinder exception section being
1599 /// created and `range` is the range of the function being added. The
1600 /// `call_sites` iterator is the raw iterator over callsite metadata
1601 /// (including exception handlers) from Cranelift.
clif_to_env_exception_tables<'a>( builder: &mut ExceptionTableBuilder, range: Range<u64>, call_sites: impl Iterator<Item = FinalizedMachCallSite<'a>>, ) -> Result<()>1602 fn clif_to_env_exception_tables<'a>(
1603     builder: &mut ExceptionTableBuilder,
1604     range: Range<u64>,
1605     call_sites: impl Iterator<Item = FinalizedMachCallSite<'a>>,
1606 ) -> Result<()> {
1607     builder.add_func(CodeOffset::try_from(range.start).unwrap(), call_sites)
1608 }
1609 
1610 /// Convert from Cranelift's representation of frame state slots and
1611 /// debug tags to Wasmtime's serialized metadata.
clif_to_env_frame_tables<'a>( builder: &mut FrameTableBuilder, range: Range<u64>, tag_sites: impl Iterator<Item = MachBufferDebugTagList<'a>>, frame_layout: &MachBufferFrameLayout, frame_descriptors: &HashMap<FuncKey, Vec<u8>>, ) -> Result<()>1612 fn clif_to_env_frame_tables<'a>(
1613     builder: &mut FrameTableBuilder,
1614     range: Range<u64>,
1615     tag_sites: impl Iterator<Item = MachBufferDebugTagList<'a>>,
1616     frame_layout: &MachBufferFrameLayout,
1617     frame_descriptors: &HashMap<FuncKey, Vec<u8>>,
1618 ) -> Result<()> {
1619     let mut frame_descriptor_indices = HashMap::new();
1620     for tag_site in tag_sites {
1621         // Split into frames; each has three debug tags.
1622         let mut frames = vec![];
1623         for frame_tags in tag_site.tags.chunks_exact(3) {
1624             let &[
1625                 ir::DebugTag::StackSlot(slot),
1626                 ir::DebugTag::User(wasm_pc_raw),
1627                 ir::DebugTag::User(stack_shape),
1628             ] = frame_tags
1629             else {
1630                 panic!("Invalid tags");
1631             };
1632 
1633             let func_key = frame_layout.stackslots[slot]
1634                 .key
1635                 .expect("Key must be present on stackslot used as state slot")
1636                 .bits();
1637             let func_key = FuncKey::from_raw_u64(func_key);
1638             let frame_descriptor = *frame_descriptor_indices.entry(slot).or_insert_with(|| {
1639                 let slot_to_fp_offset =
1640                     frame_layout.frame_to_fp_offset - frame_layout.stackslots[slot].offset;
1641                 let descriptor = frame_descriptors
1642                     .get(&func_key)
1643                     .expect("frame descriptor not present for FuncKey");
1644                 builder.add_frame_descriptor(slot_to_fp_offset, &descriptor)
1645             });
1646 
1647             frames.push((
1648                 ModulePC::new(wasm_pc_raw),
1649                 frame_descriptor,
1650                 FrameStackShape::from_raw(stack_shape),
1651             ));
1652         }
1653 
1654         let native_pc_in_code_section = u32::try_from(range.start)
1655             .unwrap()
1656             .checked_add(tag_site.offset)
1657             .unwrap();
1658         let pos = match tag_site.pos {
1659             MachDebugTagPos::Post => FrameInstPos::Post,
1660             MachDebugTagPos::Pre => FrameInstPos::Pre,
1661         };
1662         builder.add_program_point(native_pc_in_code_section, pos, &frames);
1663     }
1664 
1665     Ok(())
1666 }
1667 
1668 /// Convert from Cranelift's representation of breakpoint patches to
1669 /// Wasmtime's serialized metadata.
clif_to_env_breakpoints( range: Range<u64>, breakpoint_patches: impl Iterator<Item = (ModulePC, Range<u32>)>, patch_table: &mut Vec<(ModulePC, Range<u32>)>, ) -> Result<()>1670 fn clif_to_env_breakpoints(
1671     range: Range<u64>,
1672     breakpoint_patches: impl Iterator<Item = (ModulePC, Range<u32>)>,
1673     patch_table: &mut Vec<(ModulePC, Range<u32>)>,
1674 ) -> Result<()> {
1675     patch_table.extend(breakpoint_patches.map(|(wasm_pc, offset_range)| {
1676         let start = offset_range.start + u32::try_from(range.start).unwrap();
1677         let end = offset_range.end + u32::try_from(range.start).unwrap();
1678         (wasm_pc, start..end)
1679     }));
1680     Ok(())
1681 }
1682 
save_last_wasm_entry_context( builder: &mut FunctionBuilder, pointer_type: ir::Type, ptr_size: &dyn PtrSize, vm_store_context_offset: u32, vmctx: Value, block: ir::Block, )1683 fn save_last_wasm_entry_context(
1684     builder: &mut FunctionBuilder,
1685     pointer_type: ir::Type,
1686     ptr_size: &dyn PtrSize,
1687     vm_store_context_offset: u32,
1688     vmctx: Value,
1689     block: ir::Block,
1690 ) {
1691     // First we need to get the `VMStoreContext`.
1692     let vm_store_context = builder.ins().load(
1693         pointer_type,
1694         MemFlags::trusted(),
1695         vmctx,
1696         i32::try_from(vm_store_context_offset).unwrap(),
1697     );
1698 
1699     // Save the current fp/sp of the entry trampoline into the `VMStoreContext`.
1700     let fp = builder.ins().get_frame_pointer(pointer_type);
1701     builder.ins().store(
1702         MemFlags::trusted(),
1703         fp,
1704         vm_store_context,
1705         ptr_size.vmstore_context_last_wasm_entry_fp(),
1706     );
1707     let sp = builder.ins().get_stack_pointer(pointer_type);
1708     builder.ins().store(
1709         MemFlags::trusted(),
1710         sp,
1711         vm_store_context,
1712         ptr_size.vmstore_context_last_wasm_entry_sp(),
1713     );
1714 
1715     // Also save the address of this function's exception handler. This is used
1716     // as a resumption point for traps, for example.
1717     let trap_handler = builder
1718         .ins()
1719         .get_exception_handler_address(pointer_type, block, 0);
1720     builder.ins().store(
1721         MemFlags::trusted(),
1722         trap_handler,
1723         vm_store_context,
1724         ptr_size.vmstore_context_last_wasm_entry_trap_handler(),
1725     );
1726 }
1727 
save_last_wasm_exit_fp_and_pc( builder: &mut FunctionBuilder, pointer_type: ir::Type, ptr: &impl PtrSize, limits: Value, )1728 fn save_last_wasm_exit_fp_and_pc(
1729     builder: &mut FunctionBuilder,
1730     pointer_type: ir::Type,
1731     ptr: &impl PtrSize,
1732     limits: Value,
1733 ) {
1734     // Save the trampoline FP to the limits. Exception unwind needs
1735     // this so that it can know the SP (bottom of frame) for the very
1736     // last Wasm frame.
1737     let trampoline_fp = builder.ins().get_frame_pointer(pointer_type);
1738     builder.ins().store(
1739         MemFlags::trusted(),
1740         trampoline_fp,
1741         limits,
1742         ptr.vmstore_context_last_wasm_exit_trampoline_fp(),
1743     );
1744 
1745     // Finally save the Wasm return address to the limits.
1746     let wasm_pc = builder.ins().get_return_address(pointer_type);
1747     builder.ins().store(
1748         MemFlags::trusted(),
1749         wasm_pc,
1750         limits,
1751         ptr.vmstore_context_last_wasm_exit_pc(),
1752     );
1753 }
1754 
key_to_name(key: FuncKey) -> ir::UserFuncName1755 fn key_to_name(key: FuncKey) -> ir::UserFuncName {
1756     let (namespace, index) = key.into_raw_parts();
1757     ir::UserFuncName::User(ir::UserExternalName { namespace, index })
1758 }
1759