-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
Copy pathinfo.rs
486 lines (437 loc) · 21 KB
/
info.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
// General runtime type-information about a component.
//
// Compared to the `Module` structure for core wasm this type is pretty
// significantly different. The core wasm `Module` corresponds roughly 1-to-1
// with the structure of the wasm module itself, but instead a `Component` is
// more of a "compiled" representation where the original structure is thrown
// away in favor of a more optimized representation. The considerations for this
// are:
//
// * This representation of a `Component` avoids the need to create a
// `PrimaryMap` of some form for each of the index spaces within a component.
// This is less so an issue about allocations and moreso that this information
// generally just isn't needed any time after instantiation. Avoiding creating
// these altogether helps components be lighter weight at runtime and
// additionally accelerates instantiation.
//
// * Components can have arbitrary nesting and internally do instantiations via
// string-based matching. At instantiation-time, though, we want to do as few
// string-lookups in hash maps as much as we can since they're significantly
// slower than index-based lookups. Furthermore while the imports of a
// component are not statically known the rest of the structure of the
// component is statically known which enables the ability to track precisely
// what matches up where and do all the string lookups at compile time instead
// of instantiation time.
//
// * Finally by performing this sort of dataflow analysis we are capable of
// identifying what adapters need trampolines for compilation or fusion. For
// example this tracks when host functions are lowered which enables us to
// enumerate what trampolines are required to enter into a component.
// Additionally (eventually) this will track all of the "fused" adapter
// functions where a function from one component instance is lifted and then
// lowered into another component instance. Altogether this enables Wasmtime's
// AOT-compilation where the artifact from compilation is suitable for use in
// running the component without the support of a compiler at runtime.
//
// Note, however, that the current design of `Component` has fundamental
// limitations which it was not designed for. For example there is no feasible
// way to implement either importing or exporting a component itself from the
// root component. Currently we rely on the ability to have static knowledge of
// what's coming from the host which at this point can only be either functions
// or core wasm modules. Additionally one flat list of initializers for a
// component are produced instead of initializers-per-component which would
// otherwise be required to export a component from a component.
//
// For now this tradeoff is made as it aligns well with the intended use case
// for components in an embedding. This may need to be revisited though if the
// requirements of embeddings change over time.
use crate::component::*;
use crate::{EntityIndex, PrimaryMap, SignatureIndex};
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
/// Run-time-type-information about a `Component`, its structure, and how to
/// instantiate it.
///
/// This type is intended to mirror the `Module` type in this crate which
/// provides all the runtime information about the structure of a module and
/// how it works.
///
/// NB: Lots of the component model is not yet implemented in the runtime so
/// this is going to undergo a lot of churn.
#[derive(Default, Debug, Serialize, Deserialize)]
pub struct Component {
/// A list of typed values that this component imports.
///
/// Note that each name is given an `ImportIndex` here for the next map to
/// refer back to.
pub import_types: PrimaryMap<ImportIndex, (String, TypeDef)>,
/// A list of "flattened" imports that are used by this instance.
///
/// This import map represents extracting imports, as necessary, from the
/// general imported types by this component. The flattening here refers to
/// extracting items from instances. Currently the flat imports are either a
/// host function or a core wasm module.
///
/// For example if `ImportIndex(0)` pointed to an instance then this import
/// map represent extracting names from that map, for example extracting an
/// exported module or an exported function.
///
/// Each import item is keyed by a `RuntimeImportIndex` which is referred to
/// by types below whenever something refers to an import. The value for
/// each `RuntimeImportIndex` in this map is the `ImportIndex` for where
/// this items comes from (which can be associated with a name above in the
/// `import_types` array) as well as the list of export names if
/// `ImportIndex` refers to an instance. The export names array represents
/// recursively fetching names within an instance.
//
// TODO: this is probably a lot of `String` storage and may be something
// that needs optimization in the future. For example instead of lots of
// different `String` allocations this could instead be a pointer/length
// into one large string allocation for the entire component. Alternatively
// strings could otherwise be globally intern'd via some other mechanism to
// avoid `Linker`-specific intern-ing plus intern-ing here. Unsure what the
// best route is or whether such an optimization is even necessary here.
pub imports: PrimaryMap<RuntimeImportIndex, (ImportIndex, Vec<String>)>,
/// A list of this component's exports, indexed by either position or name.
pub exports: IndexMap<String, Export>,
/// Initializers that must be processed when instantiating this component.
///
/// This list of initializers does not correspond directly to the component
/// itself. The general goal with this is that the recursive nature of
/// components is "flattened" with an array like this which is a linear
/// sequence of instructions of how to instantiate a component. This will
/// have instantiations, for example, in addition to entries which
/// initialize `VMComponentContext` fields with previously instantiated
/// instances.
pub initializers: Vec<GlobalInitializer>,
/// The number of runtime instances (maximum `RuntimeInstanceIndex`) created
/// when instantiating this component.
pub num_runtime_instances: u32,
/// Same as `num_runtime_instances`, but for `RuntimeComponentInstanceIndex`
/// instead.
pub num_runtime_component_instances: u32,
/// The number of runtime memories (maximum `RuntimeMemoryIndex`) needed to
/// instantiate this component.
///
/// Note that this many memories will be stored in the `VMComponentContext`
/// and each memory is intended to be unique (e.g. the same memory isn't
/// stored in two different locations).
pub num_runtime_memories: u32,
/// The number of runtime reallocs (maximum `RuntimeReallocIndex`) needed to
/// instantiate this component.
///
/// Note that this many function pointers will be stored in the
/// `VMComponentContext`.
pub num_runtime_reallocs: u32,
/// Same as `num_runtime_reallocs`, but for post-return functions.
pub num_runtime_post_returns: u32,
/// The number of lowered host functions (maximum `LoweredIndex`) needed to
/// instantiate this component.
pub num_lowerings: u32,
/// The number of modules that are required to be saved within an instance
/// at runtime, or effectively the number of exported modules.
pub num_runtime_modules: u32,
/// The number of functions which "always trap" used to implement
/// `canon.lower` of `canon.lift`'d functions within the same component.
pub num_always_trap: u32,
/// The number of host transcoder functions needed for strings in adapter
/// modules.
pub num_transcoders: u32,
}
/// GlobalInitializer instructions to get processed when instantiating a component
///
/// The variants of this enum are processed during the instantiation phase of
/// a component in-order from front-to-back. These are otherwise emitted as a
/// component is parsed and read and translated.
//
// FIXME(#2639) if processing this list is ever a bottleneck we could
// theoretically use cranelift to compile an initialization function which
// performs all of these duties for us and skips the overhead of interpreting
// all of these instructions.
#[derive(Debug, Serialize, Deserialize)]
pub enum GlobalInitializer {
/// A core wasm module is being instantiated.
///
/// This will result in a new core wasm instance being created, which may
/// involve running the `start` function of the instance as well if it's
/// specified. This largely delegates to the same standard instantiation
/// process as the rest of the core wasm machinery already uses.
InstantiateModule(InstantiateModule),
/// A host function is being lowered, creating a core wasm function.
///
/// This initializer entry is intended to be used to fill out the
/// `VMComponentContext` and information about this lowering such as the
/// cranelift-compiled trampoline function pointer, the host function
/// pointer the trampoline calls, and the canonical ABI options.
LowerImport(LowerImport),
/// A core wasm function was "generated" via `canon lower` of a function
/// that was `canon lift`'d in the same component, meaning that the function
/// always traps. This is recorded within the `VMComponentContext` as a new
/// `VMCallerCheckedAnyfunc` that's available for use.
AlwaysTrap(AlwaysTrap),
/// A core wasm linear memory is going to be saved into the
/// `VMComponentContext`.
///
/// This instruction indicates that the `index`th core wasm linear memory
/// needs to be extracted from the `export` specified, a pointer to a
/// previously created module instance, and stored into the
/// `VMComponentContext` at the `index` specified. This lowering is then
/// used in the future by pointers from `CanonicalOptions`.
ExtractMemory(ExtractMemory),
/// Same as `ExtractMemory`, except it's extracting a function pointer to be
/// used as a `realloc` function.
ExtractRealloc(ExtractRealloc),
/// Same as `ExtractMemory`, except it's extracting a function pointer to be
/// used as a `post-return` function.
ExtractPostReturn(ExtractPostReturn),
/// The `module` specified is saved into the runtime state at the next
/// `RuntimeModuleIndex`, referred to later by `Export` definitions.
SaveStaticModule(StaticModuleIndex),
/// Same as `SaveModuleUpvar`, but for imports.
SaveModuleImport(RuntimeImportIndex),
/// Similar to `ExtractMemory` and friends and indicates that a
/// `VMCallerCheckedAnyfunc` needs to be initialized for a transcoder
/// function and this will later be used to instantiate an adapter module.
Transcoder(Transcoder),
}
/// Metadata for extraction of a memory of what's being extracted and where it's
/// going.
#[derive(Debug, Serialize, Deserialize)]
pub struct ExtractMemory {
/// The index of the memory being defined.
pub index: RuntimeMemoryIndex,
/// Where this memory is being extracted from.
pub export: CoreExport<MemoryIndex>,
}
/// Same as `ExtractMemory` but for the `realloc` canonical option.
#[derive(Debug, Serialize, Deserialize)]
pub struct ExtractRealloc {
/// The index of the realloc being defined.
pub index: RuntimeReallocIndex,
/// Where this realloc is being extracted from.
pub def: CoreDef,
}
/// Same as `ExtractMemory` but for the `post-return` canonical option.
#[derive(Debug, Serialize, Deserialize)]
pub struct ExtractPostReturn {
/// The index of the post-return being defined.
pub index: RuntimePostReturnIndex,
/// Where this post-return is being extracted from.
pub def: CoreDef,
}
/// Different methods of instantiating a core wasm module.
#[derive(Debug, Serialize, Deserialize)]
pub enum InstantiateModule {
/// A module defined within this component is being instantiated.
///
/// Note that this is distinct from the case of imported modules because the
/// order of imports required is statically known and can be pre-calculated
/// to avoid string lookups related to names at runtime, represented by the
/// flat list of arguments here.
Static(StaticModuleIndex, Box<[CoreDef]>),
/// An imported module is being instantiated.
///
/// This is similar to `Upvar` but notably the imports are provided as a
/// two-level named map since import resolution order needs to happen at
/// runtime.
Import(
RuntimeImportIndex,
IndexMap<String, IndexMap<String, CoreDef>>,
),
}
/// Description of a lowered import used in conjunction with
/// `GlobalInitializer::LowerImport`.
#[derive(Debug, Serialize, Deserialize)]
pub struct LowerImport {
/// The index of the lowered function that's being created.
///
/// This is guaranteed to be the `n`th `LowerImport` instruction
/// if the index is `n`.
pub index: LoweredIndex,
/// The index of the imported host function that is being lowered.
///
/// It's guaranteed that this `RuntimeImportIndex` points to a function.
pub import: RuntimeImportIndex,
/// The core wasm signature of the function that's being created.
pub canonical_abi: SignatureIndex,
/// The canonical ABI options used when lowering this function specified in
/// the original component.
pub options: CanonicalOptions,
}
/// Description of what to initialize when a `GlobalInitializer::AlwaysTrap` is
/// encountered.
#[derive(Debug, Serialize, Deserialize)]
pub struct AlwaysTrap {
/// The index of the function that is being initialized in the
/// `VMComponentContext`.
pub index: RuntimeAlwaysTrapIndex,
/// The core wasm signature of the function that's inserted.
pub canonical_abi: SignatureIndex,
}
/// Definition of a core wasm item and where it can come from within a
/// component.
///
/// Note that this is sort of a result of data-flow-like analysis on a component
/// during compile time of the component itself. References to core wasm items
/// are "compiled" to either referring to a previous instance or to some sort of
/// lowered host import.
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
pub enum CoreDef {
/// This item refers to an export of a previously instantiated core wasm
/// instance.
Export(CoreExport<EntityIndex>),
/// This item is a core wasm function with the index specified here. Note
/// that this `LoweredIndex` corresponds to the nth
/// `GlobalInitializer::LowerImport` instruction.
Lowered(LoweredIndex),
/// This is used to represent a degenerate case of where a `canon lift`'d
/// function is immediately `canon lower`'d in the same instance. Such a
/// function always traps at runtime.
AlwaysTrap(RuntimeAlwaysTrapIndex),
/// This is a reference to a wasm global which represents the
/// runtime-managed flags for a wasm instance.
InstanceFlags(RuntimeComponentInstanceIndex),
/// This refers to a cranelift-generated trampoline which calls to a
/// host-defined transcoding function.
Transcoder(RuntimeTranscoderIndex),
}
impl<T> From<CoreExport<T>> for CoreDef
where
EntityIndex: From<T>,
{
fn from(export: CoreExport<T>) -> CoreDef {
CoreDef::Export(export.map_index(|i| i.into()))
}
}
/// Identifier of an exported item from a core WebAssembly module instance.
///
/// Note that the `T` here is the index type for exports which can be
/// identified by index. The `T` is monomorphized with types like
/// [`EntityIndex`] or [`FuncIndex`].
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
pub struct CoreExport<T> {
/// The instance that this item is located within.
///
/// Note that this is intended to index the `instances` map within a
/// component. It's validated ahead of time that all instance pointers
/// refer only to previously-created instances.
pub instance: RuntimeInstanceIndex,
/// The item that this export is referencing, either by name or by index.
pub item: ExportItem<T>,
}
impl<T> CoreExport<T> {
/// Maps the index type `T` to another type `U` if this export item indeed
/// refers to an index `T`.
pub fn map_index<U>(self, f: impl FnOnce(T) -> U) -> CoreExport<U> {
CoreExport {
instance: self.instance,
item: match self.item {
ExportItem::Index(i) => ExportItem::Index(f(i)),
ExportItem::Name(s) => ExportItem::Name(s),
},
}
}
}
/// An index at which to find an item within a runtime instance.
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
pub enum ExportItem<T> {
/// An exact index that the target can be found at.
///
/// This is used where possible to avoid name lookups at runtime during the
/// instantiation process. This can only be used on instances where the
/// module was statically known at compile time, however.
Index(T),
/// An item which is identified by a name, so at runtime we need to
/// perform a name lookup to determine the index that the item is located
/// at.
///
/// This is used for instantiations of imported modules, for example, since
/// the precise shape of the module is not known.
Name(String),
}
/// Possible exports from a component.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Export {
/// A lifted function being exported which is an adaptation of a core wasm
/// function.
LiftedFunction {
/// The component function type of the function being created.
ty: TypeFuncIndex,
/// Which core WebAssembly export is being lifted.
func: CoreDef,
/// Any options, if present, associated with this lifting.
options: CanonicalOptions,
},
/// A module defined within this component is exported.
///
/// The module index here indexes a module recorded with
/// `GlobalInitializer::SaveModule` above.
Module(RuntimeModuleIndex),
/// A nested instance is being exported which has recursively defined
/// `Export` items.
Instance(IndexMap<String, Export>),
}
/// Canonical ABI options associated with a lifted or lowered function.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CanonicalOptions {
/// The component instance that this bundle was associated with.
pub instance: RuntimeComponentInstanceIndex,
/// The encoding used for strings.
pub string_encoding: StringEncoding,
/// The memory used by these options, if specified.
pub memory: Option<RuntimeMemoryIndex>,
/// The realloc function used by these options, if specified.
pub realloc: Option<RuntimeReallocIndex>,
/// The post-return function used by these options, if specified.
pub post_return: Option<RuntimePostReturnIndex>,
}
/// Possible encodings of strings within the component model.
//
// Note that the `repr(u8)` is load-bearing here since this is used in an
// `extern "C" fn()` function argument which is called from cranelift-compiled
// code so we must know the representation of this.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
#[allow(missing_docs)]
#[repr(u8)]
pub enum StringEncoding {
Utf8,
Utf16,
CompactUtf16,
}
/// Information about a string transcoding function required by an adapter
/// module.
///
/// A transcoder is used when strings are passed between adapter modules,
/// optionally changing string encodings at the same time. The transcoder is
/// implemented in a few different layers:
///
/// * Each generated adapter module has some glue around invoking the transcoder
/// represented by this item. This involves bounds-checks and handling
/// `realloc` for example.
/// * Each transcoder gets a cranelift-generated trampoline which has the
/// appropriate signature for the adapter module in question. Existence of
/// this initializer indicates that this should be compiled by Cranelift.
/// * The cranelift-generated trampoline will invoke a "transcoder libcall"
/// which is implemented natively in Rust that has a signature independent of
/// memory64 configuration options for example.
#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
pub struct Transcoder {
/// The index of the transcoder being defined and initialized.
///
/// This indicates which `VMCallerCheckedAnyfunc` slot is written to in a
/// `VMComponentContext`.
pub index: RuntimeTranscoderIndex,
/// The transcoding operation being performed.
pub op: Transcode,
/// The linear memory that the string is being read from.
pub from: RuntimeMemoryIndex,
/// Whether or not the source linear memory is 64-bit or not.
pub from64: bool,
/// The linear memory that the string is being written to.
pub to: RuntimeMemoryIndex,
/// Whether or not the destination linear memory is 64-bit or not.
pub to64: bool,
/// The wasm signature of the cranelift-generated trampoline.
pub signature: SignatureIndex,
}
pub use crate::fact::{FixedEncoding, Transcode};