Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/environ/src/compile/mod.rs
1693 views
1
//! A `Compilation` contains the compiled function bodies for a WebAssembly
2
//! module.
3
4
use crate::prelude::*;
5
use crate::{
6
DefinedFuncIndex, FlagValue, FunctionLoc, ObjectKind, PrimaryMap, StaticModuleIndex, TripleExt,
7
WasmError, WasmFuncType,
8
};
9
use crate::{Tunables, obj};
10
use anyhow::Result;
11
use object::write::{Object, SymbolId};
12
use object::{Architecture, BinaryFormat, FileFlags};
13
use std::any::Any;
14
use std::borrow::Cow;
15
use std::fmt;
16
use std::path;
17
use std::sync::Arc;
18
19
mod address_map;
20
mod key;
21
mod module_artifacts;
22
mod module_environ;
23
mod module_types;
24
mod stack_maps;
25
mod trap_encoding;
26
27
pub use self::address_map::*;
28
pub use self::key::*;
29
pub use self::module_artifacts::*;
30
pub use self::module_environ::*;
31
pub use self::module_types::*;
32
pub use self::stack_maps::*;
33
pub use self::trap_encoding::*;
34
35
/// An error while compiling WebAssembly to machine code.
36
#[derive(Debug)]
37
pub enum CompileError {
38
/// A wasm translation error occurred.
39
Wasm(WasmError),
40
41
/// A compilation error occurred.
42
Codegen(String),
43
44
/// A compilation error occurred.
45
DebugInfoNotSupported,
46
}
47
48
impl fmt::Display for CompileError {
49
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50
match self {
51
CompileError::Wasm(_) => write!(f, "WebAssembly translation error"),
52
CompileError::Codegen(s) => write!(f, "Compilation error: {s}"),
53
CompileError::DebugInfoNotSupported => {
54
write!(f, "Debug info is not supported with this configuration")
55
}
56
}
57
}
58
}
59
60
impl From<WasmError> for CompileError {
61
fn from(err: WasmError) -> CompileError {
62
CompileError::Wasm(err)
63
}
64
}
65
66
impl core::error::Error for CompileError {
67
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
68
match self {
69
CompileError::Wasm(e) => Some(e),
70
_ => None,
71
}
72
}
73
}
74
75
/// Implementation of an incremental compilation's key/value cache store.
76
///
77
/// In theory, this could just be Cranelift's `CacheKvStore` trait, but it is not as we want to
78
/// make sure that wasmtime isn't too tied to Cranelift internals (and as a matter of fact, we
79
/// can't depend on the Cranelift trait here).
80
pub trait CacheStore: Send + Sync + std::fmt::Debug {
81
/// Try to retrieve an arbitrary cache key entry, and returns a reference to bytes that were
82
/// inserted via `Self::insert` before.
83
fn get(&self, key: &[u8]) -> Option<Cow<'_, [u8]>>;
84
85
/// Given an arbitrary key and bytes, stores them in the cache.
86
///
87
/// Returns false when insertion in the cache failed.
88
fn insert(&self, key: &[u8], value: Vec<u8>) -> bool;
89
}
90
91
/// Abstract trait representing the ability to create a `Compiler` below.
92
///
93
/// This is used in Wasmtime to separate compiler implementations, currently
94
/// mostly used to separate Cranelift from Wasmtime itself.
95
pub trait CompilerBuilder: Send + Sync + fmt::Debug {
96
/// Sets the target of compilation to the target specified.
97
fn target(&mut self, target: target_lexicon::Triple) -> Result<()>;
98
99
/// Enables clif output in the directory specified.
100
fn clif_dir(&mut self, _path: &path::Path) -> Result<()> {
101
anyhow::bail!("clif output not supported");
102
}
103
104
/// Returns the currently configured target triple that compilation will
105
/// produce artifacts for.
106
fn triple(&self) -> &target_lexicon::Triple;
107
108
/// Compiler-specific method to configure various settings in the compiler
109
/// itself.
110
///
111
/// This is expected to be defined per-compiler. Compilers should return
112
/// errors for unknown names/values.
113
fn set(&mut self, name: &str, val: &str) -> Result<()>;
114
115
/// Compiler-specific method for configuring settings.
116
///
117
/// Same as [`CompilerBuilder::set`] except for enabling boolean flags.
118
/// Currently cranelift uses this to sometimes enable a family of settings.
119
fn enable(&mut self, name: &str) -> Result<()>;
120
121
/// Returns a list of all possible settings that can be configured with
122
/// [`CompilerBuilder::set`] and [`CompilerBuilder::enable`].
123
fn settings(&self) -> Vec<Setting>;
124
125
/// Enables Cranelift's incremental compilation cache, using the given `CacheStore`
126
/// implementation.
127
///
128
/// This will return an error if the compiler does not support incremental compilation.
129
fn enable_incremental_compilation(&mut self, cache_store: Arc<dyn CacheStore>) -> Result<()>;
130
131
/// Set the tunables for this compiler.
132
fn set_tunables(&mut self, tunables: Tunables) -> Result<()>;
133
134
/// Builds a new [`Compiler`] object from this configuration.
135
fn build(&self) -> Result<Box<dyn Compiler>>;
136
137
/// Enables or disables wmemcheck during runtime according to the wmemcheck CLI flag.
138
fn wmemcheck(&mut self, _enable: bool) {}
139
}
140
141
/// Description of compiler settings returned by [`CompilerBuilder::settings`].
142
#[derive(Clone, Copy, Debug)]
143
pub struct Setting {
144
/// The name of the setting.
145
pub name: &'static str,
146
/// The description of the setting.
147
pub description: &'static str,
148
/// The kind of the setting.
149
pub kind: SettingKind,
150
/// The supported values of the setting (for enum values).
151
pub values: Option<&'static [&'static str]>,
152
}
153
154
/// Different kinds of [`Setting`] values that can be configured in a
155
/// [`CompilerBuilder`]
156
#[derive(Clone, Copy, Debug)]
157
pub enum SettingKind {
158
/// The setting is an enumeration, meaning it's one of a set of values.
159
Enum,
160
/// The setting is a number.
161
Num,
162
/// The setting is a boolean.
163
Bool,
164
/// The setting is a preset.
165
Preset,
166
}
167
168
/// The result of compiling a single function body.
169
pub struct CompiledFunctionBody {
170
/// The code. This is whatever type the `Compiler` implementation wants it
171
/// to be, we just shepherd it around.
172
pub code: Box<dyn Any + Send + Sync>,
173
/// Whether the compiled function needs a GC heap to run; that is, whether
174
/// it reads a struct field, allocates, an array, or etc...
175
pub needs_gc_heap: bool,
176
}
177
178
/// An implementation of a compiler which can compile WebAssembly functions to
179
/// machine code and perform other miscellaneous tasks needed by the JIT runtime.
180
///
181
/// The diagram below depicts typical usage of this trait:
182
///
183
/// ```text
184
/// +------+
185
/// | Wasm |
186
/// +------+
187
/// |
188
/// |
189
/// Compiler::compile_function()
190
/// |
191
/// |
192
/// V
193
/// +----------------------+
194
/// | CompiledFunctionBody |
195
/// +----------------------+
196
/// | |
197
/// | |
198
/// | When
199
/// | Compiler::inlining_compiler()
200
/// | is some
201
/// | |
202
/// When |
203
/// Compiler::inlining_compiler() |-----------------.
204
/// is none | |
205
/// | | |
206
/// | Optionally call |
207
/// | InliningCompiler::inline() |
208
/// | | |
209
/// | | |
210
/// | |-----------------'
211
/// | |
212
/// | |
213
/// | V
214
/// | InliningCompiler::finish_compiling()
215
/// | |
216
/// | |
217
/// |------------------'
218
/// |
219
/// |
220
/// Compiler::append_code()
221
/// |
222
/// |
223
/// V
224
/// +--------+
225
/// | Object |
226
/// +--------+
227
/// ```
228
pub trait Compiler: Send + Sync {
229
/// Get this compiler's inliner.
230
///
231
/// Consumers of this trait **must** check for when when this method returns
232
/// `Some(_)`, and **must** call `InliningCompiler::finish_compiling` on all
233
/// `CompiledFunctionBody`s produced by this compiler in that case before
234
/// passing the the compiled functions to `Compiler::append_code`, even if
235
/// the consumer does not actually intend to do any inlining. This allows
236
/// implementations of the trait to only translate to an internal
237
/// representation in `Compiler::compile_*` methods so that they can then
238
/// perform inlining afterwards if the consumer desires, and then finally
239
/// proceed with compilng that internal representation to native code in
240
/// `InliningCompiler::finish_compiling`.
241
fn inlining_compiler(&self) -> Option<&dyn InliningCompiler>;
242
243
/// Compiles the function `index` within `translation`.
244
///
245
/// The body of the function is available in `data` and configuration
246
/// values are also passed in via `tunables`. Type information in
247
/// `translation` is all relative to `types`.
248
fn compile_function(
249
&self,
250
translation: &ModuleTranslation<'_>,
251
key: FuncKey,
252
data: FunctionBodyData<'_>,
253
types: &ModuleTypesBuilder,
254
symbol: &str,
255
) -> Result<CompiledFunctionBody, CompileError>;
256
257
/// Compile a trampoline for an array-call host function caller calling the
258
/// `index`th Wasm function.
259
///
260
/// The trampoline should save the necessary state to record the
261
/// host-to-Wasm transition (e.g. registers used for fast stack walking).
262
fn compile_array_to_wasm_trampoline(
263
&self,
264
translation: &ModuleTranslation<'_>,
265
types: &ModuleTypesBuilder,
266
key: FuncKey,
267
symbol: &str,
268
) -> Result<CompiledFunctionBody, CompileError>;
269
270
/// Compile a trampoline for a Wasm caller calling a array callee with the
271
/// given signature.
272
///
273
/// The trampoline should save the necessary state to record the
274
/// Wasm-to-host transition (e.g. registers used for fast stack walking).
275
fn compile_wasm_to_array_trampoline(
276
&self,
277
wasm_func_ty: &WasmFuncType,
278
key: FuncKey,
279
symbol: &str,
280
) -> Result<CompiledFunctionBody, CompileError>;
281
282
/// Creates a trampoline that can be used to call Wasmtime's implementation
283
/// of the builtin function specified by `index`.
284
///
285
/// The trampoline created can technically have any ABI but currently has
286
/// the native ABI. This will then perform all the necessary duties of an
287
/// exit trampoline from wasm and then perform the actual dispatch to the
288
/// builtin function. Builtin functions in Wasmtime are stored in an array
289
/// in all `VMContext` pointers, so the call to the host is an indirect
290
/// call.
291
fn compile_wasm_to_builtin(
292
&self,
293
key: FuncKey,
294
symbol: &str,
295
) -> Result<CompiledFunctionBody, CompileError>;
296
297
/// Returns the list of relocations required for a function from one of the
298
/// previous `compile_*` functions above.
299
fn compiled_function_relocation_targets<'a>(
300
&'a self,
301
func: &'a dyn Any,
302
) -> Box<dyn Iterator<Item = FuncKey> + 'a>;
303
304
/// Appends a list of compiled functions to an in-memory object.
305
///
306
/// This function will receive the same `Box<dyn Any>` produced as part of
307
/// compilation from functions like `compile_function`,
308
/// `compile_host_to_wasm_trampoline`, and other component-related shims.
309
/// Internally this will take all of these functions and add information to
310
/// the object such as:
311
///
312
/// * Compiled code in a `.text` section
313
/// * Unwind information in Wasmtime-specific sections
314
/// * Relocations, if necessary, for the text section
315
///
316
/// Each function is accompanied with its desired symbol name and the return
317
/// value of this function is the symbol for each function as well as where
318
/// each function was placed within the object.
319
///
320
/// The `resolve_reloc` argument is intended to resolving relocations
321
/// between function, chiefly resolving intra-module calls within one core
322
/// wasm module. The closure here takes two arguments:
323
///
324
/// 1. First, the index within `funcs` that is being resolved,
325
///
326
/// 2. and next the `RelocationTarget` which is the relocation target to
327
/// resolve.
328
///
329
/// The return value is an index within `funcs` that the relocation points
330
/// to.
331
fn append_code(
332
&self,
333
obj: &mut Object<'static>,
334
funcs: &[(String, Box<dyn Any + Send + Sync>)],
335
resolve_reloc: &dyn Fn(usize, FuncKey) -> usize,
336
) -> Result<Vec<(SymbolId, FunctionLoc)>>;
337
338
/// Creates a new `Object` file which is used to build the results of a
339
/// compilation into.
340
///
341
/// The returned object file will have an appropriate
342
/// architecture/endianness for `self.triple()`, but at this time it is
343
/// always an ELF file, regardless of target platform.
344
fn object(&self, kind: ObjectKind) -> Result<Object<'static>> {
345
use target_lexicon::Architecture::*;
346
347
let triple = self.triple();
348
let (arch, flags) = match triple.architecture {
349
X86_32(_) => (Architecture::I386, 0),
350
X86_64 => (Architecture::X86_64, 0),
351
Arm(_) => (Architecture::Arm, 0),
352
Aarch64(_) => (Architecture::Aarch64, 0),
353
S390x => (Architecture::S390x, 0),
354
Riscv64(_) => (Architecture::Riscv64, 0),
355
// XXX: the `object` crate won't successfully build an object
356
// with relocations and such if it doesn't know the
357
// architecture, so just pretend we are riscv64. Yolo!
358
//
359
// Also note that we add some flags to `e_flags` in the object file
360
// to indicate that it's pulley, not actually riscv64. This is used
361
// by `wasmtime objdump` for example.
362
Pulley32 | Pulley32be => (Architecture::Riscv64, obj::EF_WASMTIME_PULLEY32),
363
Pulley64 | Pulley64be => (Architecture::Riscv64, obj::EF_WASMTIME_PULLEY64),
364
architecture => {
365
anyhow::bail!("target architecture {:?} is unsupported", architecture,);
366
}
367
};
368
let mut obj = Object::new(
369
BinaryFormat::Elf,
370
arch,
371
match triple.endianness().unwrap() {
372
target_lexicon::Endianness::Little => object::Endianness::Little,
373
target_lexicon::Endianness::Big => object::Endianness::Big,
374
},
375
);
376
obj.flags = FileFlags::Elf {
377
os_abi: obj::ELFOSABI_WASMTIME,
378
e_flags: flags
379
| match kind {
380
ObjectKind::Module => obj::EF_WASMTIME_MODULE,
381
ObjectKind::Component => obj::EF_WASMTIME_COMPONENT,
382
},
383
abi_version: 0,
384
};
385
Ok(obj)
386
}
387
388
/// Returns the target triple that this compiler is compiling for.
389
fn triple(&self) -> &target_lexicon::Triple;
390
391
/// Returns the alignment necessary to align values to the page size of the
392
/// compilation target. Note that this may be an upper-bound where the
393
/// alignment is larger than necessary for some platforms since it may
394
/// depend on the platform's runtime configuration.
395
fn page_size_align(&self) -> u64 {
396
// Conservatively assume the max-of-all-supported-hosts for pulley
397
// and round up to 64k.
398
if self.triple().is_pulley() {
399
return 0x10000;
400
}
401
402
use target_lexicon::*;
403
match (self.triple().operating_system, self.triple().architecture) {
404
(
405
OperatingSystem::MacOSX { .. }
406
| OperatingSystem::Darwin(_)
407
| OperatingSystem::IOS(_)
408
| OperatingSystem::TvOS(_),
409
Architecture::Aarch64(..),
410
) => 0x4000,
411
// 64 KB is the maximal page size (i.e. memory translation granule size)
412
// supported by the architecture and is used on some platforms.
413
(_, Architecture::Aarch64(..)) => 0x10000,
414
_ => 0x1000,
415
}
416
}
417
418
/// Returns a list of configured settings for this compiler.
419
fn flags(&self) -> Vec<(&'static str, FlagValue<'static>)>;
420
421
/// Same as [`Compiler::flags`], but ISA-specific (a cranelift-ism)
422
fn isa_flags(&self) -> Vec<(&'static str, FlagValue<'static>)>;
423
424
/// Get a flag indicating whether branch protection is enabled.
425
fn is_branch_protection_enabled(&self) -> bool;
426
427
/// Returns a suitable compiler usable for component-related compilations.
428
///
429
/// Note that the `ComponentCompiler` trait can also be implemented for
430
/// `Self` in which case this function would simply return `self`.
431
#[cfg(feature = "component-model")]
432
fn component_compiler(&self) -> &dyn crate::component::ComponentCompiler;
433
434
/// Appends generated DWARF sections to the `obj` specified.
435
///
436
/// The `translations` track all compiled functions and `get_func` can be
437
/// used to acquire the metadata for a particular function within a module.
438
fn append_dwarf<'a>(
439
&self,
440
obj: &mut Object<'_>,
441
translations: &'a PrimaryMap<StaticModuleIndex, ModuleTranslation<'a>>,
442
get_func: &'a dyn Fn(
443
StaticModuleIndex,
444
DefinedFuncIndex,
445
) -> (SymbolId, &'a (dyn Any + Send + Sync)),
446
dwarf_package_bytes: Option<&'a [u8]>,
447
tunables: &'a Tunables,
448
) -> Result<()>;
449
450
/// Creates a new System V Common Information Entry for the ISA.
451
///
452
/// Returns `None` if the ISA does not support System V unwind information.
453
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
454
// By default, an ISA cannot create a System V CIE.
455
None
456
}
457
}
458
459
/// An inlining compiler.
460
pub trait InliningCompiler: Sync + Send {
461
/// Enumerate the function calls that the given `func` makes.
462
fn calls(&self, func: &CompiledFunctionBody, calls: &mut IndexSet<FuncKey>) -> Result<()>;
463
464
/// Get the abstract size of the given function, for the purposes of
465
/// inlining heuristics.
466
fn size(&self, func: &CompiledFunctionBody) -> u32;
467
468
/// Process this function for inlining.
469
///
470
/// Implementations should call `get_callee` for each of their direct
471
/// function call sites and if `get_callee` returns `Some(_)`, they should
472
/// inline the given function body into that call site.
473
fn inline<'a>(
474
&self,
475
func: &mut CompiledFunctionBody,
476
get_callee: &'a mut dyn FnMut(FuncKey) -> Option<&'a CompiledFunctionBody>,
477
) -> Result<()>;
478
479
/// Finish compiling the given function.
480
///
481
/// This method **must** be called before passing the
482
/// `CompiledFunctionBody`'s contents to `Compiler::append_code`, even if no
483
/// inlining was performed.
484
fn finish_compiling(
485
&self,
486
func: &mut CompiledFunctionBody,
487
input: Option<wasmparser::FunctionBody<'_>>,
488
symbol: &str,
489
) -> Result<()>;
490
}
491
492