Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/environ/src/component/dfg.rs
3081 views
1
//! A dataflow-graph-like intermediate representation of a component
2
//!
3
//! This module contains `ComponentDfg` which is an intermediate step towards
4
//! becoming a full-fledged `Component`. The main purpose for the existence of
5
//! this representation of a component is to track dataflow between various
6
//! items within a component and support edits to them after the initial inlined
7
//! translation of a component.
8
//!
9
//! Currently fused adapters are represented with a core WebAssembly module
10
//! which gets "injected" into the final component as-if the component already
11
//! bundled it. In doing so the adapter modules need to be partitioned and
12
//! inserted into the final sequence of modules to instantiate. While this is
13
//! possible to do with a flat `GlobalInitializer` list it gets unwieldy really
14
//! quickly especially when other translation features are added.
15
//!
16
//! This module is largely a duplicate of the `component::info` module in this
17
//! crate. The hierarchy here uses `*Id` types instead of `*Index` types to
18
//! represent that they don't have any necessary implicit ordering. Additionally
19
//! nothing is kept in an ordered list and instead this is worked with in a
20
//! general dataflow fashion where dependencies are walked during processing.
21
//!
22
//! The `ComponentDfg::finish` method will convert the dataflow graph to a
23
//! linearized `GlobalInitializer` list which is intended to not be edited after
24
//! it's created.
25
//!
26
//! The `ComponentDfg` is created as part of the `component::inline` phase of
27
//! translation where the dataflow performed there allows identification of
28
//! fused adapters, what arguments make their way to core wasm modules, etc.
29
30
use crate::component::*;
31
use crate::error::Result;
32
use crate::prelude::*;
33
use crate::{EntityIndex, EntityRef, ModuleInternedTypeIndex, PrimaryMap, WasmValType};
34
use cranelift_entity::packed_option::PackedOption;
35
use indexmap::IndexMap;
36
use info::LinearMemoryOptions;
37
use std::collections::HashMap;
38
use std::hash::Hash;
39
use std::ops::Index;
40
use wasmparser::component_types::ComponentCoreModuleTypeId;
41
42
/// High-level representation of a component as a "data-flow graph".
43
#[derive(Default)]
44
pub struct ComponentDfg {
45
/// Same as `Component::import_types`
46
pub import_types: PrimaryMap<ImportIndex, (String, TypeDef)>,
47
48
/// Same as `Component::imports`
49
pub imports: PrimaryMap<RuntimeImportIndex, (ImportIndex, Vec<String>)>,
50
51
/// Same as `Component::exports`
52
pub exports: IndexMap<String, Export>,
53
54
/// All trampolines and their type signature which will need to get
55
/// compiled by Cranelift.
56
pub trampolines: Intern<TrampolineIndex, (ModuleInternedTypeIndex, Trampoline)>,
57
58
/// A map from `UnsafeIntrinsic::index()` to that intrinsic's
59
/// module-interned type.
60
pub unsafe_intrinsics: [PackedOption<ModuleInternedTypeIndex>; UnsafeIntrinsic::len() as usize],
61
62
/// Know reallocation functions which are used by `lowerings` (e.g. will be
63
/// used by the host)
64
pub reallocs: Intern<ReallocId, CoreDef>,
65
66
/// Same as `reallocs`, but for async-lifted functions.
67
pub callbacks: Intern<CallbackId, CoreDef>,
68
69
/// Same as `reallocs`, but for post-return.
70
pub post_returns: Intern<PostReturnId, CoreDef>,
71
72
/// Same as `reallocs`, but for memories.
73
pub memories: Intern<MemoryId, CoreExport<MemoryIndex>>,
74
75
/// Same as `reallocs`, but for tables.
76
pub tables: Intern<TableId, CoreExport<TableIndex>>,
77
78
/// Metadata about identified fused adapters.
79
///
80
/// Note that this list is required to be populated in-order where the
81
/// "left" adapters cannot depend on "right" adapters. Currently this falls
82
/// out of the inlining pass of translation.
83
pub adapters: Intern<AdapterId, Adapter>,
84
85
/// Metadata about all known core wasm instances created.
86
///
87
/// This is mostly an ordered list and is not deduplicated based on contents
88
/// unlike the items above. Creation of an `Instance` is side-effectful and
89
/// all instances here are always required to be created. These are
90
/// considered "roots" in dataflow.
91
pub instances: PrimaryMap<InstanceId, Instance>,
92
93
/// Number of component instances that were created during the inlining
94
/// phase (this is not edited after creation).
95
pub num_runtime_component_instances: u32,
96
97
/// Known adapter modules and how they are instantiated.
98
///
99
/// This map is not filled in on the initial creation of a `ComponentDfg`.
100
/// Instead these modules are filled in by the `inline::adapt` phase where
101
/// adapter modules are identified and filled in here.
102
///
103
/// The payload here is the static module index representing the core wasm
104
/// adapter module that was generated as well as the arguments to the
105
/// instantiation of the adapter module.
106
pub adapter_modules: PrimaryMap<AdapterModuleId, (StaticModuleIndex, Vec<CoreDef>)>,
107
108
/// Metadata about where adapters can be found within their respective
109
/// adapter modules.
110
///
111
/// Like `adapter_modules` this is not filled on the initial creation of
112
/// `ComponentDfg` but rather is created alongside `adapter_modules` during
113
/// the `inline::adapt` phase of translation.
114
///
115
/// The values here are the module that the adapter is present within along
116
/// as the core wasm index of the export corresponding to the lowered
117
/// version of the adapter.
118
pub adapter_partitionings: PrimaryMap<AdapterId, (AdapterModuleId, EntityIndex)>,
119
120
/// Defined resources in this component sorted by index with metadata about
121
/// each resource.
122
///
123
/// Note that each index here is a unique resource, and that may mean it was
124
/// the same component instantiated twice for example.
125
pub resources: PrimaryMap<DefinedResourceIndex, Resource>,
126
127
/// Metadata about all imported resources into this component. This records
128
/// both how many imported resources there are (the size of this map) along
129
/// with what the corresponding runtime import is.
130
pub imported_resources: PrimaryMap<ResourceIndex, RuntimeImportIndex>,
131
132
/// The total number of future tables that will be used by this component.
133
pub num_future_tables: usize,
134
135
/// The total number of stream tables that will be used by this component.
136
pub num_stream_tables: usize,
137
138
/// The total number of error-context tables that will be used by this
139
/// component.
140
pub num_error_context_tables: usize,
141
142
/// An ordered list of side effects induced by instantiating this component.
143
///
144
/// Currently all side effects are either instantiating core wasm modules or
145
/// declaring a resource. These side effects affect the dataflow processing
146
/// of this component by idnicating what order operations should be
147
/// performed during instantiation.
148
pub side_effects: Vec<SideEffect>,
149
150
/// Interned map of id-to-`CanonicalOptions`, or all sets-of-options used by
151
/// this component.
152
pub options: Intern<OptionsId, CanonicalOptions>,
153
}
154
155
/// Possible side effects that are possible with instantiating this component.
156
pub enum SideEffect {
157
/// A core wasm instance was created.
158
///
159
/// Instantiation is side-effectful due to the presence of constructs such
160
/// as traps and the core wasm `start` function which may call component
161
/// imports. Instantiation order from the original component must be done in
162
/// the same order.
163
Instance(InstanceId, RuntimeComponentInstanceIndex),
164
165
/// A resource was declared in this component.
166
///
167
/// This is a bit less side-effectful than instantiation but this serves as
168
/// the order in which resources are initialized in a component with their
169
/// destructors. Destructors are loaded from core wasm instances (or
170
/// lowerings) which are produced by prior side-effectful operations.
171
Resource(DefinedResourceIndex),
172
}
173
174
/// A sound approximation of a particular module's set of instantiations.
175
///
176
/// This type forms a simple lattice that we can use in static analyses that in
177
/// turn let us specialize a module's compilation to exactly the imports it is
178
/// given.
179
#[derive(Clone, Copy, Default)]
180
pub enum AbstractInstantiations<'a> {
181
/// The associated module is instantiated many times.
182
Many,
183
184
/// The module is instantiated exactly once, with the given definitions as
185
/// arguments to that instantiation.
186
One(&'a [info::CoreDef]),
187
188
/// The module is never instantiated.
189
#[default]
190
None,
191
}
192
193
impl AbstractInstantiations<'_> {
194
/// Join two facts about a particular module's instantiation together.
195
///
196
/// This is the least-upper-bound operation on the lattice.
197
pub fn join(&mut self, other: Self) {
198
*self = match (*self, other) {
199
(Self::Many, _) | (_, Self::Many) => Self::Many,
200
(Self::One(a), Self::One(b)) if a == b => Self::One(a),
201
(Self::One(_), Self::One(_)) => Self::Many,
202
(Self::One(a), Self::None) | (Self::None, Self::One(a)) => Self::One(a),
203
(Self::None, Self::None) => Self::None,
204
}
205
}
206
}
207
208
macro_rules! id {
209
($(pub struct $name:ident(u32);)*) => ($(
210
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
211
#[expect(missing_docs, reason = "tedious to document")]
212
pub struct $name(u32);
213
cranelift_entity::entity_impl!($name);
214
)*)
215
}
216
217
id! {
218
pub struct InstanceId(u32);
219
pub struct MemoryId(u32);
220
pub struct TableId(u32);
221
pub struct ReallocId(u32);
222
pub struct CallbackId(u32);
223
pub struct AdapterId(u32);
224
pub struct PostReturnId(u32);
225
pub struct AdapterModuleId(u32);
226
pub struct OptionsId(u32);
227
}
228
229
/// Same as `info::InstantiateModule`
230
#[expect(missing_docs, reason = "tedious to document variants")]
231
pub enum Instance {
232
Static(StaticModuleIndex, Box<[CoreDef]>),
233
Import(
234
RuntimeImportIndex,
235
IndexMap<String, IndexMap<String, CoreDef>>,
236
),
237
}
238
239
/// Same as `info::Export`
240
#[expect(missing_docs, reason = "tedious to document variants")]
241
pub enum Export {
242
LiftedFunction {
243
ty: TypeFuncIndex,
244
func: CoreDef,
245
options: OptionsId,
246
},
247
ModuleStatic {
248
ty: ComponentCoreModuleTypeId,
249
index: StaticModuleIndex,
250
},
251
ModuleImport {
252
ty: TypeModuleIndex,
253
import: RuntimeImportIndex,
254
},
255
Instance {
256
ty: TypeComponentInstanceIndex,
257
exports: IndexMap<String, Export>,
258
},
259
Type(TypeDef),
260
}
261
262
/// Same as `info::CoreDef`, except has an extra `Adapter` variant.
263
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
264
#[expect(missing_docs, reason = "tedious to document variants")]
265
pub enum CoreDef {
266
Export(CoreExport<EntityIndex>),
267
InstanceFlags(RuntimeComponentInstanceIndex),
268
Trampoline(TrampolineIndex),
269
UnsafeIntrinsic(ModuleInternedTypeIndex, UnsafeIntrinsic),
270
TaskMayBlock,
271
272
/// This is a special variant not present in `info::CoreDef` which
273
/// represents that this definition refers to a fused adapter function. This
274
/// adapter is fully processed after the initial translation and
275
/// identification of adapters.
276
///
277
/// During translation into `info::CoreDef` this variant is erased and
278
/// replaced by `info::CoreDef::Export` since adapters are always
279
/// represented as the exports of a core wasm instance.
280
Adapter(AdapterId),
281
}
282
283
impl<T> From<CoreExport<T>> for CoreDef
284
where
285
EntityIndex: From<T>,
286
{
287
fn from(export: CoreExport<T>) -> CoreDef {
288
CoreDef::Export(export.map_index(|i| i.into()))
289
}
290
}
291
292
/// Same as `info::CoreExport`
293
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
294
#[expect(missing_docs, reason = "self-describing fields")]
295
pub struct CoreExport<T> {
296
pub instance: InstanceId,
297
pub item: ExportItem<T>,
298
}
299
300
impl<T> CoreExport<T> {
301
#[expect(missing_docs, reason = "self-describing function")]
302
pub fn map_index<U>(self, f: impl FnOnce(T) -> U) -> CoreExport<U> {
303
CoreExport {
304
instance: self.instance,
305
item: match self.item {
306
ExportItem::Index(i) => ExportItem::Index(f(i)),
307
ExportItem::Name(s) => ExportItem::Name(s),
308
},
309
}
310
}
311
}
312
313
/// Same as `info::Trampoline`
314
#[derive(Clone, PartialEq, Eq, Hash)]
315
#[expect(missing_docs, reason = "self-describing fields")]
316
pub enum Trampoline {
317
LowerImport {
318
import: RuntimeImportIndex,
319
options: OptionsId,
320
lower_ty: TypeFuncIndex,
321
},
322
Transcoder {
323
op: Transcode,
324
from: MemoryId,
325
from64: bool,
326
to: MemoryId,
327
to64: bool,
328
},
329
ResourceNew {
330
instance: RuntimeComponentInstanceIndex,
331
ty: TypeResourceTableIndex,
332
},
333
ResourceRep {
334
instance: RuntimeComponentInstanceIndex,
335
ty: TypeResourceTableIndex,
336
},
337
ResourceDrop {
338
instance: RuntimeComponentInstanceIndex,
339
ty: TypeResourceTableIndex,
340
},
341
BackpressureInc {
342
instance: RuntimeComponentInstanceIndex,
343
},
344
BackpressureDec {
345
instance: RuntimeComponentInstanceIndex,
346
},
347
TaskReturn {
348
instance: RuntimeComponentInstanceIndex,
349
results: TypeTupleIndex,
350
options: OptionsId,
351
},
352
TaskCancel {
353
instance: RuntimeComponentInstanceIndex,
354
},
355
WaitableSetNew {
356
instance: RuntimeComponentInstanceIndex,
357
},
358
WaitableSetWait {
359
instance: RuntimeComponentInstanceIndex,
360
options: OptionsId,
361
},
362
WaitableSetPoll {
363
instance: RuntimeComponentInstanceIndex,
364
options: OptionsId,
365
},
366
WaitableSetDrop {
367
instance: RuntimeComponentInstanceIndex,
368
},
369
WaitableJoin {
370
instance: RuntimeComponentInstanceIndex,
371
},
372
ThreadYield {
373
instance: RuntimeComponentInstanceIndex,
374
cancellable: bool,
375
},
376
SubtaskDrop {
377
instance: RuntimeComponentInstanceIndex,
378
},
379
SubtaskCancel {
380
instance: RuntimeComponentInstanceIndex,
381
async_: bool,
382
},
383
StreamNew {
384
instance: RuntimeComponentInstanceIndex,
385
ty: TypeStreamTableIndex,
386
},
387
StreamRead {
388
instance: RuntimeComponentInstanceIndex,
389
ty: TypeStreamTableIndex,
390
options: OptionsId,
391
},
392
StreamWrite {
393
instance: RuntimeComponentInstanceIndex,
394
ty: TypeStreamTableIndex,
395
options: OptionsId,
396
},
397
StreamCancelRead {
398
instance: RuntimeComponentInstanceIndex,
399
ty: TypeStreamTableIndex,
400
async_: bool,
401
},
402
StreamCancelWrite {
403
instance: RuntimeComponentInstanceIndex,
404
ty: TypeStreamTableIndex,
405
async_: bool,
406
},
407
StreamDropReadable {
408
instance: RuntimeComponentInstanceIndex,
409
ty: TypeStreamTableIndex,
410
},
411
StreamDropWritable {
412
instance: RuntimeComponentInstanceIndex,
413
ty: TypeStreamTableIndex,
414
},
415
FutureNew {
416
instance: RuntimeComponentInstanceIndex,
417
ty: TypeFutureTableIndex,
418
},
419
FutureRead {
420
instance: RuntimeComponentInstanceIndex,
421
ty: TypeFutureTableIndex,
422
options: OptionsId,
423
},
424
FutureWrite {
425
instance: RuntimeComponentInstanceIndex,
426
ty: TypeFutureTableIndex,
427
options: OptionsId,
428
},
429
FutureCancelRead {
430
instance: RuntimeComponentInstanceIndex,
431
ty: TypeFutureTableIndex,
432
async_: bool,
433
},
434
FutureCancelWrite {
435
instance: RuntimeComponentInstanceIndex,
436
ty: TypeFutureTableIndex,
437
async_: bool,
438
},
439
FutureDropReadable {
440
instance: RuntimeComponentInstanceIndex,
441
ty: TypeFutureTableIndex,
442
},
443
FutureDropWritable {
444
instance: RuntimeComponentInstanceIndex,
445
ty: TypeFutureTableIndex,
446
},
447
ErrorContextNew {
448
instance: RuntimeComponentInstanceIndex,
449
ty: TypeComponentLocalErrorContextTableIndex,
450
options: OptionsId,
451
},
452
ErrorContextDebugMessage {
453
instance: RuntimeComponentInstanceIndex,
454
ty: TypeComponentLocalErrorContextTableIndex,
455
options: OptionsId,
456
},
457
ErrorContextDrop {
458
instance: RuntimeComponentInstanceIndex,
459
ty: TypeComponentLocalErrorContextTableIndex,
460
},
461
ResourceTransferOwn,
462
ResourceTransferBorrow,
463
PrepareCall {
464
memory: Option<MemoryId>,
465
},
466
SyncStartCall {
467
callback: Option<CallbackId>,
468
},
469
AsyncStartCall {
470
callback: Option<CallbackId>,
471
post_return: Option<PostReturnId>,
472
},
473
FutureTransfer,
474
StreamTransfer,
475
ErrorContextTransfer,
476
Trap,
477
EnterSyncCall,
478
ExitSyncCall,
479
ContextGet {
480
instance: RuntimeComponentInstanceIndex,
481
slot: u32,
482
},
483
ContextSet {
484
instance: RuntimeComponentInstanceIndex,
485
slot: u32,
486
},
487
ThreadIndex,
488
ThreadNewIndirect {
489
instance: RuntimeComponentInstanceIndex,
490
start_func_ty_idx: ComponentTypeIndex,
491
start_func_table_id: TableId,
492
},
493
ThreadSwitchTo {
494
instance: RuntimeComponentInstanceIndex,
495
cancellable: bool,
496
},
497
ThreadSuspend {
498
instance: RuntimeComponentInstanceIndex,
499
cancellable: bool,
500
},
501
ThreadResumeLater {
502
instance: RuntimeComponentInstanceIndex,
503
},
504
ThreadYieldTo {
505
instance: RuntimeComponentInstanceIndex,
506
cancellable: bool,
507
},
508
}
509
510
#[derive(Copy, Clone, Hash, Eq, PartialEq)]
511
#[expect(missing_docs, reason = "self-describing fields")]
512
pub struct FutureInfo {
513
pub instance: RuntimeComponentInstanceIndex,
514
pub payload_type: Option<InterfaceType>,
515
}
516
517
#[derive(Copy, Clone, Hash, Eq, PartialEq)]
518
#[expect(missing_docs, reason = "self-describing fields")]
519
pub struct StreamInfo {
520
pub instance: RuntimeComponentInstanceIndex,
521
pub payload_type: InterfaceType,
522
}
523
524
/// Same as `info::CanonicalOptionsDataModel`.
525
#[derive(Clone, Hash, Eq, PartialEq)]
526
#[expect(missing_docs, reason = "self-describing fields")]
527
pub enum CanonicalOptionsDataModel {
528
Gc {},
529
LinearMemory {
530
memory: Option<MemoryId>,
531
realloc: Option<ReallocId>,
532
},
533
}
534
535
/// Same as `info::CanonicalOptions`
536
#[derive(Clone, Hash, Eq, PartialEq)]
537
#[expect(missing_docs, reason = "self-describing fields")]
538
pub struct CanonicalOptions {
539
pub instance: RuntimeComponentInstanceIndex,
540
pub string_encoding: StringEncoding,
541
pub callback: Option<CallbackId>,
542
pub post_return: Option<PostReturnId>,
543
pub async_: bool,
544
pub cancellable: bool,
545
pub core_type: ModuleInternedTypeIndex,
546
pub data_model: CanonicalOptionsDataModel,
547
}
548
549
/// Same as `info::Resource`
550
#[expect(missing_docs, reason = "self-describing fields")]
551
pub struct Resource {
552
pub rep: WasmValType,
553
pub dtor: Option<CoreDef>,
554
pub instance: RuntimeComponentInstanceIndex,
555
}
556
557
/// A helper structure to "intern" and deduplicate values of type `V` with an
558
/// identifying key `K`.
559
///
560
/// Note that this can also be used where `V` can't be intern'd to represent a
561
/// flat list of items.
562
pub struct Intern<K: EntityRef, V> {
563
intern_map: HashMap<V, K>,
564
key_map: PrimaryMap<K, V>,
565
}
566
567
impl<K, V> Intern<K, V>
568
where
569
K: EntityRef,
570
{
571
/// Inserts the `value` specified into this set, returning either a fresh
572
/// key `K` if this value hasn't been seen before or otherwise returning the
573
/// previous `K` used to represent value.
574
///
575
/// Note that this should only be used for component model items where the
576
/// creation of `value` is not side-effectful.
577
pub fn push(&mut self, value: V) -> K
578
where
579
V: Hash + Eq + Clone,
580
{
581
*self
582
.intern_map
583
.entry(value.clone())
584
.or_insert_with(|| self.key_map.push(value))
585
}
586
587
/// Returns an iterator of all the values contained within this set.
588
pub fn iter(&self) -> impl Iterator<Item = (K, &V)> {
589
self.key_map.iter()
590
}
591
}
592
593
impl<K: EntityRef, V> Index<K> for Intern<K, V> {
594
type Output = V;
595
fn index(&self, key: K) -> &V {
596
&self.key_map[key]
597
}
598
}
599
600
impl<K: EntityRef, V> Default for Intern<K, V> {
601
fn default() -> Intern<K, V> {
602
Intern {
603
intern_map: HashMap::new(),
604
key_map: PrimaryMap::new(),
605
}
606
}
607
}
608
609
impl ComponentDfg {
610
/// Consumes the intermediate `ComponentDfg` to produce a final `Component`
611
/// with a linear initializer list.
612
pub fn finish(
613
self,
614
wasmtime_types: &mut ComponentTypesBuilder,
615
wasmparser_types: wasmparser::types::TypesRef<'_>,
616
) -> Result<ComponentTranslation> {
617
let mut linearize = LinearizeDfg {
618
dfg: &self,
619
initializers: Vec::new(),
620
runtime_memories: Default::default(),
621
runtime_tables: Default::default(),
622
runtime_post_return: Default::default(),
623
runtime_reallocs: Default::default(),
624
runtime_callbacks: Default::default(),
625
runtime_instances: Default::default(),
626
num_lowerings: 0,
627
unsafe_intrinsics: Default::default(),
628
trampolines: Default::default(),
629
trampoline_defs: Default::default(),
630
trampoline_map: Default::default(),
631
options: Default::default(),
632
options_map: Default::default(),
633
};
634
635
// Handle all side effects of this component in the order that they're
636
// defined. This will, for example, process all instantiations necessary
637
// of core wasm modules.
638
for item in linearize.dfg.side_effects.iter() {
639
linearize.side_effect(item);
640
}
641
642
// Next the exports of the instance are handled which will likely end up
643
// creating some lowered imports, perhaps some saved modules, etc.
644
let mut export_items = PrimaryMap::new();
645
let mut exports = NameMap::default();
646
for (name, export) in self.exports.iter() {
647
let export =
648
linearize.export(export, &mut export_items, wasmtime_types, wasmparser_types)?;
649
exports.insert(name, &mut NameMapNoIntern, false, export)?;
650
}
651
652
// With all those pieces done the results of the dataflow-based
653
// linearization are recorded into the `Component`. The number of
654
// runtime values used for each index space is used from the `linearize`
655
// result.
656
Ok(ComponentTranslation {
657
trampolines: linearize.trampoline_defs,
658
component: Component {
659
exports,
660
export_items,
661
initializers: linearize.initializers,
662
unsafe_intrinsics: linearize.unsafe_intrinsics,
663
trampolines: linearize.trampolines,
664
num_lowerings: linearize.num_lowerings,
665
options: linearize.options,
666
667
num_runtime_memories: linearize.runtime_memories.len() as u32,
668
num_runtime_tables: linearize.runtime_tables.len() as u32,
669
num_runtime_post_returns: linearize.runtime_post_return.len() as u32,
670
num_runtime_reallocs: linearize.runtime_reallocs.len() as u32,
671
num_runtime_callbacks: linearize.runtime_callbacks.len() as u32,
672
num_runtime_instances: linearize.runtime_instances.len() as u32,
673
imports: self.imports,
674
import_types: self.import_types,
675
num_runtime_component_instances: self.num_runtime_component_instances,
676
num_future_tables: self.num_future_tables,
677
num_stream_tables: self.num_stream_tables,
678
num_error_context_tables: self.num_error_context_tables,
679
num_resources: (self.resources.len() + self.imported_resources.len()) as u32,
680
imported_resources: self.imported_resources,
681
defined_resource_instances: self
682
.resources
683
.iter()
684
.map(|(_, r)| r.instance)
685
.collect(),
686
},
687
})
688
}
689
690
/// Converts the provided defined index into a normal index, adding in the
691
/// number of imported resources.
692
pub fn resource_index(&self, defined: DefinedResourceIndex) -> ResourceIndex {
693
ResourceIndex::from_u32(defined.as_u32() + (self.imported_resources.len() as u32))
694
}
695
}
696
697
struct LinearizeDfg<'a> {
698
dfg: &'a ComponentDfg,
699
initializers: Vec<GlobalInitializer>,
700
unsafe_intrinsics: [PackedOption<ModuleInternedTypeIndex>; UnsafeIntrinsic::len() as usize],
701
trampolines: PrimaryMap<TrampolineIndex, ModuleInternedTypeIndex>,
702
trampoline_defs: PrimaryMap<TrampolineIndex, info::Trampoline>,
703
options: PrimaryMap<OptionsIndex, info::CanonicalOptions>,
704
trampoline_map: HashMap<TrampolineIndex, TrampolineIndex>,
705
runtime_memories: HashMap<MemoryId, RuntimeMemoryIndex>,
706
runtime_tables: HashMap<TableId, RuntimeTableIndex>,
707
runtime_reallocs: HashMap<ReallocId, RuntimeReallocIndex>,
708
runtime_callbacks: HashMap<CallbackId, RuntimeCallbackIndex>,
709
runtime_post_return: HashMap<PostReturnId, RuntimePostReturnIndex>,
710
runtime_instances: HashMap<RuntimeInstance, RuntimeInstanceIndex>,
711
options_map: HashMap<OptionsId, OptionsIndex>,
712
num_lowerings: u32,
713
}
714
715
#[derive(Copy, Clone, Hash, Eq, PartialEq)]
716
enum RuntimeInstance {
717
Normal(InstanceId),
718
Adapter(AdapterModuleId),
719
}
720
721
impl LinearizeDfg<'_> {
722
fn side_effect(&mut self, effect: &SideEffect) {
723
match effect {
724
SideEffect::Instance(i, ci) => {
725
self.instantiate(*i, &self.dfg.instances[*i], *ci);
726
}
727
SideEffect::Resource(i) => {
728
self.resource(*i, &self.dfg.resources[*i]);
729
}
730
}
731
}
732
733
fn instantiate(
734
&mut self,
735
instance: InstanceId,
736
args: &Instance,
737
component_instance: RuntimeComponentInstanceIndex,
738
) {
739
log::trace!("creating instance {instance:?}");
740
let instantiation = match args {
741
Instance::Static(index, args) => InstantiateModule::Static(
742
*index,
743
args.iter().map(|def| self.core_def(def)).collect(),
744
),
745
Instance::Import(index, args) => InstantiateModule::Import(
746
*index,
747
args.iter()
748
.map(|(module, values)| {
749
let values = values
750
.iter()
751
.map(|(name, def)| (name.clone(), self.core_def(def)))
752
.collect();
753
(module.clone(), values)
754
})
755
.collect(),
756
),
757
};
758
let index = RuntimeInstanceIndex::new(self.runtime_instances.len());
759
self.initializers.push(GlobalInitializer::InstantiateModule(
760
instantiation,
761
Some(component_instance),
762
));
763
let prev = self
764
.runtime_instances
765
.insert(RuntimeInstance::Normal(instance), index);
766
assert!(prev.is_none());
767
}
768
769
fn resource(&mut self, index: DefinedResourceIndex, resource: &Resource) {
770
let dtor = resource.dtor.as_ref().map(|dtor| self.core_def(dtor));
771
self.initializers
772
.push(GlobalInitializer::Resource(info::Resource {
773
dtor,
774
index,
775
rep: resource.rep,
776
instance: resource.instance,
777
}));
778
}
779
780
fn export(
781
&mut self,
782
export: &Export,
783
items: &mut PrimaryMap<ExportIndex, info::Export>,
784
wasmtime_types: &mut ComponentTypesBuilder,
785
wasmparser_types: wasmparser::types::TypesRef<'_>,
786
) -> Result<ExportIndex> {
787
let item = match export {
788
Export::LiftedFunction { ty, func, options } => {
789
let func = self.core_def(func);
790
let options = self.options(*options);
791
info::Export::LiftedFunction {
792
ty: *ty,
793
func,
794
options,
795
}
796
}
797
Export::ModuleStatic { ty, index } => info::Export::ModuleStatic {
798
ty: wasmtime_types.convert_module(wasmparser_types, *ty)?,
799
index: *index,
800
},
801
Export::ModuleImport { ty, import } => info::Export::ModuleImport {
802
ty: *ty,
803
import: *import,
804
},
805
Export::Instance { ty, exports } => info::Export::Instance {
806
ty: *ty,
807
exports: {
808
let mut map = NameMap::default();
809
for (name, export) in exports {
810
let export =
811
self.export(export, items, wasmtime_types, wasmparser_types)?;
812
map.insert(name, &mut NameMapNoIntern, false, export)?;
813
}
814
map
815
},
816
},
817
Export::Type(def) => info::Export::Type(*def),
818
};
819
Ok(items.push(item))
820
}
821
822
fn options(&mut self, options: OptionsId) -> OptionsIndex {
823
self.intern_no_init(
824
options,
825
|me| &mut me.options_map,
826
|me, options| me.convert_options(options),
827
)
828
}
829
830
fn convert_options(&mut self, options: OptionsId) -> OptionsIndex {
831
let options = &self.dfg.options[options];
832
let data_model = match options.data_model {
833
CanonicalOptionsDataModel::Gc {} => info::CanonicalOptionsDataModel::Gc {},
834
CanonicalOptionsDataModel::LinearMemory { memory, realloc } => {
835
info::CanonicalOptionsDataModel::LinearMemory(LinearMemoryOptions {
836
memory: memory.map(|mem| self.runtime_memory(mem)),
837
realloc: realloc.map(|mem| self.runtime_realloc(mem)),
838
})
839
}
840
};
841
let callback = options.callback.map(|mem| self.runtime_callback(mem));
842
let post_return = options.post_return.map(|mem| self.runtime_post_return(mem));
843
let options = info::CanonicalOptions {
844
instance: options.instance,
845
string_encoding: options.string_encoding,
846
callback,
847
post_return,
848
async_: options.async_,
849
cancellable: options.cancellable,
850
core_type: options.core_type,
851
data_model,
852
};
853
self.options.push(options)
854
}
855
856
fn runtime_memory(&mut self, mem: MemoryId) -> RuntimeMemoryIndex {
857
self.intern(
858
mem,
859
|me| &mut me.runtime_memories,
860
|me, mem| me.core_export(&me.dfg.memories[mem]),
861
|index, export| GlobalInitializer::ExtractMemory(ExtractMemory { index, export }),
862
)
863
}
864
865
fn runtime_table(&mut self, table: TableId) -> RuntimeTableIndex {
866
self.intern(
867
table,
868
|me| &mut me.runtime_tables,
869
|me, table| me.core_export(&me.dfg.tables[table]),
870
|index, export| GlobalInitializer::ExtractTable(ExtractTable { index, export }),
871
)
872
}
873
874
fn runtime_realloc(&mut self, realloc: ReallocId) -> RuntimeReallocIndex {
875
self.intern(
876
realloc,
877
|me| &mut me.runtime_reallocs,
878
|me, realloc| me.core_def(&me.dfg.reallocs[realloc]),
879
|index, def| GlobalInitializer::ExtractRealloc(ExtractRealloc { index, def }),
880
)
881
}
882
883
fn runtime_callback(&mut self, callback: CallbackId) -> RuntimeCallbackIndex {
884
self.intern(
885
callback,
886
|me| &mut me.runtime_callbacks,
887
|me, callback| me.core_def(&me.dfg.callbacks[callback]),
888
|index, def| GlobalInitializer::ExtractCallback(ExtractCallback { index, def }),
889
)
890
}
891
892
fn runtime_post_return(&mut self, post_return: PostReturnId) -> RuntimePostReturnIndex {
893
self.intern(
894
post_return,
895
|me| &mut me.runtime_post_return,
896
|me, post_return| me.core_def(&me.dfg.post_returns[post_return]),
897
|index, def| GlobalInitializer::ExtractPostReturn(ExtractPostReturn { index, def }),
898
)
899
}
900
901
fn core_def(&mut self, def: &CoreDef) -> info::CoreDef {
902
match def {
903
CoreDef::Export(e) => info::CoreDef::Export(self.core_export(e)),
904
CoreDef::InstanceFlags(i) => info::CoreDef::InstanceFlags(*i),
905
CoreDef::Adapter(id) => info::CoreDef::Export(self.adapter(*id)),
906
CoreDef::Trampoline(index) => info::CoreDef::Trampoline(self.trampoline(*index)),
907
CoreDef::UnsafeIntrinsic(ty, i) => {
908
let index = usize::try_from(i.index()).unwrap();
909
if self.unsafe_intrinsics[index].is_none() {
910
self.unsafe_intrinsics[index] = Some(*ty).into();
911
}
912
info::CoreDef::UnsafeIntrinsic(*i)
913
}
914
CoreDef::TaskMayBlock => info::CoreDef::TaskMayBlock,
915
}
916
}
917
918
fn trampoline(&mut self, index: TrampolineIndex) -> TrampolineIndex {
919
if let Some(idx) = self.trampoline_map.get(&index) {
920
return *idx;
921
}
922
let (signature, trampoline) = &self.dfg.trampolines[index];
923
let trampoline = match trampoline {
924
Trampoline::LowerImport {
925
import,
926
options,
927
lower_ty,
928
} => {
929
let index = LoweredIndex::from_u32(self.num_lowerings);
930
self.num_lowerings += 1;
931
self.initializers.push(GlobalInitializer::LowerImport {
932
index,
933
import: *import,
934
});
935
info::Trampoline::LowerImport {
936
index,
937
options: self.options(*options),
938
lower_ty: *lower_ty,
939
}
940
}
941
Trampoline::Transcoder {
942
op,
943
from,
944
from64,
945
to,
946
to64,
947
} => info::Trampoline::Transcoder {
948
op: *op,
949
from: self.runtime_memory(*from),
950
from64: *from64,
951
to: self.runtime_memory(*to),
952
to64: *to64,
953
},
954
Trampoline::ResourceNew { instance, ty } => info::Trampoline::ResourceNew {
955
instance: *instance,
956
ty: *ty,
957
},
958
Trampoline::ResourceDrop { instance, ty } => info::Trampoline::ResourceDrop {
959
instance: *instance,
960
ty: *ty,
961
},
962
Trampoline::ResourceRep { instance, ty } => info::Trampoline::ResourceRep {
963
instance: *instance,
964
ty: *ty,
965
},
966
Trampoline::BackpressureInc { instance } => info::Trampoline::BackpressureInc {
967
instance: *instance,
968
},
969
Trampoline::BackpressureDec { instance } => info::Trampoline::BackpressureDec {
970
instance: *instance,
971
},
972
Trampoline::TaskReturn {
973
instance,
974
results,
975
options,
976
} => info::Trampoline::TaskReturn {
977
instance: *instance,
978
results: *results,
979
options: self.options(*options),
980
},
981
Trampoline::TaskCancel { instance } => info::Trampoline::TaskCancel {
982
instance: *instance,
983
},
984
Trampoline::WaitableSetNew { instance } => info::Trampoline::WaitableSetNew {
985
instance: *instance,
986
},
987
Trampoline::WaitableSetWait { instance, options } => {
988
info::Trampoline::WaitableSetWait {
989
instance: *instance,
990
options: self.options(*options),
991
}
992
}
993
Trampoline::WaitableSetPoll { instance, options } => {
994
info::Trampoline::WaitableSetPoll {
995
instance: *instance,
996
options: self.options(*options),
997
}
998
}
999
Trampoline::WaitableSetDrop { instance } => info::Trampoline::WaitableSetDrop {
1000
instance: *instance,
1001
},
1002
Trampoline::WaitableJoin { instance } => info::Trampoline::WaitableJoin {
1003
instance: *instance,
1004
},
1005
Trampoline::ThreadYield {
1006
instance,
1007
cancellable,
1008
} => info::Trampoline::ThreadYield {
1009
instance: *instance,
1010
cancellable: *cancellable,
1011
},
1012
Trampoline::SubtaskDrop { instance } => info::Trampoline::SubtaskDrop {
1013
instance: *instance,
1014
},
1015
Trampoline::SubtaskCancel { instance, async_ } => info::Trampoline::SubtaskCancel {
1016
instance: *instance,
1017
async_: *async_,
1018
},
1019
Trampoline::StreamNew { instance, ty } => info::Trampoline::StreamNew {
1020
instance: *instance,
1021
ty: *ty,
1022
},
1023
Trampoline::StreamRead {
1024
instance,
1025
ty,
1026
options,
1027
} => info::Trampoline::StreamRead {
1028
instance: *instance,
1029
ty: *ty,
1030
options: self.options(*options),
1031
},
1032
Trampoline::StreamWrite {
1033
instance,
1034
ty,
1035
options,
1036
} => info::Trampoline::StreamWrite {
1037
instance: *instance,
1038
ty: *ty,
1039
options: self.options(*options),
1040
},
1041
Trampoline::StreamCancelRead {
1042
instance,
1043
ty,
1044
async_,
1045
} => info::Trampoline::StreamCancelRead {
1046
instance: *instance,
1047
ty: *ty,
1048
async_: *async_,
1049
},
1050
Trampoline::StreamCancelWrite {
1051
instance,
1052
ty,
1053
async_,
1054
} => info::Trampoline::StreamCancelWrite {
1055
instance: *instance,
1056
ty: *ty,
1057
async_: *async_,
1058
},
1059
Trampoline::StreamDropReadable { instance, ty } => {
1060
info::Trampoline::StreamDropReadable {
1061
instance: *instance,
1062
ty: *ty,
1063
}
1064
}
1065
Trampoline::StreamDropWritable { instance, ty } => {
1066
info::Trampoline::StreamDropWritable {
1067
instance: *instance,
1068
ty: *ty,
1069
}
1070
}
1071
Trampoline::FutureNew { instance, ty } => info::Trampoline::FutureNew {
1072
instance: *instance,
1073
ty: *ty,
1074
},
1075
Trampoline::FutureRead {
1076
instance,
1077
ty,
1078
options,
1079
} => info::Trampoline::FutureRead {
1080
instance: *instance,
1081
ty: *ty,
1082
options: self.options(*options),
1083
},
1084
Trampoline::FutureWrite {
1085
instance,
1086
ty,
1087
options,
1088
} => info::Trampoline::FutureWrite {
1089
instance: *instance,
1090
ty: *ty,
1091
options: self.options(*options),
1092
},
1093
Trampoline::FutureCancelRead {
1094
instance,
1095
ty,
1096
async_,
1097
} => info::Trampoline::FutureCancelRead {
1098
instance: *instance,
1099
ty: *ty,
1100
async_: *async_,
1101
},
1102
Trampoline::FutureCancelWrite {
1103
instance,
1104
ty,
1105
async_,
1106
} => info::Trampoline::FutureCancelWrite {
1107
instance: *instance,
1108
ty: *ty,
1109
async_: *async_,
1110
},
1111
Trampoline::FutureDropReadable { instance, ty } => {
1112
info::Trampoline::FutureDropReadable {
1113
instance: *instance,
1114
ty: *ty,
1115
}
1116
}
1117
Trampoline::FutureDropWritable { instance, ty } => {
1118
info::Trampoline::FutureDropWritable {
1119
instance: *instance,
1120
ty: *ty,
1121
}
1122
}
1123
Trampoline::ErrorContextNew {
1124
instance,
1125
ty,
1126
options,
1127
} => info::Trampoline::ErrorContextNew {
1128
instance: *instance,
1129
ty: *ty,
1130
options: self.options(*options),
1131
},
1132
Trampoline::ErrorContextDebugMessage {
1133
instance,
1134
ty,
1135
options,
1136
} => info::Trampoline::ErrorContextDebugMessage {
1137
instance: *instance,
1138
ty: *ty,
1139
options: self.options(*options),
1140
},
1141
Trampoline::ErrorContextDrop { instance, ty } => info::Trampoline::ErrorContextDrop {
1142
instance: *instance,
1143
ty: *ty,
1144
},
1145
Trampoline::ResourceTransferOwn => info::Trampoline::ResourceTransferOwn,
1146
Trampoline::ResourceTransferBorrow => info::Trampoline::ResourceTransferBorrow,
1147
Trampoline::PrepareCall { memory } => info::Trampoline::PrepareCall {
1148
memory: memory.map(|v| self.runtime_memory(v)),
1149
},
1150
Trampoline::SyncStartCall { callback } => info::Trampoline::SyncStartCall {
1151
callback: callback.map(|v| self.runtime_callback(v)),
1152
},
1153
Trampoline::AsyncStartCall {
1154
callback,
1155
post_return,
1156
} => info::Trampoline::AsyncStartCall {
1157
callback: callback.map(|v| self.runtime_callback(v)),
1158
post_return: post_return.map(|v| self.runtime_post_return(v)),
1159
},
1160
Trampoline::FutureTransfer => info::Trampoline::FutureTransfer,
1161
Trampoline::StreamTransfer => info::Trampoline::StreamTransfer,
1162
Trampoline::ErrorContextTransfer => info::Trampoline::ErrorContextTransfer,
1163
Trampoline::Trap => info::Trampoline::Trap,
1164
Trampoline::EnterSyncCall => info::Trampoline::EnterSyncCall,
1165
Trampoline::ExitSyncCall => info::Trampoline::ExitSyncCall,
1166
Trampoline::ContextGet { instance, slot } => info::Trampoline::ContextGet {
1167
instance: *instance,
1168
slot: *slot,
1169
},
1170
Trampoline::ContextSet { instance, slot } => info::Trampoline::ContextSet {
1171
instance: *instance,
1172
slot: *slot,
1173
},
1174
Trampoline::ThreadIndex => info::Trampoline::ThreadIndex,
1175
Trampoline::ThreadNewIndirect {
1176
instance,
1177
start_func_ty_idx,
1178
start_func_table_id,
1179
} => info::Trampoline::ThreadNewIndirect {
1180
instance: *instance,
1181
start_func_ty_idx: *start_func_ty_idx,
1182
start_func_table_idx: self.runtime_table(*start_func_table_id),
1183
},
1184
Trampoline::ThreadSwitchTo {
1185
instance,
1186
cancellable,
1187
} => info::Trampoline::ThreadSwitchTo {
1188
instance: *instance,
1189
cancellable: *cancellable,
1190
},
1191
Trampoline::ThreadSuspend {
1192
instance,
1193
cancellable,
1194
} => info::Trampoline::ThreadSuspend {
1195
instance: *instance,
1196
cancellable: *cancellable,
1197
},
1198
Trampoline::ThreadResumeLater { instance } => info::Trampoline::ThreadResumeLater {
1199
instance: *instance,
1200
},
1201
Trampoline::ThreadYieldTo {
1202
instance,
1203
cancellable,
1204
} => info::Trampoline::ThreadYieldTo {
1205
instance: *instance,
1206
cancellable: *cancellable,
1207
},
1208
};
1209
let i1 = self.trampolines.push(*signature);
1210
let i2 = self.trampoline_defs.push(trampoline);
1211
assert_eq!(i1, i2);
1212
self.trampoline_map.insert(index, i1);
1213
i1
1214
}
1215
1216
fn core_export<T>(&mut self, export: &CoreExport<T>) -> info::CoreExport<T>
1217
where
1218
T: Clone,
1219
{
1220
let instance = export.instance;
1221
log::trace!("referencing export of {instance:?}");
1222
info::CoreExport {
1223
instance: self.runtime_instances[&RuntimeInstance::Normal(instance)],
1224
item: export.item.clone(),
1225
}
1226
}
1227
1228
fn adapter(&mut self, adapter: AdapterId) -> info::CoreExport<EntityIndex> {
1229
let (adapter_module, entity_index) = self.dfg.adapter_partitionings[adapter];
1230
1231
// Instantiates the adapter module if it hasn't already been
1232
// instantiated or otherwise returns the index that the module was
1233
// already instantiated at.
1234
let instance = self.adapter_module(adapter_module);
1235
1236
// This adapter is always an export of the instance.
1237
info::CoreExport {
1238
instance,
1239
item: ExportItem::Index(entity_index),
1240
}
1241
}
1242
1243
fn adapter_module(&mut self, adapter_module: AdapterModuleId) -> RuntimeInstanceIndex {
1244
self.intern(
1245
RuntimeInstance::Adapter(adapter_module),
1246
|me| &mut me.runtime_instances,
1247
|me, _| {
1248
log::debug!("instantiating {adapter_module:?}");
1249
let (module_index, args) = &me.dfg.adapter_modules[adapter_module];
1250
let args = args.iter().map(|arg| me.core_def(arg)).collect();
1251
let instantiate = InstantiateModule::Static(*module_index, args);
1252
GlobalInitializer::InstantiateModule(instantiate, None)
1253
},
1254
|_, init| init,
1255
)
1256
}
1257
1258
/// Helper function to manage interning of results to avoid duplicate
1259
/// initializers being inserted into the final list.
1260
///
1261
/// * `key` - the key being referenced which is used to deduplicate.
1262
/// * `map` - a closure to access the interning map on `Self`
1263
/// * `gen` - a closure to generate an intermediate value with `Self` from
1264
/// `K`. This is only used if `key` hasn't previously been seen. This
1265
/// closure can recursively intern other values possibly.
1266
/// * `init` - a closure to use the result of `gen` to create the final
1267
/// initializer now that the index `V` of the runtime item is known.
1268
///
1269
/// This is used by all the other interning methods above to lazily append
1270
/// initializers on-demand and avoid pushing more than one initializer at a
1271
/// time.
1272
fn intern<K, V, T>(
1273
&mut self,
1274
key: K,
1275
map: impl Fn(&mut Self) -> &mut HashMap<K, V>,
1276
generate: impl FnOnce(&mut Self, K) -> T,
1277
init: impl FnOnce(V, T) -> GlobalInitializer,
1278
) -> V
1279
where
1280
K: Hash + Eq + Copy,
1281
V: EntityRef,
1282
{
1283
self.intern_(key, map, generate, |me, key, val| {
1284
me.initializers.push(init(key, val));
1285
})
1286
}
1287
1288
fn intern_no_init<K, V, T>(
1289
&mut self,
1290
key: K,
1291
map: impl Fn(&mut Self) -> &mut HashMap<K, V>,
1292
generate: impl FnOnce(&mut Self, K) -> T,
1293
) -> V
1294
where
1295
K: Hash + Eq + Copy,
1296
V: EntityRef,
1297
{
1298
self.intern_(key, map, generate, |_me, _key, _val| {})
1299
}
1300
1301
fn intern_<K, V, T>(
1302
&mut self,
1303
key: K,
1304
map: impl Fn(&mut Self) -> &mut HashMap<K, V>,
1305
generate: impl FnOnce(&mut Self, K) -> T,
1306
init: impl FnOnce(&mut Self, V, T),
1307
) -> V
1308
where
1309
K: Hash + Eq + Copy,
1310
V: EntityRef,
1311
{
1312
if let Some(val) = map(self).get(&key) {
1313
return *val;
1314
}
1315
let tmp = generate(self, key);
1316
let index = V::new(map(self).len());
1317
init(self, index, tmp);
1318
let prev = map(self).insert(key, index);
1319
assert!(prev.is_none());
1320
index
1321
}
1322
}
1323
1324