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