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 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 { 65 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 { 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 { 120 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. 151 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 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 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 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 { 225 fn inlining_compiler(&self) -> Option<&dyn wasmtime_environ::InliningCompiler> { 226 Some(self) 227 } 228 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 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 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.returns(), &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 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::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 637 fn triple(&self) -> &target_lexicon::Triple { 638 self.isa.triple() 639 } 640 641 fn flags(&self) -> Vec<(&'static str, FlagValue<'static>)> { 642 crate::clif_flags_to_wasmtime(self.isa.flags().iter()) 643 } 644 645 fn isa_flags(&self) -> Vec<(&'static str, FlagValue<'static>)> { 646 crate::clif_flags_to_wasmtime(self.isa.isa_flags()) 647 } 648 649 fn is_branch_protection_enabled(&self) -> bool { 650 self.isa.is_branch_protection_enabled() 651 } 652 653 #[cfg(feature = "component-model")] 654 fn component_compiler(&self) -> &dyn wasmtime_environ::component::ComponentCompiler { 655 self 656 } 657 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 733 fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> { 734 self.isa.create_systemv_cie() 735 } 736 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 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 { 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 871 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 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 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 { 995 fn get(&self, key: &[u8]) -> Option<std::borrow::Cow<'_, [u8]>> { 996 self.0.get(key) 997 } 998 fn insert(&mut self, key: &[u8], val: Vec<u8>) { 999 self.0.insert(key, val); 1000 } 1001 } 1002 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"))] 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 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. 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.returns().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. 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. 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 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. 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`. 1232 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 1263 pub fn isa(&self) -> &dyn TargetIsa { 1264 &*self.isa 1265 } 1266 1267 pub fn tunables(&self) -> &Tunables { 1268 &self.tunables 1269 } 1270 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 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 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.returns(), 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<'_> { 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 1467 fn finish(self, symbol: &str) -> Result<CompiledFunction, CompileError> { 1468 self.finish_with_info(None, symbol) 1469 } 1470 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. 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. 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. 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), 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 wasm_pc, 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. 1670 fn clif_to_env_breakpoints( 1671 range: Range<u64>, 1672 breakpoint_patches: impl Iterator<Item = (u32, Range<u32>)>, 1673 patch_table: &mut Vec<(u32, 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 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 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 1755 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