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