Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/environ/src/component/translate/inline.rs
1692 views
1
//! Implementation of "inlining" a component into a flat list of initializers.
2
//!
3
//! After the first phase of compiling a component we're left with a single
4
//! root `Translation` for the original component along with a "static" list of
5
//! child components. Each `Translation` has a list of `LocalInitializer` items
6
//! inside of it which is a primitive representation of how the component
7
//! should be constructed with effectively one initializer per item in the
8
//! index space of a component. This "local initializer" list would be
9
//! relatively inefficient to process at runtime and more importantly doesn't
10
//! convey enough information to understand what trampolines need to be
11
//! compiled or what fused adapters need to be generated. This consequently is
12
//! the motivation for this file.
13
//!
14
//! The second phase of compilation, inlining here, will in a sense interpret
15
//! the initializers, at compile time, into a new list of `GlobalInitializer` entries
16
//! which are a sort of "global initializer". The generated `GlobalInitializer` is
17
//! much more specific than the `LocalInitializer` and additionally far fewer
18
//! `GlobalInitializer` structures are generated (in theory) than there are local
19
//! initializers.
20
//!
21
//! The "inlining" portion of the name of this module indicates how the
22
//! instantiation of a component is interpreted as calling a function. The
23
//! function's arguments are the imports provided to the instantiation of a
24
//! component, and further nested function calls happen on a stack when a
25
//! nested component is instantiated. The inlining then refers to how this
26
//! stack of instantiations is flattened to one list of `GlobalInitializer`
27
//! entries to represent the process of instantiating a component graph,
28
//! similar to how function inlining removes call instructions and creates one
29
//! giant function for a call graph. Here there are no inlining heuristics or
30
//! anything like that, we simply inline everything into the root component's
31
//! list of initializers.
32
//!
33
//! Another primary task this module performs is a form of dataflow analysis
34
//! to represent items in each index space with their definition rather than
35
//! references of relative indices. These definitions (all the `*Def` types in
36
//! this module) are not local to any one nested component and instead
37
//! represent state available at runtime tracked in the final `Component`
38
//! produced.
39
//!
40
//! With all this pieced together the general idea is relatively
41
//! straightforward. All of a component's initializers are processed in sequence
42
//! where instantiating a nested component pushes a "frame" onto a stack to
43
//! start executing and we resume at the old one when we're done. Items are
44
//! tracked where they come from and at the end after processing only the
45
//! side-effectful initializers are emitted to the `GlobalInitializer` list in the
46
//! final `Component`.
47
48
use crate::component::translate::*;
49
use crate::{EntityType, IndexType};
50
use std::borrow::Cow;
51
use wasmparser::component_types::{ComponentAnyTypeId, ComponentCoreModuleTypeId};
52
53
pub(super) fn run(
54
types: &mut ComponentTypesBuilder,
55
result: &Translation<'_>,
56
nested_modules: &PrimaryMap<StaticModuleIndex, ModuleTranslation<'_>>,
57
nested_components: &PrimaryMap<StaticComponentIndex, Translation<'_>>,
58
) -> Result<dfg::ComponentDfg> {
59
let mut inliner = Inliner {
60
nested_modules,
61
nested_components,
62
result: Default::default(),
63
import_path_interner: Default::default(),
64
runtime_instances: PrimaryMap::default(),
65
};
66
67
let index = RuntimeComponentInstanceIndex::from_u32(0);
68
69
// The initial arguments to the root component are all host imports. This
70
// means that they're all using the `ComponentItemDef::Host` variant. Here
71
// an `ImportIndex` is allocated for each item and then the argument is
72
// recorded.
73
//
74
// Note that this is represents the abstract state of a host import of an
75
// item since we don't know the precise structure of the host import.
76
let mut args = HashMap::with_capacity(result.exports.len());
77
let mut path = Vec::new();
78
types.resources_mut().set_current_instance(index);
79
let types_ref = result.types_ref();
80
for init in result.initializers.iter() {
81
let (name, ty) = match *init {
82
LocalInitializer::Import(name, ty) => (name, ty),
83
_ => continue,
84
};
85
86
// Before `convert_component_entity_type` below all resource types
87
// introduced by this import need to be registered and have indexes
88
// assigned to them. Any fresh new resource type referred to by imports
89
// is a brand new introduction of a resource which needs to have a type
90
// allocated to it, so new runtime imports are injected for each
91
// resource along with updating the `imported_resources` map.
92
let index = inliner.result.import_types.next_key();
93
types.resources_mut().register_component_entity_type(
94
&types_ref,
95
ty,
96
&mut path,
97
&mut |path| {
98
let index = inliner.runtime_import(&ImportPath {
99
index,
100
path: path.iter().copied().map(Into::into).collect(),
101
});
102
inliner.result.imported_resources.push(index)
103
},
104
);
105
106
// With resources all taken care of it's now possible to convert this
107
// into Wasmtime's type system.
108
let ty = types.convert_component_entity_type(types_ref, ty)?;
109
110
// Imports of types that aren't resources are not required to be
111
// specified by the host since it's just for type information within
112
// the component.
113
if let TypeDef::Interface(_) = ty {
114
continue;
115
}
116
let index = inliner.result.import_types.push((name.0.to_string(), ty));
117
let path = ImportPath::root(index);
118
args.insert(name.0, ComponentItemDef::from_import(path, ty)?);
119
}
120
121
// This will run the inliner to completion after being seeded with the
122
// initial frame. When the inliner finishes it will return the exports of
123
// the root frame which are then used for recording the exports of the
124
// component.
125
inliner.result.num_runtime_component_instances += 1;
126
let frame = InlinerFrame::new(index, result, ComponentClosure::default(), args, None);
127
let resources_snapshot = types.resources_mut().clone();
128
let mut frames = vec![(frame, resources_snapshot)];
129
let exports = inliner.run(types, &mut frames)?;
130
assert!(frames.is_empty());
131
132
let mut export_map = Default::default();
133
for (name, def) in exports {
134
inliner.record_export(name, def, types, &mut export_map)?;
135
}
136
inliner.result.exports = export_map;
137
inliner.result.num_future_tables = types.num_future_tables();
138
inliner.result.num_stream_tables = types.num_stream_tables();
139
inliner.result.num_error_context_tables = types.num_error_context_tables();
140
141
Ok(inliner.result)
142
}
143
144
struct Inliner<'a> {
145
/// The list of static modules that were found during initial translation of
146
/// the component.
147
///
148
/// This is used during the instantiation of these modules to ahead-of-time
149
/// order the arguments precisely according to what the module is defined as
150
/// needing which avoids the need to do string lookups or permute arguments
151
/// at runtime.
152
nested_modules: &'a PrimaryMap<StaticModuleIndex, ModuleTranslation<'a>>,
153
154
/// The list of static components that were found during initial translation of
155
/// the component.
156
///
157
/// This is used when instantiating nested components to push a new
158
/// `InlinerFrame` with the `Translation`s here.
159
nested_components: &'a PrimaryMap<StaticComponentIndex, Translation<'a>>,
160
161
/// The final `Component` that is being constructed and returned from this
162
/// inliner.
163
result: dfg::ComponentDfg,
164
165
// Maps used to "intern" various runtime items to only save them once at
166
// runtime instead of multiple times.
167
import_path_interner: HashMap<ImportPath<'a>, RuntimeImportIndex>,
168
169
/// Origin information about where each runtime instance came from
170
runtime_instances: PrimaryMap<dfg::InstanceId, InstanceModule>,
171
}
172
173
/// A "stack frame" as part of the inlining process, or the progress through
174
/// instantiating a component.
175
///
176
/// All instantiations of a component will create an `InlinerFrame` and are
177
/// incrementally processed via the `initializers` list here. Note that the
178
/// inliner frames are stored on the heap to avoid recursion based on user
179
/// input.
180
struct InlinerFrame<'a> {
181
instance: RuntimeComponentInstanceIndex,
182
183
/// The remaining initializers to process when instantiating this component.
184
initializers: std::slice::Iter<'a, LocalInitializer<'a>>,
185
186
/// The component being instantiated.
187
translation: &'a Translation<'a>,
188
189
/// The "closure arguments" to this component, or otherwise the maps indexed
190
/// by `ModuleUpvarIndex` and `ComponentUpvarIndex`. This is created when
191
/// a component is created and stored as part of a component's state during
192
/// inlining.
193
closure: ComponentClosure<'a>,
194
195
/// The arguments to the creation of this component.
196
///
197
/// At the root level these are all imports from the host and between
198
/// components this otherwise tracks how all the arguments are defined.
199
args: HashMap<&'a str, ComponentItemDef<'a>>,
200
201
// core wasm index spaces
202
funcs: PrimaryMap<FuncIndex, (ModuleInternedTypeIndex, dfg::CoreDef)>,
203
memories: PrimaryMap<MemoryIndex, dfg::CoreExport<EntityIndex>>,
204
tables: PrimaryMap<TableIndex, dfg::CoreExport<EntityIndex>>,
205
globals: PrimaryMap<GlobalIndex, dfg::CoreExport<EntityIndex>>,
206
tags: PrimaryMap<GlobalIndex, dfg::CoreExport<EntityIndex>>,
207
modules: PrimaryMap<ModuleIndex, ModuleDef<'a>>,
208
209
// component model index spaces
210
component_funcs: PrimaryMap<ComponentFuncIndex, ComponentFuncDef<'a>>,
211
module_instances: PrimaryMap<ModuleInstanceIndex, ModuleInstanceDef<'a>>,
212
component_instances: PrimaryMap<ComponentInstanceIndex, ComponentInstanceDef<'a>>,
213
components: PrimaryMap<ComponentIndex, ComponentDef<'a>>,
214
215
/// The type of instance produced by completing the instantiation of this
216
/// frame.
217
///
218
/// This is a wasmparser-relative piece of type information which is used to
219
/// register resource types after instantiation has completed.
220
///
221
/// This is `Some` for all subcomponents and `None` for the root component.
222
instance_ty: Option<ComponentInstanceTypeId>,
223
}
224
225
/// "Closure state" for a component which is resolved from the `ClosedOverVars`
226
/// state that was calculated during translation.
227
//
228
// FIXME: this is cloned quite a lot and given the internal maps if this is a
229
// perf issue we may want to `Rc` these fields. Note that this is only a perf
230
// hit at compile-time though which we in general don't pay too much
231
// attention to.
232
#[derive(Default, Clone)]
233
struct ComponentClosure<'a> {
234
modules: PrimaryMap<ModuleUpvarIndex, ModuleDef<'a>>,
235
components: PrimaryMap<ComponentUpvarIndex, ComponentDef<'a>>,
236
}
237
238
/// Representation of a "path" into an import.
239
///
240
/// Imports from the host at this time are one of three things:
241
///
242
/// * Functions
243
/// * Core wasm modules
244
/// * "Instances" of these three items
245
///
246
/// The "base" values are functions and core wasm modules, but the abstraction
247
/// of an instance allows embedding functions/modules deeply within other
248
/// instances. This "path" represents optionally walking through a host instance
249
/// to get to the final desired item. At runtime instances are just maps of
250
/// values and so this is used to ensure that we primarily only deal with
251
/// individual functions and modules instead of synthetic instances.
252
#[derive(Clone, PartialEq, Hash, Eq)]
253
struct ImportPath<'a> {
254
index: ImportIndex,
255
path: Vec<Cow<'a, str>>,
256
}
257
258
/// Representation of all items which can be defined within a component.
259
///
260
/// This is the "value" of an item defined within a component and is used to
261
/// represent both imports and exports.
262
#[derive(Clone)]
263
enum ComponentItemDef<'a> {
264
Component(ComponentDef<'a>),
265
Instance(ComponentInstanceDef<'a>),
266
Func(ComponentFuncDef<'a>),
267
Module(ModuleDef<'a>),
268
Type(TypeDef),
269
}
270
271
#[derive(Clone)]
272
enum ModuleDef<'a> {
273
/// A core wasm module statically defined within the original component.
274
///
275
/// The `StaticModuleIndex` indexes into the `static_modules` map in the
276
/// `Inliner`.
277
Static(StaticModuleIndex, ComponentCoreModuleTypeId),
278
279
/// A core wasm module that was imported from the host.
280
Import(ImportPath<'a>, TypeModuleIndex),
281
}
282
283
// Note that unlike all other `*Def` types which are not allowed to have local
284
// indices this type does indeed have local indices. That is represented with
285
// the lack of a `Clone` here where once this is created it's never moved across
286
// components because module instances always stick within one component.
287
enum ModuleInstanceDef<'a> {
288
/// A core wasm module instance was created through the instantiation of a
289
/// module.
290
///
291
/// The `RuntimeInstanceIndex` was the index allocated as this was the
292
/// `n`th instantiation and the `ModuleIndex` points into an
293
/// `InlinerFrame`'s local index space.
294
Instantiated(dfg::InstanceId, ModuleIndex),
295
296
/// A "synthetic" core wasm module which is just a bag of named indices.
297
///
298
/// Note that this can really only be used for passing as an argument to
299
/// another module's instantiation and is used to rename arguments locally.
300
Synthetic(&'a HashMap<&'a str, EntityIndex>),
301
}
302
303
#[derive(Clone)]
304
enum ComponentFuncDef<'a> {
305
/// A host-imported component function.
306
Import(ImportPath<'a>),
307
308
/// A core wasm function was lifted into a component function.
309
Lifted {
310
/// The component function type.
311
ty: TypeFuncIndex,
312
/// The core Wasm function.
313
func: dfg::CoreDef,
314
/// Canonical options.
315
options: AdapterOptions,
316
},
317
}
318
319
#[derive(Clone)]
320
enum ComponentInstanceDef<'a> {
321
/// A host-imported instance.
322
///
323
/// This typically means that it's "just" a map of named values. It's not
324
/// actually supported to take a `wasmtime::component::Instance` and pass it
325
/// to another instance at this time.
326
Import(ImportPath<'a>, TypeComponentInstanceIndex),
327
328
/// A concrete map of values.
329
///
330
/// This is used for both instantiated components as well as "synthetic"
331
/// components. This variant can be used for both because both are
332
/// represented by simply a bag of items within the entire component
333
/// instantiation process.
334
//
335
// FIXME: same as the issue on `ComponentClosure` where this is cloned a lot
336
// and may need `Rc`.
337
Items(
338
IndexMap<&'a str, ComponentItemDef<'a>>,
339
TypeComponentInstanceIndex,
340
),
341
}
342
343
#[derive(Clone)]
344
struct ComponentDef<'a> {
345
index: StaticComponentIndex,
346
closure: ComponentClosure<'a>,
347
}
348
349
impl<'a> Inliner<'a> {
350
/// Symbolically instantiates a component using the type information and
351
/// `frames` provided.
352
///
353
/// The `types` provided is the type information for the entire component
354
/// translation process. This is a distinct output artifact separate from
355
/// the component metadata.
356
///
357
/// The `frames` argument is storage to handle a "call stack" of components
358
/// instantiating one another. The youngest frame (last element) of the
359
/// frames list is a component that's currently having its initializers
360
/// processed. The second element of each frame is a snapshot of the
361
/// resource-related information just before the frame was translated. For
362
/// more information on this snapshotting see the documentation on
363
/// `ResourcesBuilder`.
364
fn run(
365
&mut self,
366
types: &mut ComponentTypesBuilder,
367
frames: &mut Vec<(InlinerFrame<'a>, ResourcesBuilder)>,
368
) -> Result<IndexMap<&'a str, ComponentItemDef<'a>>> {
369
// This loop represents the execution of the instantiation of a
370
// component. This is an iterative process which is finished once all
371
// initializers are processed. Currently this is modeled as an infinite
372
// loop which drives the top-most iterator of the `frames` stack
373
// provided as an argument to this function.
374
loop {
375
let (frame, _) = frames.last_mut().unwrap();
376
types.resources_mut().set_current_instance(frame.instance);
377
match frame.initializers.next() {
378
// Process the initializer and if it started the instantiation
379
// of another component then we push that frame on the stack to
380
// continue onwards.
381
Some(init) => match self.initializer(frame, types, init)? {
382
Some(new_frame) => {
383
frames.push((new_frame, types.resources_mut().clone()));
384
}
385
None => {}
386
},
387
388
// If there are no more initializers for this frame then the
389
// component it represents has finished instantiation. The
390
// exports of the component are collected and then the entire
391
// frame is discarded. The exports are then either pushed in the
392
// parent frame, if any, as a new component instance or they're
393
// returned from this function for the root set of exports.
394
None => {
395
let exports = frame
396
.translation
397
.exports
398
.iter()
399
.map(|(name, item)| Ok((*name, frame.item(*item, types)?)))
400
.collect::<Result<_>>()?;
401
let instance_ty = frame.instance_ty;
402
let (_, snapshot) = frames.pop().unwrap();
403
*types.resources_mut() = snapshot;
404
match frames.last_mut() {
405
Some((parent, _)) => {
406
parent.finish_instantiate(exports, instance_ty.unwrap(), types)?;
407
}
408
None => break Ok(exports),
409
}
410
}
411
}
412
}
413
}
414
415
fn initializer(
416
&mut self,
417
frame: &mut InlinerFrame<'a>,
418
types: &mut ComponentTypesBuilder,
419
initializer: &'a LocalInitializer,
420
) -> Result<Option<InlinerFrame<'a>>> {
421
use LocalInitializer::*;
422
423
match initializer {
424
// When a component imports an item the actual definition of the
425
// item is looked up here (not at runtime) via its name. The
426
// arguments provided in our `InlinerFrame` describe how each
427
// argument was defined, so we simply move it from there into the
428
// correct index space.
429
//
430
// Note that for the root component this will add `*::Import` items
431
// but for sub-components this will do resolution to connect what
432
// was provided as an import at the instantiation-site to what was
433
// needed during the component's instantiation.
434
Import(name, ty) => {
435
let arg = match frame.args.get(name.0) {
436
Some(arg) => arg,
437
438
// Not all arguments need to be provided for instantiation,
439
// namely the root component in Wasmtime doesn't require
440
// structural type imports to be satisfied. These type
441
// imports are relevant for bindings generators and such but
442
// as a runtime there's not really a definition to fit in.
443
//
444
// If no argument was provided for `name` then it's asserted
445
// that this is a type import and additionally it's not a
446
// resource type import (which indeed must be provided). If
447
// all that passes then this initializer is effectively
448
// skipped.
449
None => {
450
match ty {
451
ComponentEntityType::Type {
452
created: ComponentAnyTypeId::Resource(_),
453
..
454
} => unreachable!(),
455
ComponentEntityType::Type { .. } => {}
456
_ => unreachable!(),
457
}
458
return Ok(None);
459
}
460
};
461
462
// Next resource types need to be handled. For example if a
463
// resource is imported into this component then it needs to be
464
// assigned a unique table to provide the isolation guarantees
465
// of resources (this component's table is shared with no
466
// others). Here `register_component_entity_type` will find
467
// imported resources and then `lookup_resource` will find the
468
// resource within `arg` as necessary to lookup the original
469
// true definition of this resource.
470
//
471
// This is what enables tracking true resource origins
472
// throughout component translation while simultaneously also
473
// tracking unique tables for each resource in each component.
474
let mut path = Vec::new();
475
let (resources, types) = types.resources_mut_and_types();
476
resources.register_component_entity_type(
477
&frame.translation.types_ref(),
478
*ty,
479
&mut path,
480
&mut |path| arg.lookup_resource(path, types),
481
);
482
483
// And now with all the type information out of the way the
484
// `arg` definition is moved into its corresponding index space.
485
frame.push_item(arg.clone());
486
}
487
488
// Lowering a component function to a core wasm function is
489
// generally what "triggers compilation". Here various metadata is
490
// recorded and then the final component gets an initializer
491
// recording the lowering.
492
//
493
// NB: at this time only lowered imported functions are supported.
494
Lower {
495
func,
496
options,
497
lower_ty,
498
} => {
499
let lower_ty =
500
types.convert_component_func_type(frame.translation.types_ref(), *lower_ty)?;
501
let options_lower = self.adapter_options(frame, types, options);
502
let lower_core_type = options_lower.core_type;
503
let func = match &frame.component_funcs[*func] {
504
// If this component function was originally a host import
505
// then this is a lowered host function which needs a
506
// trampoline to enter WebAssembly. That's recorded here
507
// with all relevant information.
508
ComponentFuncDef::Import(path) => {
509
let import = self.runtime_import(path);
510
let options = self.canonical_options(options_lower);
511
let index = self.result.trampolines.push((
512
lower_core_type,
513
dfg::Trampoline::LowerImport {
514
import,
515
options,
516
lower_ty,
517
},
518
));
519
dfg::CoreDef::Trampoline(index)
520
}
521
522
// This case handles when a lifted function is later
523
// lowered, and both the lowering and the lifting are
524
// happening within the same component instance.
525
//
526
// In this situation if the `canon.lower`'d function is
527
// called then it immediately sets `may_enter` to `false`.
528
// When calling the callee, however, that's `canon.lift`
529
// which immediately traps if `may_enter` is `false`. That
530
// means that this pairing of functions creates a function
531
// that always traps.
532
//
533
// When closely reading the spec though the precise trap
534
// that comes out can be somewhat variable. Technically the
535
// function yielded here is one that should validate the
536
// arguments by lifting them, and then trap. This means that
537
// the trap could be different depending on whether all
538
// arguments are valid for now. This was discussed in
539
// WebAssembly/component-model#51 somewhat and the
540
// conclusion was that we can probably get away with "always
541
// trap" here.
542
//
543
// The `CoreDef::AlwaysTrap` variant here is used to
544
// indicate that this function is valid but if something
545
// actually calls it then it just generates a trap
546
// immediately.
547
ComponentFuncDef::Lifted {
548
options: options_lift,
549
..
550
} if options_lift.instance == options_lower.instance => {
551
let index = self
552
.result
553
.trampolines
554
.push((lower_core_type, dfg::Trampoline::AlwaysTrap));
555
dfg::CoreDef::Trampoline(index)
556
}
557
558
// Lowering a lifted function where the destination
559
// component is different than the source component means
560
// that a "fused adapter" was just identified.
561
//
562
// Metadata about this fused adapter is recorded in the
563
// `Adapters` output of this compilation pass. Currently the
564
// implementation of fused adapters is to generate a core
565
// wasm module which is instantiated with relevant imports
566
// and the exports are used as the fused adapters. At this
567
// time we don't know when precisely the instance will be
568
// created but we do know that the result of this will be an
569
// export from a previously-created instance.
570
//
571
// To model this the result of this arm is a
572
// `CoreDef::Export`. The actual indices listed within the
573
// export are "fake indices" in the sense of they're not
574
// resolved yet. This resolution will happen at a later
575
// compilation phase. Any usages of the `CoreDef::Export`
576
// here will be detected and rewritten to an actual runtime
577
// instance created.
578
//
579
// The `instance` field of the `CoreExport` has a marker
580
// which indicates that it's a fused adapter. The `item` is
581
// a function where the function index corresponds to the
582
// `adapter_idx` which contains the metadata about this
583
// adapter being created. The metadata is used to learn
584
// about the dependencies and when the adapter module can
585
// be instantiated.
586
ComponentFuncDef::Lifted {
587
ty: lift_ty,
588
func,
589
options: options_lift,
590
} => {
591
let adapter_idx = self.result.adapters.push(Adapter {
592
lift_ty: *lift_ty,
593
lift_options: options_lift.clone(),
594
lower_ty,
595
lower_options: options_lower,
596
func: func.clone(),
597
});
598
dfg::CoreDef::Adapter(adapter_idx)
599
}
600
};
601
frame.funcs.push((lower_core_type, func));
602
}
603
604
// Lifting a core wasm function is relatively easy for now in that
605
// some metadata about the lifting is simply recorded. This'll get
606
// plumbed through to exports or a fused adapter later on.
607
Lift(ty, func, options) => {
608
let ty = types.convert_component_func_type(frame.translation.types_ref(), *ty)?;
609
let options = self.adapter_options(frame, types, options);
610
let func = frame.funcs[*func].1.clone();
611
frame
612
.component_funcs
613
.push(ComponentFuncDef::Lifted { ty, func, options });
614
}
615
616
// A new resource type is being introduced, so it's recorded as a
617
// brand new resource in the final `resources` array. Additionally
618
// for now resource introductions are considered side effects to
619
// know when to register their destructors so that's recorded as
620
// well.
621
//
622
// Note that this has the effect of when a component is instantiated
623
// twice it will produce unique types for the resources from each
624
// instantiation. That's the intended runtime semantics and
625
// implementation here, however.
626
Resource(ty, rep, dtor) => {
627
let idx = self.result.resources.push(dfg::Resource {
628
rep: *rep,
629
dtor: dtor.map(|i| frame.funcs[i].1.clone()),
630
instance: frame.instance,
631
});
632
self.result
633
.side_effects
634
.push(dfg::SideEffect::Resource(idx));
635
636
// Register with type translation that all future references to
637
// `ty` will refer to `idx`.
638
//
639
// Note that this registration information is lost when this
640
// component finishes instantiation due to the snapshotting
641
// behavior in the frame processing loop above. This is also
642
// intended, though, since `ty` can't be referred to outside of
643
// this component.
644
let idx = self.result.resource_index(idx);
645
types.resources_mut().register_resource(ty.resource(), idx);
646
}
647
648
// Resource-related intrinsics are generally all the same.
649
// Wasmparser type information is converted to wasmtime type
650
// information and then new entries for each intrinsic are recorded.
651
ResourceNew(id, ty) => {
652
let id = types.resource_id(id.resource());
653
let index = self
654
.result
655
.trampolines
656
.push((*ty, dfg::Trampoline::ResourceNew(id)));
657
frame.funcs.push((*ty, dfg::CoreDef::Trampoline(index)));
658
}
659
ResourceRep(id, ty) => {
660
let id = types.resource_id(id.resource());
661
let index = self
662
.result
663
.trampolines
664
.push((*ty, dfg::Trampoline::ResourceRep(id)));
665
frame.funcs.push((*ty, dfg::CoreDef::Trampoline(index)));
666
}
667
ResourceDrop(id, ty) => {
668
let id = types.resource_id(id.resource());
669
let index = self
670
.result
671
.trampolines
672
.push((*ty, dfg::Trampoline::ResourceDrop(id)));
673
frame.funcs.push((*ty, dfg::CoreDef::Trampoline(index)));
674
}
675
BackpressureSet { func } => {
676
let index = self.result.trampolines.push((
677
*func,
678
dfg::Trampoline::BackpressureSet {
679
instance: frame.instance,
680
},
681
));
682
frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));
683
}
684
TaskReturn { result, options } => {
685
let results = result
686
.iter()
687
.map(|ty| types.valtype(frame.translation.types_ref(), ty))
688
.collect::<Result<_>>()?;
689
let results = types.new_tuple_type(results);
690
let func = options.core_type;
691
let options = self.adapter_options(frame, types, options);
692
let options = self.canonical_options(options);
693
let index = self
694
.result
695
.trampolines
696
.push((func, dfg::Trampoline::TaskReturn { results, options }));
697
frame.funcs.push((func, dfg::CoreDef::Trampoline(index)));
698
}
699
TaskCancel { func } => {
700
let index = self.result.trampolines.push((
701
*func,
702
dfg::Trampoline::TaskCancel {
703
instance: frame.instance,
704
},
705
));
706
frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));
707
}
708
WaitableSetNew { func } => {
709
let index = self.result.trampolines.push((
710
*func,
711
dfg::Trampoline::WaitableSetNew {
712
instance: frame.instance,
713
},
714
));
715
frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));
716
}
717
WaitableSetWait { options } => {
718
let func = options.core_type;
719
let options = self.adapter_options(frame, types, options);
720
let options = self.canonical_options(options);
721
let index = self
722
.result
723
.trampolines
724
.push((func, dfg::Trampoline::WaitableSetWait { options }));
725
frame.funcs.push((func, dfg::CoreDef::Trampoline(index)));
726
}
727
WaitableSetPoll { options } => {
728
let func = options.core_type;
729
let options = self.adapter_options(frame, types, options);
730
let options = self.canonical_options(options);
731
let index = self
732
.result
733
.trampolines
734
.push((func, dfg::Trampoline::WaitableSetPoll { options }));
735
frame.funcs.push((func, dfg::CoreDef::Trampoline(index)));
736
}
737
WaitableSetDrop { func } => {
738
let index = self.result.trampolines.push((
739
*func,
740
dfg::Trampoline::WaitableSetDrop {
741
instance: frame.instance,
742
},
743
));
744
frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));
745
}
746
WaitableJoin { func } => {
747
let index = self.result.trampolines.push((
748
*func,
749
dfg::Trampoline::WaitableJoin {
750
instance: frame.instance,
751
},
752
));
753
frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));
754
}
755
Yield { func, async_ } => {
756
let index = self
757
.result
758
.trampolines
759
.push((*func, dfg::Trampoline::Yield { async_: *async_ }));
760
frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));
761
}
762
SubtaskDrop { func } => {
763
let index = self.result.trampolines.push((
764
*func,
765
dfg::Trampoline::SubtaskDrop {
766
instance: frame.instance,
767
},
768
));
769
frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));
770
}
771
SubtaskCancel { func, async_ } => {
772
let index = self.result.trampolines.push((
773
*func,
774
dfg::Trampoline::SubtaskCancel {
775
instance: frame.instance,
776
async_: *async_,
777
},
778
));
779
frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));
780
}
781
StreamNew { ty, func } => {
782
let InterfaceType::Stream(ty) =
783
types.defined_type(frame.translation.types_ref(), *ty)?
784
else {
785
unreachable!()
786
};
787
let index = self
788
.result
789
.trampolines
790
.push((*func, dfg::Trampoline::StreamNew { ty }));
791
frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));
792
}
793
StreamRead { ty, options } => {
794
let InterfaceType::Stream(ty) =
795
types.defined_type(frame.translation.types_ref(), *ty)?
796
else {
797
unreachable!()
798
};
799
let func = options.core_type;
800
let options = self.adapter_options(frame, types, options);
801
let options = self.canonical_options(options);
802
let index = self
803
.result
804
.trampolines
805
.push((func, dfg::Trampoline::StreamRead { ty, options }));
806
frame.funcs.push((func, dfg::CoreDef::Trampoline(index)));
807
}
808
StreamWrite { ty, options } => {
809
let InterfaceType::Stream(ty) =
810
types.defined_type(frame.translation.types_ref(), *ty)?
811
else {
812
unreachable!()
813
};
814
let func = options.core_type;
815
let options = self.adapter_options(frame, types, options);
816
let options = self.canonical_options(options);
817
let index = self
818
.result
819
.trampolines
820
.push((func, dfg::Trampoline::StreamWrite { ty, options }));
821
frame.funcs.push((func, dfg::CoreDef::Trampoline(index)));
822
}
823
StreamCancelRead { ty, func, async_ } => {
824
let InterfaceType::Stream(ty) =
825
types.defined_type(frame.translation.types_ref(), *ty)?
826
else {
827
unreachable!()
828
};
829
let index = self.result.trampolines.push((
830
*func,
831
dfg::Trampoline::StreamCancelRead {
832
ty,
833
async_: *async_,
834
},
835
));
836
frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));
837
}
838
StreamCancelWrite { ty, func, async_ } => {
839
let InterfaceType::Stream(ty) =
840
types.defined_type(frame.translation.types_ref(), *ty)?
841
else {
842
unreachable!()
843
};
844
let index = self.result.trampolines.push((
845
*func,
846
dfg::Trampoline::StreamCancelWrite {
847
ty,
848
async_: *async_,
849
},
850
));
851
frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));
852
}
853
StreamDropReadable { ty, func } => {
854
let InterfaceType::Stream(ty) =
855
types.defined_type(frame.translation.types_ref(), *ty)?
856
else {
857
unreachable!()
858
};
859
let index = self
860
.result
861
.trampolines
862
.push((*func, dfg::Trampoline::StreamDropReadable { ty }));
863
frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));
864
}
865
StreamDropWritable { ty, func } => {
866
let InterfaceType::Stream(ty) =
867
types.defined_type(frame.translation.types_ref(), *ty)?
868
else {
869
unreachable!()
870
};
871
let index = self
872
.result
873
.trampolines
874
.push((*func, dfg::Trampoline::StreamDropWritable { ty }));
875
frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));
876
}
877
FutureNew { ty, func } => {
878
let InterfaceType::Future(ty) =
879
types.defined_type(frame.translation.types_ref(), *ty)?
880
else {
881
unreachable!()
882
};
883
let index = self
884
.result
885
.trampolines
886
.push((*func, dfg::Trampoline::FutureNew { ty }));
887
frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));
888
}
889
FutureRead { ty, options } => {
890
let InterfaceType::Future(ty) =
891
types.defined_type(frame.translation.types_ref(), *ty)?
892
else {
893
unreachable!()
894
};
895
let func = options.core_type;
896
let options = self.adapter_options(frame, types, options);
897
let options = self.canonical_options(options);
898
let index = self
899
.result
900
.trampolines
901
.push((func, dfg::Trampoline::FutureRead { ty, options }));
902
frame.funcs.push((func, dfg::CoreDef::Trampoline(index)));
903
}
904
FutureWrite { ty, options } => {
905
let InterfaceType::Future(ty) =
906
types.defined_type(frame.translation.types_ref(), *ty)?
907
else {
908
unreachable!()
909
};
910
let func = options.core_type;
911
let options = self.adapter_options(frame, types, options);
912
let options = self.canonical_options(options);
913
let index = self
914
.result
915
.trampolines
916
.push((func, dfg::Trampoline::FutureWrite { ty, options }));
917
frame.funcs.push((func, dfg::CoreDef::Trampoline(index)));
918
}
919
FutureCancelRead { ty, func, async_ } => {
920
let InterfaceType::Future(ty) =
921
types.defined_type(frame.translation.types_ref(), *ty)?
922
else {
923
unreachable!()
924
};
925
let index = self.result.trampolines.push((
926
*func,
927
dfg::Trampoline::FutureCancelRead {
928
ty,
929
async_: *async_,
930
},
931
));
932
frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));
933
}
934
FutureCancelWrite { ty, func, async_ } => {
935
let InterfaceType::Future(ty) =
936
types.defined_type(frame.translation.types_ref(), *ty)?
937
else {
938
unreachable!()
939
};
940
let index = self.result.trampolines.push((
941
*func,
942
dfg::Trampoline::FutureCancelWrite {
943
ty,
944
async_: *async_,
945
},
946
));
947
frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));
948
}
949
FutureDropReadable { ty, func } => {
950
let InterfaceType::Future(ty) =
951
types.defined_type(frame.translation.types_ref(), *ty)?
952
else {
953
unreachable!()
954
};
955
let index = self
956
.result
957
.trampolines
958
.push((*func, dfg::Trampoline::FutureDropReadable { ty }));
959
frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));
960
}
961
FutureDropWritable { ty, func } => {
962
let InterfaceType::Future(ty) =
963
types.defined_type(frame.translation.types_ref(), *ty)?
964
else {
965
unreachable!()
966
};
967
let index = self
968
.result
969
.trampolines
970
.push((*func, dfg::Trampoline::FutureDropWritable { ty }));
971
frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));
972
}
973
ErrorContextNew { options } => {
974
let ty = types.error_context_table_type()?;
975
let func = options.core_type;
976
let options = self.adapter_options(frame, types, options);
977
let options = self.canonical_options(options);
978
let index = self
979
.result
980
.trampolines
981
.push((func, dfg::Trampoline::ErrorContextNew { ty, options }));
982
frame.funcs.push((func, dfg::CoreDef::Trampoline(index)));
983
}
984
ErrorContextDebugMessage { options } => {
985
let ty = types.error_context_table_type()?;
986
let func = options.core_type;
987
let options = self.adapter_options(frame, types, options);
988
let options = self.canonical_options(options);
989
let index = self.result.trampolines.push((
990
func,
991
dfg::Trampoline::ErrorContextDebugMessage { ty, options },
992
));
993
frame.funcs.push((func, dfg::CoreDef::Trampoline(index)));
994
}
995
ErrorContextDrop { func } => {
996
let ty = types.error_context_table_type()?;
997
let index = self
998
.result
999
.trampolines
1000
.push((*func, dfg::Trampoline::ErrorContextDrop { ty }));
1001
frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));
1002
}
1003
ContextGet { func, i } => {
1004
let index = self
1005
.result
1006
.trampolines
1007
.push((*func, dfg::Trampoline::ContextGet(*i)));
1008
frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));
1009
}
1010
ContextSet { func, i } => {
1011
let index = self
1012
.result
1013
.trampolines
1014
.push((*func, dfg::Trampoline::ContextSet(*i)));
1015
frame.funcs.push((*func, dfg::CoreDef::Trampoline(index)));
1016
}
1017
1018
ModuleStatic(idx, ty) => {
1019
frame.modules.push(ModuleDef::Static(*idx, *ty));
1020
}
1021
1022
// Instantiation of a module is one of the meatier initializers that
1023
// we'll generate. The main magic here is that for a statically
1024
// known module we can order the imports as a list to exactly what
1025
// the static module needs to be instantiated. For imported modules,
1026
// however, the runtime string resolution must happen at runtime so
1027
// that is deferred here by organizing the arguments as a two-layer
1028
// `IndexMap` of what we're providing.
1029
//
1030
// In both cases though a new `RuntimeInstanceIndex` is allocated
1031
// and an initializer is recorded to indicate that it's being
1032
// instantiated.
1033
ModuleInstantiate(module, args) => {
1034
let (instance_module, init) = match &frame.modules[*module] {
1035
ModuleDef::Static(idx, _ty) => {
1036
let mut defs = Vec::new();
1037
for (module, name, _ty) in self.nested_modules[*idx].module.imports() {
1038
let instance = args[module];
1039
defs.push(
1040
self.core_def_of_module_instance_export(frame, instance, name),
1041
);
1042
}
1043
(
1044
InstanceModule::Static(*idx),
1045
dfg::Instance::Static(*idx, defs.into()),
1046
)
1047
}
1048
ModuleDef::Import(path, ty) => {
1049
let mut defs = IndexMap::new();
1050
for ((module, name), _) in types[*ty].imports.iter() {
1051
let instance = args[module.as_str()];
1052
let def =
1053
self.core_def_of_module_instance_export(frame, instance, name);
1054
defs.entry(module.to_string())
1055
.or_insert(IndexMap::new())
1056
.insert(name.to_string(), def);
1057
}
1058
let index = self.runtime_import(path);
1059
(
1060
InstanceModule::Import(*ty),
1061
dfg::Instance::Import(index, defs),
1062
)
1063
}
1064
};
1065
1066
let instance = self.result.instances.push(init);
1067
let instance2 = self.runtime_instances.push(instance_module);
1068
assert_eq!(instance, instance2);
1069
1070
self.result
1071
.side_effects
1072
.push(dfg::SideEffect::Instance(instance));
1073
1074
frame
1075
.module_instances
1076
.push(ModuleInstanceDef::Instantiated(instance, *module));
1077
}
1078
1079
ModuleSynthetic(map) => {
1080
frame
1081
.module_instances
1082
.push(ModuleInstanceDef::Synthetic(map));
1083
}
1084
1085
// This is one of the stages of the "magic" of implementing outer
1086
// aliases to components and modules. For more information on this
1087
// see the documentation on `LexicalScope`. This stage of the
1088
// implementation of outer aliases is where the `ClosedOverVars` is
1089
// transformed into a `ComponentClosure` state using the current
1090
// `InlinerFrame`'s state. This will capture the "runtime" state of
1091
// outer components and upvars and such naturally as part of the
1092
// inlining process.
1093
ComponentStatic(index, vars) => {
1094
frame.components.push(ComponentDef {
1095
index: *index,
1096
closure: ComponentClosure {
1097
modules: vars
1098
.modules
1099
.iter()
1100
.map(|(_, m)| frame.closed_over_module(m))
1101
.collect(),
1102
components: vars
1103
.components
1104
.iter()
1105
.map(|(_, m)| frame.closed_over_component(m))
1106
.collect(),
1107
},
1108
});
1109
}
1110
1111
// Like module instantiation is this is a "meaty" part, and don't be
1112
// fooled by the relative simplicity of this case. This is
1113
// implemented primarily by the `Inliner` structure and the design
1114
// of this entire module, so the "easy" step here is to simply
1115
// create a new inliner frame and return it to get pushed onto the
1116
// stack.
1117
ComponentInstantiate(component, args, ty) => {
1118
let component: &ComponentDef<'a> = &frame.components[*component];
1119
let index = RuntimeComponentInstanceIndex::from_u32(
1120
self.result.num_runtime_component_instances,
1121
);
1122
self.result.num_runtime_component_instances += 1;
1123
let frame = InlinerFrame::new(
1124
index,
1125
&self.nested_components[component.index],
1126
component.closure.clone(),
1127
args.iter()
1128
.map(|(name, item)| Ok((*name, frame.item(*item, types)?)))
1129
.collect::<Result<_>>()?,
1130
Some(*ty),
1131
);
1132
return Ok(Some(frame));
1133
}
1134
1135
ComponentSynthetic(map, ty) => {
1136
let items = map
1137
.iter()
1138
.map(|(name, index)| Ok((*name, frame.item(*index, types)?)))
1139
.collect::<Result<_>>()?;
1140
let types_ref = frame.translation.types_ref();
1141
let ty = types.convert_instance(types_ref, *ty)?;
1142
frame
1143
.component_instances
1144
.push(ComponentInstanceDef::Items(items, ty));
1145
}
1146
1147
// Core wasm aliases, this and the cases below, are creating
1148
// `CoreExport` items primarily to insert into the index space so we
1149
// can create a unique identifier pointing to each core wasm export
1150
// with the instance and relevant index/name as necessary.
1151
AliasExportFunc(instance, name) => {
1152
let (ty, def) = match &frame.module_instances[*instance] {
1153
ModuleInstanceDef::Instantiated(instance, module) => {
1154
let (ty, item) = match &frame.modules[*module] {
1155
ModuleDef::Static(idx, _ty) => {
1156
let entity = self.nested_modules[*idx].module.exports[*name];
1157
let ty = match entity {
1158
EntityIndex::Function(f) => {
1159
self.nested_modules[*idx].module.functions[f]
1160
.signature
1161
.unwrap_module_type_index()
1162
}
1163
_ => unreachable!(),
1164
};
1165
(ty, ExportItem::Index(entity))
1166
}
1167
ModuleDef::Import(_path, module_ty) => {
1168
let module_ty = &types.component_types()[*module_ty];
1169
let entity_ty = &module_ty.exports[&**name];
1170
let ty = entity_ty.unwrap_func().unwrap_module_type_index();
1171
(ty, ExportItem::Name((*name).to_string()))
1172
}
1173
};
1174
let def = dfg::CoreExport {
1175
instance: *instance,
1176
item,
1177
}
1178
.into();
1179
(ty, def)
1180
}
1181
ModuleInstanceDef::Synthetic(instance) => match instance[*name] {
1182
EntityIndex::Function(i) => frame.funcs[i].clone(),
1183
_ => unreachable!(),
1184
},
1185
};
1186
frame.funcs.push((ty, def));
1187
}
1188
1189
AliasExportTable(instance, name) => {
1190
frame.tables.push(
1191
match self.core_def_of_module_instance_export(frame, *instance, *name) {
1192
dfg::CoreDef::Export(e) => e,
1193
_ => unreachable!(),
1194
},
1195
);
1196
}
1197
1198
AliasExportGlobal(instance, name) => {
1199
frame.globals.push(
1200
match self.core_def_of_module_instance_export(frame, *instance, *name) {
1201
dfg::CoreDef::Export(e) => e,
1202
_ => unreachable!(),
1203
},
1204
);
1205
}
1206
1207
AliasExportMemory(instance, name) => {
1208
frame.memories.push(
1209
match self.core_def_of_module_instance_export(frame, *instance, *name) {
1210
dfg::CoreDef::Export(e) => e,
1211
_ => unreachable!(),
1212
},
1213
);
1214
}
1215
1216
AliasExportTag(instance, name) => {
1217
frame.tags.push(
1218
match self.core_def_of_module_instance_export(frame, *instance, *name) {
1219
dfg::CoreDef::Export(e) => e,
1220
_ => unreachable!(),
1221
},
1222
);
1223
}
1224
1225
AliasComponentExport(instance, name) => {
1226
match &frame.component_instances[*instance] {
1227
// Aliasing an export from an imported instance means that
1228
// we're extending the `ImportPath` by one name, represented
1229
// with the clone + push here. Afterwards an appropriate
1230
// item is then pushed in the relevant index space.
1231
ComponentInstanceDef::Import(path, ty) => {
1232
let path = path.push(*name);
1233
let def = ComponentItemDef::from_import(path, types[*ty].exports[*name])?;
1234
frame.push_item(def);
1235
}
1236
1237
// Given a component instance which was either created
1238
// through instantiation of a component or through a
1239
// synthetic renaming of items we just schlep around the
1240
// definitions of various items here.
1241
ComponentInstanceDef::Items(map, _) => frame.push_item(map[*name].clone()),
1242
}
1243
}
1244
1245
// For more information on these see `LexicalScope` but otherwise
1246
// this is just taking a closed over variable and inserting the
1247
// actual definition into the local index space since this
1248
// represents an outer alias to a module/component
1249
AliasModule(idx) => {
1250
frame.modules.push(frame.closed_over_module(idx));
1251
}
1252
AliasComponent(idx) => {
1253
frame.components.push(frame.closed_over_component(idx));
1254
}
1255
1256
Export(item) => match item {
1257
ComponentItem::Func(i) => {
1258
frame
1259
.component_funcs
1260
.push(frame.component_funcs[*i].clone());
1261
}
1262
ComponentItem::Module(i) => {
1263
frame.modules.push(frame.modules[*i].clone());
1264
}
1265
ComponentItem::Component(i) => {
1266
frame.components.push(frame.components[*i].clone());
1267
}
1268
ComponentItem::ComponentInstance(i) => {
1269
frame
1270
.component_instances
1271
.push(frame.component_instances[*i].clone());
1272
}
1273
1274
// Type index spaces aren't maintained during this inlining pass
1275
// so ignore this.
1276
ComponentItem::Type(_) => {}
1277
},
1278
}
1279
1280
Ok(None)
1281
}
1282
1283
/// "Commits" a path of an import to an actual index which is something that
1284
/// will be calculated at runtime.
1285
///
1286
/// Note that the cost of calculating an item for a `RuntimeImportIndex` at
1287
/// runtime is amortized with an `InstancePre` which represents "all the
1288
/// runtime imports are lined up" and after that no more name resolution is
1289
/// necessary.
1290
fn runtime_import(&mut self, path: &ImportPath<'a>) -> RuntimeImportIndex {
1291
*self
1292
.import_path_interner
1293
.entry(path.clone())
1294
.or_insert_with(|| {
1295
self.result.imports.push((
1296
path.index,
1297
path.path.iter().map(|s| s.to_string()).collect(),
1298
))
1299
})
1300
}
1301
1302
/// Returns the `CoreDef`, the canonical definition for a core wasm item,
1303
/// for the export `name` of `instance` within `frame`.
1304
fn core_def_of_module_instance_export(
1305
&self,
1306
frame: &InlinerFrame<'a>,
1307
instance: ModuleInstanceIndex,
1308
name: &'a str,
1309
) -> dfg::CoreDef {
1310
match &frame.module_instances[instance] {
1311
// Instantiations of a statically known module means that we can
1312
// refer to the exported item by a precise index, skipping name
1313
// lookups at runtime.
1314
//
1315
// Instantiations of an imported module, however, must do name
1316
// lookups at runtime since we don't know the structure ahead of
1317
// time here.
1318
ModuleInstanceDef::Instantiated(instance, module) => {
1319
let item = match frame.modules[*module] {
1320
ModuleDef::Static(idx, _ty) => {
1321
let entity = self.nested_modules[idx].module.exports[name];
1322
ExportItem::Index(entity)
1323
}
1324
ModuleDef::Import(..) => ExportItem::Name(name.to_string()),
1325
};
1326
dfg::CoreExport {
1327
instance: *instance,
1328
item,
1329
}
1330
.into()
1331
}
1332
1333
// This is a synthetic instance so the canonical definition of the
1334
// original item is returned.
1335
ModuleInstanceDef::Synthetic(instance) => match instance[name] {
1336
EntityIndex::Function(i) => frame.funcs[i].1.clone(),
1337
EntityIndex::Table(i) => frame.tables[i].clone().into(),
1338
EntityIndex::Global(i) => frame.globals[i].clone().into(),
1339
EntityIndex::Memory(i) => frame.memories[i].clone().into(),
1340
EntityIndex::Tag(_) => todo!(), // FIXME: #10252 support for tags in the component model
1341
},
1342
}
1343
}
1344
1345
fn memory(
1346
&mut self,
1347
frame: &InlinerFrame<'a>,
1348
types: &ComponentTypesBuilder,
1349
memory: MemoryIndex,
1350
) -> (dfg::CoreExport<MemoryIndex>, bool) {
1351
let memory = frame.memories[memory].clone().map_index(|i| match i {
1352
EntityIndex::Memory(i) => i,
1353
_ => unreachable!(),
1354
});
1355
let memory64 = match &self.runtime_instances[memory.instance] {
1356
InstanceModule::Static(idx) => match &memory.item {
1357
ExportItem::Index(i) => {
1358
let ty = &self.nested_modules[*idx].module.memories[*i];
1359
match ty.idx_type {
1360
IndexType::I32 => false,
1361
IndexType::I64 => true,
1362
}
1363
}
1364
ExportItem::Name(_) => unreachable!(),
1365
},
1366
InstanceModule::Import(ty) => match &memory.item {
1367
ExportItem::Name(name) => match types[*ty].exports[name] {
1368
EntityType::Memory(m) => match m.idx_type {
1369
IndexType::I32 => false,
1370
IndexType::I64 => true,
1371
},
1372
_ => unreachable!(),
1373
},
1374
ExportItem::Index(_) => unreachable!(),
1375
},
1376
};
1377
(memory, memory64)
1378
}
1379
1380
/// Translates a `LocalCanonicalOptions` which indexes into the `frame`
1381
/// specified into a runtime representation.
1382
fn adapter_options(
1383
&mut self,
1384
frame: &InlinerFrame<'a>,
1385
types: &ComponentTypesBuilder,
1386
options: &LocalCanonicalOptions,
1387
) -> AdapterOptions {
1388
let data_model = match options.data_model {
1389
LocalDataModel::Gc {} => DataModel::Gc {},
1390
LocalDataModel::LinearMemory { memory, realloc } => {
1391
let (memory, memory64) = memory
1392
.map(|i| {
1393
let (memory, memory64) = self.memory(frame, types, i);
1394
(Some(memory), memory64)
1395
})
1396
.unwrap_or((None, false));
1397
let realloc = realloc.map(|i| frame.funcs[i].1.clone());
1398
DataModel::LinearMemory {
1399
memory,
1400
memory64,
1401
realloc,
1402
}
1403
}
1404
};
1405
let callback = options.callback.map(|i| frame.funcs[i].1.clone());
1406
let post_return = options.post_return.map(|i| frame.funcs[i].1.clone());
1407
AdapterOptions {
1408
instance: frame.instance,
1409
string_encoding: options.string_encoding,
1410
callback,
1411
post_return,
1412
async_: options.async_,
1413
core_type: options.core_type,
1414
data_model,
1415
}
1416
}
1417
1418
/// Translatees an `AdapterOptions` into a `CanonicalOptions` where
1419
/// memories/functions are inserted into the global initializer list for
1420
/// use at runtime. This is only used for lowered host functions and lifted
1421
/// functions exported to the host.
1422
fn canonical_options(&mut self, options: AdapterOptions) -> dfg::OptionsId {
1423
let data_model = match options.data_model {
1424
DataModel::Gc {} => dfg::CanonicalOptionsDataModel::Gc {},
1425
DataModel::LinearMemory {
1426
memory,
1427
memory64: _,
1428
realloc,
1429
} => dfg::CanonicalOptionsDataModel::LinearMemory {
1430
memory: memory.map(|export| self.result.memories.push(export)),
1431
realloc: realloc.map(|def| self.result.reallocs.push(def)),
1432
},
1433
};
1434
let callback = options.callback.map(|def| self.result.callbacks.push(def));
1435
let post_return = options
1436
.post_return
1437
.map(|def| self.result.post_returns.push(def));
1438
self.result.options.push(dfg::CanonicalOptions {
1439
instance: options.instance,
1440
string_encoding: options.string_encoding,
1441
callback,
1442
post_return,
1443
async_: options.async_,
1444
core_type: options.core_type,
1445
data_model,
1446
})
1447
}
1448
1449
fn record_export(
1450
&mut self,
1451
name: &str,
1452
def: ComponentItemDef<'a>,
1453
types: &'a ComponentTypesBuilder,
1454
map: &mut IndexMap<String, dfg::Export>,
1455
) -> Result<()> {
1456
let export = match def {
1457
// Exported modules are currently saved in a `PrimaryMap`, at
1458
// runtime, so an index (`RuntimeModuleIndex`) is assigned here and
1459
// then an initializer is recorded about where the module comes
1460
// from.
1461
ComponentItemDef::Module(module) => match module {
1462
ModuleDef::Static(index, ty) => dfg::Export::ModuleStatic { ty, index },
1463
ModuleDef::Import(path, ty) => dfg::Export::ModuleImport {
1464
ty,
1465
import: self.runtime_import(&path),
1466
},
1467
},
1468
1469
ComponentItemDef::Func(func) => match func {
1470
// If this is a lifted function from something lowered in this
1471
// component then the configured options are plumbed through
1472
// here.
1473
ComponentFuncDef::Lifted { ty, func, options } => {
1474
let options = self.canonical_options(options);
1475
dfg::Export::LiftedFunction { ty, func, options }
1476
}
1477
1478
// Currently reexported functions from an import are not
1479
// supported. Being able to actually call these functions is
1480
// somewhat tricky and needs something like temporary scratch
1481
// space that isn't implemented.
1482
ComponentFuncDef::Import(_) => {
1483
bail!(
1484
"component export `{name}` is a reexport of an imported function which is not implemented"
1485
)
1486
}
1487
},
1488
1489
ComponentItemDef::Instance(instance) => {
1490
let mut exports = IndexMap::new();
1491
match instance {
1492
// If this instance is one that was originally imported by
1493
// the component itself then the imports are translated here
1494
// by converting to a `ComponentItemDef` and then
1495
// recursively recording the export as a reexport.
1496
//
1497
// Note that for now this would only work with
1498
// module-exporting instances.
1499
ComponentInstanceDef::Import(path, ty) => {
1500
for (name, ty) in types[ty].exports.iter() {
1501
let path = path.push(name);
1502
let def = ComponentItemDef::from_import(path, *ty)?;
1503
self.record_export(name, def, types, &mut exports)?;
1504
}
1505
dfg::Export::Instance { ty, exports }
1506
}
1507
1508
// An exported instance which is itself a bag of items is
1509
// translated recursively here to our `exports` map which is
1510
// the bag of items we're exporting.
1511
ComponentInstanceDef::Items(map, ty) => {
1512
for (name, def) in map {
1513
self.record_export(name, def, types, &mut exports)?;
1514
}
1515
dfg::Export::Instance { ty, exports }
1516
}
1517
}
1518
}
1519
1520
// FIXME(#4283) should make an official decision on whether this is
1521
// the final treatment of this or not.
1522
ComponentItemDef::Component(_) => {
1523
bail!("exporting a component from the root component is not supported")
1524
}
1525
1526
ComponentItemDef::Type(def) => dfg::Export::Type(def),
1527
};
1528
1529
map.insert(name.to_string(), export);
1530
Ok(())
1531
}
1532
}
1533
1534
impl<'a> InlinerFrame<'a> {
1535
fn new(
1536
instance: RuntimeComponentInstanceIndex,
1537
translation: &'a Translation<'a>,
1538
closure: ComponentClosure<'a>,
1539
args: HashMap<&'a str, ComponentItemDef<'a>>,
1540
instance_ty: Option<ComponentInstanceTypeId>,
1541
) -> Self {
1542
// FIXME: should iterate over the initializers of `translation` and
1543
// calculate the size of each index space to use `with_capacity` for
1544
// all the maps below. Given that doing such would be wordy and compile
1545
// time is otherwise not super crucial it's not done at this time.
1546
InlinerFrame {
1547
instance,
1548
translation,
1549
closure,
1550
args,
1551
instance_ty,
1552
initializers: translation.initializers.iter(),
1553
1554
funcs: Default::default(),
1555
memories: Default::default(),
1556
tables: Default::default(),
1557
globals: Default::default(),
1558
tags: Default::default(),
1559
1560
component_instances: Default::default(),
1561
component_funcs: Default::default(),
1562
module_instances: Default::default(),
1563
components: Default::default(),
1564
modules: Default::default(),
1565
}
1566
}
1567
1568
fn item(
1569
&self,
1570
index: ComponentItem,
1571
types: &mut ComponentTypesBuilder,
1572
) -> Result<ComponentItemDef<'a>> {
1573
Ok(match index {
1574
ComponentItem::Func(i) => ComponentItemDef::Func(self.component_funcs[i].clone()),
1575
ComponentItem::Component(i) => ComponentItemDef::Component(self.components[i].clone()),
1576
ComponentItem::ComponentInstance(i) => {
1577
ComponentItemDef::Instance(self.component_instances[i].clone())
1578
}
1579
ComponentItem::Module(i) => ComponentItemDef::Module(self.modules[i].clone()),
1580
ComponentItem::Type(t) => {
1581
let types_ref = self.translation.types_ref();
1582
ComponentItemDef::Type(types.convert_type(types_ref, t)?)
1583
}
1584
})
1585
}
1586
1587
/// Pushes the component `item` definition provided into the appropriate
1588
/// index space within this component.
1589
fn push_item(&mut self, item: ComponentItemDef<'a>) {
1590
match item {
1591
ComponentItemDef::Func(i) => {
1592
self.component_funcs.push(i);
1593
}
1594
ComponentItemDef::Module(i) => {
1595
self.modules.push(i);
1596
}
1597
ComponentItemDef::Component(i) => {
1598
self.components.push(i);
1599
}
1600
ComponentItemDef::Instance(i) => {
1601
self.component_instances.push(i);
1602
}
1603
1604
// In short, type definitions aren't tracked here.
1605
//
1606
// The longer form explanation for this is that structural types
1607
// like lists and records don't need to be tracked at all and the
1608
// only significant type which needs tracking is resource types
1609
// themselves. Resource types, however, are tracked within the
1610
// `ResourcesBuilder` state rather than an `InlinerFrame` so they're
1611
// ignored here as well. The general reason for that is that type
1612
// information is everywhere and this `InlinerFrame` is not
1613
// everywhere so it seemed like it would make sense to split the
1614
// two.
1615
//
1616
// Note though that this case is actually frequently hit, so it
1617
// can't be `unreachable!()`. Instead callers are responsible for
1618
// handling this appropriately with respect to resources.
1619
ComponentItemDef::Type(_ty) => {}
1620
}
1621
}
1622
1623
fn closed_over_module(&self, index: &ClosedOverModule) -> ModuleDef<'a> {
1624
match *index {
1625
ClosedOverModule::Local(i) => self.modules[i].clone(),
1626
ClosedOverModule::Upvar(i) => self.closure.modules[i].clone(),
1627
}
1628
}
1629
1630
fn closed_over_component(&self, index: &ClosedOverComponent) -> ComponentDef<'a> {
1631
match *index {
1632
ClosedOverComponent::Local(i) => self.components[i].clone(),
1633
ClosedOverComponent::Upvar(i) => self.closure.components[i].clone(),
1634
}
1635
}
1636
1637
/// Completes the instantiation of a subcomponent and records type
1638
/// information for the instance that was produced.
1639
///
1640
/// This method is invoked when an `InlinerFrame` finishes for a
1641
/// subcomponent. The `def` provided represents the instance that was
1642
/// produced from instantiation, and `ty` is the wasmparser-defined type of
1643
/// the instance produced.
1644
///
1645
/// The purpose of this method is to record type information about resources
1646
/// in the instance produced. In the component model all instantiations of a
1647
/// component produce fresh new types for all resources which are unequal to
1648
/// all prior resources. This means that if wasmparser's `ty` type
1649
/// information references a unique resource within `def` that has never
1650
/// been registered before then that means it's a defined resource within
1651
/// the component that was just instantiated (as opposed to an imported
1652
/// resource which was reexported).
1653
///
1654
/// Further type translation after this instantiation can refer to these
1655
/// resource types and a mapping from those types to the wasmtime-internal
1656
/// types is required, so this method builds up those mappings.
1657
///
1658
/// Essentially what happens here is that the `ty` type is registered and
1659
/// any new unique resources are registered so new tables can be introduced
1660
/// along with origin information about the actual underlying resource type
1661
/// and which component instantiated it.
1662
fn finish_instantiate(
1663
&mut self,
1664
exports: IndexMap<&'a str, ComponentItemDef<'a>>,
1665
ty: ComponentInstanceTypeId,
1666
types: &mut ComponentTypesBuilder,
1667
) -> Result<()> {
1668
let types_ref = self.translation.types_ref();
1669
{
1670
let (resources, types) = types.resources_mut_and_types();
1671
let mut path = Vec::new();
1672
resources.register_component_entity_type(
1673
&types_ref,
1674
ComponentEntityType::Instance(ty),
1675
&mut path,
1676
&mut |path| match path {
1677
[] => unreachable!(),
1678
[name, rest @ ..] => exports[name].lookup_resource(rest, types),
1679
},
1680
);
1681
}
1682
let ty = types.convert_instance(types_ref, ty)?;
1683
let def = ComponentInstanceDef::Items(exports, ty);
1684
let arg = ComponentItemDef::Instance(def);
1685
self.push_item(arg);
1686
Ok(())
1687
}
1688
}
1689
1690
impl<'a> ImportPath<'a> {
1691
fn root(index: ImportIndex) -> ImportPath<'a> {
1692
ImportPath {
1693
index,
1694
path: Vec::new(),
1695
}
1696
}
1697
1698
fn push(&self, s: impl Into<Cow<'a, str>>) -> ImportPath<'a> {
1699
let mut new = self.clone();
1700
new.path.push(s.into());
1701
new
1702
}
1703
}
1704
1705
impl<'a> ComponentItemDef<'a> {
1706
fn from_import(path: ImportPath<'a>, ty: TypeDef) -> Result<ComponentItemDef<'a>> {
1707
let item = match ty {
1708
TypeDef::Module(ty) => ComponentItemDef::Module(ModuleDef::Import(path, ty)),
1709
TypeDef::ComponentInstance(ty) => {
1710
ComponentItemDef::Instance(ComponentInstanceDef::Import(path, ty))
1711
}
1712
TypeDef::ComponentFunc(_ty) => ComponentItemDef::Func(ComponentFuncDef::Import(path)),
1713
// FIXME(#4283) should commit one way or another to how this
1714
// should be treated.
1715
TypeDef::Component(_ty) => bail!("root-level component imports are not supported"),
1716
TypeDef::Interface(_) | TypeDef::Resource(_) => ComponentItemDef::Type(ty),
1717
TypeDef::CoreFunc(_) => unreachable!(),
1718
};
1719
Ok(item)
1720
}
1721
1722
/// Walks the `path` within `self` to find a resource at that path.
1723
///
1724
/// This method is used when resources are found within wasmparser's type
1725
/// information and they need to be correlated with actual concrete
1726
/// definitions from this inlining pass. The `path` here is a list of
1727
/// instance export names (or empty) to walk to reach down into the final
1728
/// definition which should refer to a resource itself.
1729
fn lookup_resource(&self, path: &[&str], types: &ComponentTypes) -> ResourceIndex {
1730
let mut cur = self.clone();
1731
1732
// Each element of `path` represents unwrapping a layer of an instance
1733
// type, so handle those here by updating `cur` iteratively.
1734
for element in path.iter().copied() {
1735
let instance = match cur {
1736
ComponentItemDef::Instance(def) => def,
1737
_ => unreachable!(),
1738
};
1739
cur = match instance {
1740
// If this instance is a "bag of things" then this is as easy as
1741
// looking up the name in the bag of names.
1742
ComponentInstanceDef::Items(names, _) => names[element].clone(),
1743
1744
// If, however, this instance is an imported instance then this
1745
// is a further projection within the import with one more path
1746
// element. The `types` type information is used to lookup the
1747
// type of `element` within the instance type, and that's used
1748
// in conjunction with a one-longer `path` to produce a new item
1749
// definition.
1750
ComponentInstanceDef::Import(path, ty) => {
1751
ComponentItemDef::from_import(path.push(element), types[ty].exports[element])
1752
.unwrap()
1753
}
1754
};
1755
}
1756
1757
// Once `path` has been iterated over it must be the case that the final
1758
// item is a resource type, in which case a lookup can be performed.
1759
match cur {
1760
ComponentItemDef::Type(TypeDef::Resource(idx)) => types[idx].ty,
1761
_ => unreachable!(),
1762
}
1763
}
1764
}
1765
1766
#[derive(Clone, Copy)]
1767
enum InstanceModule {
1768
Static(StaticModuleIndex),
1769
Import(TypeModuleIndex),
1770
}
1771
1772