Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/environ/src/fact.rs
3073 views
1
//! Wasmtime's Fused Adapter Compiler of Trampolines (FACT)
2
//!
3
//! This module contains a compiler which emits trampolines to implement fused
4
//! adapters for the component model. A fused adapter is when a core wasm
5
//! function is lifted from one component instance and then lowered into another
6
//! component instance. This communication between components is well-defined by
7
//! the spec and ends up creating what's called a "fused adapter".
8
//!
9
//! Adapters are currently implemented with WebAssembly modules. This submodule
10
//! will generate a core wasm binary which contains the adapters specified
11
//! during compilation. The actual wasm is then later processed by standard
12
//! paths in Wasmtime to create native machine code and runtime representations
13
//! of modules.
14
//!
15
//! Note that identification of precisely what goes into an adapter module is
16
//! not handled in this file, instead that's all done in `translate/adapt.rs`.
17
//! Otherwise this module is only responsible for taking a set of adapters and
18
//! their imports and then generating a core wasm module to implement all of
19
//! that.
20
21
use crate::component::dfg::CoreDef;
22
use crate::component::{
23
Adapter, AdapterOptions as AdapterOptionsDfg, ComponentTypesBuilder, FlatType, InterfaceType,
24
RuntimeComponentInstanceIndex, StringEncoding, Transcode, TypeFuncIndex,
25
};
26
use crate::fact::transcode::Transcoder;
27
use crate::{EntityRef, FuncIndex, GlobalIndex, MemoryIndex, PrimaryMap, Tunables};
28
use crate::{ModuleInternedTypeIndex, prelude::*};
29
use std::collections::HashMap;
30
use wasm_encoder::*;
31
32
mod core_types;
33
mod signature;
34
mod trampoline;
35
mod transcode;
36
37
/// Fixed parameter types for the `prepare_call` built-in function.
38
///
39
/// Note that `prepare_call` also takes a variable number of parameters in
40
/// addition to these, determined by the signature of the function for which
41
/// we're generating an adapter.
42
pub static PREPARE_CALL_FIXED_PARAMS: &[ValType] = &[
43
ValType::FUNCREF, // start
44
ValType::FUNCREF, // return
45
ValType::I32, // caller_instance
46
ValType::I32, // callee_instance
47
ValType::I32, // task_return_type
48
ValType::I32, // callee_async
49
ValType::I32, // string_encoding
50
ValType::I32, // result_count_or_max_if_async
51
];
52
53
/// Representation of an adapter module.
54
pub struct Module<'a> {
55
/// Compilation configuration
56
tunables: &'a Tunables,
57
/// Type information from the creator of this `Module`
58
types: &'a ComponentTypesBuilder,
59
60
/// Core wasm type section that's incrementally built
61
core_types: core_types::CoreTypes,
62
63
/// Core wasm import section which is built as adapters are inserted. Note
64
/// that imports here are intern'd to avoid duplicate imports of the same
65
/// item.
66
core_imports: ImportSection,
67
/// Final list of imports that this module ended up using, in the same order
68
/// as the imports in the import section.
69
imports: Vec<Import>,
70
/// Intern'd imports and what index they were assigned. Note that this map
71
/// covers all the index spaces for imports, not just one.
72
imported: HashMap<CoreDef, usize>,
73
/// Intern'd transcoders and what index they were assigned.
74
imported_transcoders: HashMap<Transcoder, FuncIndex>,
75
76
/// Cached versions of imported trampolines for working with resources.
77
imported_resource_transfer_own: Option<FuncIndex>,
78
imported_resource_transfer_borrow: Option<FuncIndex>,
79
imported_resource_enter_call: Option<FuncIndex>,
80
imported_resource_exit_call: Option<FuncIndex>,
81
82
// Cached versions of imported trampolines for working with the async ABI.
83
imported_async_start_calls: HashMap<(Option<FuncIndex>, Option<FuncIndex>), FuncIndex>,
84
85
// Cached versions of imported trampolines for working with `stream`s,
86
// `future`s, and `error-context`s.
87
imported_future_transfer: Option<FuncIndex>,
88
imported_stream_transfer: Option<FuncIndex>,
89
imported_error_context_transfer: Option<FuncIndex>,
90
91
imported_enter_sync_call: Option<FuncIndex>,
92
imported_exit_sync_call: Option<FuncIndex>,
93
94
imported_trap: Option<FuncIndex>,
95
96
// Current status of index spaces from the imports generated so far.
97
imported_funcs: PrimaryMap<FuncIndex, Option<CoreDef>>,
98
imported_memories: PrimaryMap<MemoryIndex, CoreDef>,
99
imported_globals: PrimaryMap<GlobalIndex, CoreDef>,
100
101
funcs: PrimaryMap<FunctionId, Function>,
102
helper_funcs: HashMap<Helper, FunctionId>,
103
helper_worklist: Vec<(FunctionId, Helper)>,
104
105
exports: Vec<(u32, String)>,
106
107
task_may_block: Option<GlobalIndex>,
108
}
109
110
struct AdapterData {
111
/// Export name of this adapter
112
name: String,
113
/// Options specified during the `canon lift` operation
114
lift: AdapterOptions,
115
/// Options specified during the `canon lower` operation
116
lower: AdapterOptions,
117
/// The core wasm function that this adapter will be calling (the original
118
/// function that was `canon lift`'d)
119
callee: FuncIndex,
120
}
121
122
/// Configuration options which apply at the "global adapter" level.
123
///
124
/// These options are typically unique per-adapter and generally aren't needed
125
/// when translating recursive types within an adapter.
126
struct AdapterOptions {
127
/// The Wasmtime-assigned component instance index where the options were
128
/// originally specified.
129
instance: RuntimeComponentInstanceIndex,
130
/// The ancestors (i.e. chain of instantiating instances) of the instance
131
/// specified in the `instance` field.
132
ancestors: Vec<RuntimeComponentInstanceIndex>,
133
/// The ascribed type of this adapter.
134
ty: TypeFuncIndex,
135
/// The global that represents the instance flags for where this adapter
136
/// came from.
137
flags: GlobalIndex,
138
/// The configured post-return function, if any.
139
post_return: Option<FuncIndex>,
140
/// Other, more general, options configured.
141
options: Options,
142
}
143
144
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
145
/// Linear memory.
146
struct LinearMemoryOptions {
147
/// Whether or not the `memory` field, if present, is a 64-bit memory.
148
memory64: bool,
149
/// An optionally-specified memory where values may travel through for
150
/// types like lists.
151
memory: Option<MemoryIndex>,
152
/// An optionally-specified function to be used to allocate space for
153
/// types such as strings as they go into a module.
154
realloc: Option<FuncIndex>,
155
}
156
157
impl LinearMemoryOptions {
158
fn ptr(&self) -> ValType {
159
if self.memory64 {
160
ValType::I64
161
} else {
162
ValType::I32
163
}
164
}
165
166
fn ptr_size(&self) -> u8 {
167
if self.memory64 { 8 } else { 4 }
168
}
169
}
170
171
/// The data model for objects passed through an adapter.
172
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
173
enum DataModel {
174
Gc {},
175
LinearMemory(LinearMemoryOptions),
176
}
177
178
impl DataModel {
179
#[track_caller]
180
fn unwrap_memory(&self) -> &LinearMemoryOptions {
181
match self {
182
DataModel::Gc {} => panic!("`unwrap_memory` on GC"),
183
DataModel::LinearMemory(opts) => opts,
184
}
185
}
186
}
187
188
/// This type is split out of `AdapterOptions` and is specifically used to
189
/// deduplicate translation functions within a module. Consequently this has
190
/// as few fields as possible to minimize the number of functions generated
191
/// within an adapter module.
192
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
193
struct Options {
194
/// The encoding that strings use from this adapter.
195
string_encoding: StringEncoding,
196
callback: Option<FuncIndex>,
197
async_: bool,
198
core_type: ModuleInternedTypeIndex,
199
data_model: DataModel,
200
}
201
202
/// Representation of a "helper function" which may be generated as part of
203
/// generating an adapter trampoline.
204
///
205
/// Helper functions are created when inlining the translation for a type in its
206
/// entirety would make a function excessively large. This is currently done via
207
/// a simple fuel/cost heuristic based on the type being translated but may get
208
/// fancier over time.
209
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
210
struct Helper {
211
/// Metadata about the source type of what's being translated.
212
src: HelperType,
213
/// Metadata about the destination type which is being translated to.
214
dst: HelperType,
215
}
216
217
/// Information about a source or destination type in a `Helper` which is
218
/// generated.
219
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
220
struct HelperType {
221
/// The concrete type being translated.
222
ty: InterfaceType,
223
/// The configuration options (memory, etc) for the adapter.
224
opts: Options,
225
/// Where the type is located (either the stack or in memory)
226
loc: HelperLocation,
227
}
228
229
/// Where a `HelperType` is located, dictating the signature of the helper
230
/// function.
231
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
232
enum HelperLocation {
233
/// Located on the stack in wasm locals.
234
Stack,
235
/// Located in linear memory as configured by `opts`.
236
Memory,
237
/// Located in a GC struct field.
238
#[expect(dead_code, reason = "CM+GC is still WIP")]
239
StructField,
240
/// Located in a GC array element.
241
#[expect(dead_code, reason = "CM+GC is still WIP")]
242
ArrayElement,
243
}
244
245
impl<'a> Module<'a> {
246
/// Creates an empty module.
247
pub fn new(types: &'a ComponentTypesBuilder, tunables: &'a Tunables) -> Module<'a> {
248
Module {
249
tunables,
250
types,
251
core_types: Default::default(),
252
core_imports: Default::default(),
253
imported: Default::default(),
254
imports: Default::default(),
255
imported_transcoders: Default::default(),
256
imported_funcs: PrimaryMap::new(),
257
imported_memories: PrimaryMap::new(),
258
imported_globals: PrimaryMap::new(),
259
funcs: PrimaryMap::new(),
260
helper_funcs: HashMap::new(),
261
helper_worklist: Vec::new(),
262
imported_resource_transfer_own: None,
263
imported_resource_transfer_borrow: None,
264
imported_resource_enter_call: None,
265
imported_resource_exit_call: None,
266
imported_async_start_calls: HashMap::new(),
267
imported_future_transfer: None,
268
imported_stream_transfer: None,
269
imported_error_context_transfer: None,
270
imported_enter_sync_call: None,
271
imported_exit_sync_call: None,
272
imported_trap: None,
273
exports: Vec::new(),
274
task_may_block: None,
275
}
276
}
277
278
/// Registers a new adapter within this adapter module.
279
///
280
/// The `name` provided is the export name of the adapter from the final
281
/// module, and `adapter` contains all metadata necessary for compilation.
282
pub fn adapt(&mut self, name: &str, adapter: &Adapter) {
283
// Import any items required by the various canonical options
284
// (memories, reallocs, etc)
285
let mut lift = self.import_options(adapter.lift_ty, &adapter.lift_options);
286
let lower = self.import_options(adapter.lower_ty, &adapter.lower_options);
287
288
// Lowering options are not allowed to specify post-return as per the
289
// current canonical abi specification.
290
assert!(adapter.lower_options.post_return.is_none());
291
292
// Import the core wasm function which was lifted using its appropriate
293
// signature since the exported function this adapter generates will
294
// call the lifted function.
295
let signature = self.types.signature(&lift);
296
let ty = self
297
.core_types
298
.function(&signature.params, &signature.results);
299
let callee = self.import_func("callee", name, ty, adapter.func.clone());
300
301
// Handle post-return specifically here where we have `core_ty` and the
302
// results of `core_ty` are the parameters to the post-return function.
303
lift.post_return = adapter.lift_options.post_return.as_ref().map(|func| {
304
let ty = self.core_types.function(&signature.results, &[]);
305
self.import_func("post_return", name, ty, func.clone())
306
});
307
308
// This will internally create the adapter as specified and append
309
// anything necessary to `self.funcs`.
310
trampoline::compile(
311
self,
312
&AdapterData {
313
name: name.to_string(),
314
lift,
315
lower,
316
callee,
317
},
318
);
319
320
while let Some((result, helper)) = self.helper_worklist.pop() {
321
trampoline::compile_helper(self, result, helper);
322
}
323
}
324
325
fn import_options(&mut self, ty: TypeFuncIndex, options: &AdapterOptionsDfg) -> AdapterOptions {
326
let AdapterOptionsDfg {
327
instance,
328
ancestors,
329
string_encoding,
330
post_return: _, // handled above
331
callback,
332
async_,
333
core_type,
334
data_model,
335
cancellable,
336
} = options;
337
assert!(!cancellable);
338
339
let flags = self.import_global(
340
"flags",
341
&format!("instance{}", instance.as_u32()),
342
GlobalType {
343
val_type: ValType::I32,
344
mutable: true,
345
shared: false,
346
},
347
CoreDef::InstanceFlags(*instance),
348
);
349
350
let data_model = match data_model {
351
crate::component::DataModel::Gc {} => DataModel::Gc {},
352
crate::component::DataModel::LinearMemory {
353
memory,
354
memory64,
355
realloc,
356
} => {
357
let memory = memory.as_ref().map(|memory| {
358
self.import_memory(
359
"memory",
360
&format!("m{}", self.imported_memories.len()),
361
MemoryType {
362
minimum: 0,
363
maximum: None,
364
shared: false,
365
memory64: *memory64,
366
page_size_log2: None,
367
},
368
memory.clone().into(),
369
)
370
});
371
let realloc = realloc.as_ref().map(|func| {
372
let ptr = if *memory64 {
373
ValType::I64
374
} else {
375
ValType::I32
376
};
377
let ty = self.core_types.function(&[ptr, ptr, ptr, ptr], &[ptr]);
378
self.import_func(
379
"realloc",
380
&format!("f{}", self.imported_funcs.len()),
381
ty,
382
func.clone(),
383
)
384
});
385
DataModel::LinearMemory(LinearMemoryOptions {
386
memory64: *memory64,
387
memory,
388
realloc,
389
})
390
}
391
};
392
393
let callback = callback.as_ref().map(|func| {
394
let ty = self
395
.core_types
396
.function(&[ValType::I32, ValType::I32, ValType::I32], &[ValType::I32]);
397
self.import_func(
398
"callback",
399
&format!("f{}", self.imported_funcs.len()),
400
ty,
401
func.clone(),
402
)
403
});
404
405
AdapterOptions {
406
instance: *instance,
407
ancestors: ancestors.clone(),
408
ty,
409
flags,
410
post_return: None,
411
options: Options {
412
string_encoding: *string_encoding,
413
callback,
414
async_: *async_,
415
core_type: *core_type,
416
data_model,
417
},
418
}
419
}
420
421
fn import_func(&mut self, module: &str, name: &str, ty: u32, def: CoreDef) -> FuncIndex {
422
self.import(module, name, EntityType::Function(ty), def, |m| {
423
&mut m.imported_funcs
424
})
425
}
426
427
fn import_global(
428
&mut self,
429
module: &str,
430
name: &str,
431
ty: GlobalType,
432
def: CoreDef,
433
) -> GlobalIndex {
434
self.import(module, name, EntityType::Global(ty), def, |m| {
435
&mut m.imported_globals
436
})
437
}
438
439
fn import_memory(
440
&mut self,
441
module: &str,
442
name: &str,
443
ty: MemoryType,
444
def: CoreDef,
445
) -> MemoryIndex {
446
self.import(module, name, EntityType::Memory(ty), def, |m| {
447
&mut m.imported_memories
448
})
449
}
450
451
fn import<K: EntityRef, V: From<CoreDef>>(
452
&mut self,
453
module: &str,
454
name: &str,
455
ty: EntityType,
456
def: CoreDef,
457
map: impl FnOnce(&mut Self) -> &mut PrimaryMap<K, V>,
458
) -> K {
459
if let Some(prev) = self.imported.get(&def) {
460
return K::new(*prev);
461
}
462
let idx = map(self).push(def.clone().into());
463
self.core_imports.import(module, name, ty);
464
self.imported.insert(def.clone(), idx.index());
465
self.imports.push(Import::CoreDef(def));
466
idx
467
}
468
469
fn import_task_may_block(&mut self) -> GlobalIndex {
470
if let Some(task_may_block) = self.task_may_block {
471
task_may_block
472
} else {
473
let task_may_block = self.import_global(
474
"instance",
475
"task_may_block",
476
GlobalType {
477
val_type: ValType::I32,
478
mutable: true,
479
shared: false,
480
},
481
CoreDef::TaskMayBlock,
482
);
483
self.task_may_block = Some(task_may_block);
484
task_may_block
485
}
486
}
487
488
fn import_transcoder(&mut self, transcoder: transcode::Transcoder) -> FuncIndex {
489
*self
490
.imported_transcoders
491
.entry(transcoder)
492
.or_insert_with(|| {
493
// Add the import to the core wasm import section...
494
let name = transcoder.name();
495
let ty = transcoder.ty(&mut self.core_types);
496
self.core_imports.import("transcode", &name, ty);
497
498
// ... and also record the metadata for what this import
499
// corresponds to.
500
let from = self.imported_memories[transcoder.from_memory].clone();
501
let to = self.imported_memories[transcoder.to_memory].clone();
502
self.imports.push(Import::Transcode {
503
op: transcoder.op,
504
from,
505
from64: transcoder.from_memory64,
506
to,
507
to64: transcoder.to_memory64,
508
});
509
510
self.imported_funcs.push(None)
511
})
512
}
513
514
fn import_simple(
515
&mut self,
516
module: &str,
517
name: &str,
518
params: &[ValType],
519
results: &[ValType],
520
import: Import,
521
get: impl Fn(&mut Self) -> &mut Option<FuncIndex>,
522
) -> FuncIndex {
523
self.import_simple_get_and_set(
524
module,
525
name,
526
params,
527
results,
528
import,
529
|me| *get(me),
530
|me, v| *get(me) = Some(v),
531
)
532
}
533
534
fn import_simple_get_and_set(
535
&mut self,
536
module: &str,
537
name: &str,
538
params: &[ValType],
539
results: &[ValType],
540
import: Import,
541
get: impl Fn(&mut Self) -> Option<FuncIndex>,
542
set: impl Fn(&mut Self, FuncIndex),
543
) -> FuncIndex {
544
if let Some(idx) = get(self) {
545
return idx;
546
}
547
let ty = self.core_types.function(params, results);
548
let ty = EntityType::Function(ty);
549
self.core_imports.import(module, name, ty);
550
551
self.imports.push(import);
552
let idx = self.imported_funcs.push(None);
553
set(self, idx);
554
idx
555
}
556
557
/// Import a host built-in function to set up a subtask for a sync-lowered
558
/// import call to an async-lifted export.
559
///
560
/// Given that the callee may exert backpressure before the host can copy
561
/// the parameters, the adapter must use this function to set up the subtask
562
/// and stash the parameters as part of that subtask until any backpressure
563
/// has cleared.
564
fn import_prepare_call(
565
&mut self,
566
suffix: &str,
567
params: &[ValType],
568
memory: Option<MemoryIndex>,
569
) -> FuncIndex {
570
let ty = self.core_types.function(
571
&PREPARE_CALL_FIXED_PARAMS
572
.iter()
573
.copied()
574
.chain(params.iter().copied())
575
.collect::<Vec<_>>(),
576
&[],
577
);
578
self.core_imports.import(
579
"sync",
580
&format!("[prepare-call]{suffix}"),
581
EntityType::Function(ty),
582
);
583
let import = Import::PrepareCall {
584
memory: memory.map(|v| self.imported_memories[v].clone()),
585
};
586
self.imports.push(import);
587
self.imported_funcs.push(None)
588
}
589
590
/// Import a host built-in function to start a subtask for a sync-lowered
591
/// import call to an async-lifted export.
592
///
593
/// This call with block until the subtask has produced result(s) via the
594
/// `task.return` intrinsic.
595
///
596
/// Note that this could potentially be combined with the `sync-prepare`
597
/// built-in into a single built-in function that does both jobs. However,
598
/// we've kept them separate to allow a future optimization where the caller
599
/// calls the callee directly rather than using `sync-start` to have the host
600
/// do it.
601
fn import_sync_start_call(
602
&mut self,
603
suffix: &str,
604
callback: Option<FuncIndex>,
605
results: &[ValType],
606
) -> FuncIndex {
607
let ty = self
608
.core_types
609
.function(&[ValType::FUNCREF, ValType::I32], results);
610
self.core_imports.import(
611
"sync",
612
&format!("[start-call]{suffix}"),
613
EntityType::Function(ty),
614
);
615
let import = Import::SyncStartCall {
616
callback: callback
617
.map(|callback| self.imported_funcs.get(callback).unwrap().clone().unwrap()),
618
};
619
self.imports.push(import);
620
self.imported_funcs.push(None)
621
}
622
623
/// Import a host built-in function to start a subtask for an async-lowered
624
/// import call to an async- or sync-lifted export.
625
///
626
/// Note that this could potentially be combined with the `async-prepare`
627
/// built-in into a single built-in function that does both jobs. However,
628
/// we've kept them separate to allow a future optimization where the caller
629
/// calls the callee directly rather than using `async-start` to have the
630
/// host do it.
631
fn import_async_start_call(
632
&mut self,
633
suffix: &str,
634
callback: Option<FuncIndex>,
635
post_return: Option<FuncIndex>,
636
) -> FuncIndex {
637
self.import_simple_get_and_set(
638
"async",
639
&format!("[start-call]{suffix}"),
640
&[ValType::FUNCREF, ValType::I32, ValType::I32, ValType::I32],
641
&[ValType::I32],
642
Import::AsyncStartCall {
643
callback: callback
644
.map(|callback| self.imported_funcs.get(callback).unwrap().clone().unwrap()),
645
post_return: post_return.map(|post_return| {
646
self.imported_funcs
647
.get(post_return)
648
.unwrap()
649
.clone()
650
.unwrap()
651
}),
652
},
653
|me| {
654
me.imported_async_start_calls
655
.get(&(callback, post_return))
656
.copied()
657
},
658
|me, v| {
659
assert!(
660
me.imported_async_start_calls
661
.insert((callback, post_return), v)
662
.is_none()
663
)
664
},
665
)
666
}
667
668
fn import_future_transfer(&mut self) -> FuncIndex {
669
self.import_simple(
670
"future",
671
"transfer",
672
&[ValType::I32; 3],
673
&[ValType::I32],
674
Import::FutureTransfer,
675
|me| &mut me.imported_future_transfer,
676
)
677
}
678
679
fn import_stream_transfer(&mut self) -> FuncIndex {
680
self.import_simple(
681
"stream",
682
"transfer",
683
&[ValType::I32; 3],
684
&[ValType::I32],
685
Import::StreamTransfer,
686
|me| &mut me.imported_stream_transfer,
687
)
688
}
689
690
fn import_error_context_transfer(&mut self) -> FuncIndex {
691
self.import_simple(
692
"error-context",
693
"transfer",
694
&[ValType::I32; 3],
695
&[ValType::I32],
696
Import::ErrorContextTransfer,
697
|me| &mut me.imported_error_context_transfer,
698
)
699
}
700
701
fn import_resource_transfer_own(&mut self) -> FuncIndex {
702
self.import_simple(
703
"resource",
704
"transfer-own",
705
&[ValType::I32, ValType::I32, ValType::I32],
706
&[ValType::I32],
707
Import::ResourceTransferOwn,
708
|me| &mut me.imported_resource_transfer_own,
709
)
710
}
711
712
fn import_resource_transfer_borrow(&mut self) -> FuncIndex {
713
self.import_simple(
714
"resource",
715
"transfer-borrow",
716
&[ValType::I32, ValType::I32, ValType::I32],
717
&[ValType::I32],
718
Import::ResourceTransferBorrow,
719
|me| &mut me.imported_resource_transfer_borrow,
720
)
721
}
722
723
fn import_resource_enter_call(&mut self) -> FuncIndex {
724
self.import_simple(
725
"resource",
726
"enter-call",
727
&[],
728
&[],
729
Import::ResourceEnterCall,
730
|me| &mut me.imported_resource_enter_call,
731
)
732
}
733
734
fn import_resource_exit_call(&mut self) -> FuncIndex {
735
self.import_simple(
736
"resource",
737
"exit-call",
738
&[],
739
&[],
740
Import::ResourceExitCall,
741
|me| &mut me.imported_resource_exit_call,
742
)
743
}
744
745
fn import_enter_sync_call(&mut self) -> FuncIndex {
746
self.import_simple(
747
"async",
748
"enter-sync-call",
749
&[ValType::I32; 3],
750
&[],
751
Import::EnterSyncCall,
752
|me| &mut me.imported_enter_sync_call,
753
)
754
}
755
756
fn import_exit_sync_call(&mut self) -> FuncIndex {
757
self.import_simple(
758
"async",
759
"exit-sync-call",
760
&[],
761
&[],
762
Import::ExitSyncCall,
763
|me| &mut me.imported_exit_sync_call,
764
)
765
}
766
767
fn import_trap(&mut self) -> FuncIndex {
768
self.import_simple(
769
"runtime",
770
"trap",
771
&[ValType::I32],
772
&[],
773
Import::Trap,
774
|me| &mut me.imported_trap,
775
)
776
}
777
778
fn translate_helper(&mut self, helper: Helper) -> FunctionId {
779
*self.helper_funcs.entry(helper).or_insert_with(|| {
780
// Generate a fresh `Function` with a unique id for what we're about to
781
// generate.
782
let ty = helper.core_type(self.types, &mut self.core_types);
783
let id = self.funcs.push(Function::new(None, ty));
784
self.helper_worklist.push((id, helper));
785
id
786
})
787
}
788
789
/// Encodes this module into a WebAssembly binary.
790
pub fn encode(&mut self) -> Vec<u8> {
791
// Build the function/export sections of the wasm module in a first pass
792
// which will assign a final `FuncIndex` to all functions defined in
793
// `self.funcs`.
794
let mut funcs = FunctionSection::new();
795
let mut exports = ExportSection::new();
796
let mut id_to_index = PrimaryMap::<FunctionId, FuncIndex>::new();
797
for (id, func) in self.funcs.iter() {
798
assert!(func.filled_in);
799
let idx = FuncIndex::from_u32(self.imported_funcs.next_key().as_u32() + id.as_u32());
800
let id2 = id_to_index.push(idx);
801
assert_eq!(id2, id);
802
803
funcs.function(func.ty);
804
805
if let Some(name) = &func.export {
806
exports.export(name, ExportKind::Func, idx.as_u32());
807
}
808
}
809
for (idx, name) in &self.exports {
810
exports.export(name, ExportKind::Func, *idx);
811
}
812
813
// With all functions numbered the fragments of the body of each
814
// function can be assigned into one final adapter function.
815
let mut code = CodeSection::new();
816
for (_, func) in self.funcs.iter() {
817
let mut body = Vec::new();
818
819
// Encode all locals used for this function
820
func.locals.len().encode(&mut body);
821
for (count, ty) in func.locals.iter() {
822
count.encode(&mut body);
823
ty.encode(&mut body);
824
}
825
826
// Then encode each "chunk" of a body which may have optional traps
827
// specified within it. Traps get offset by the current length of
828
// the body and otherwise our `Call` instructions are "relocated"
829
// here to the final function index.
830
for chunk in func.body.iter() {
831
match chunk {
832
Body::Raw(code) => {
833
body.extend_from_slice(code);
834
}
835
Body::Call(id) => {
836
Instruction::Call(id_to_index[*id].as_u32()).encode(&mut body);
837
}
838
Body::RefFunc(id) => {
839
Instruction::RefFunc(id_to_index[*id].as_u32()).encode(&mut body);
840
}
841
}
842
}
843
code.raw(&body);
844
}
845
846
let mut result = wasm_encoder::Module::new();
847
result.section(&self.core_types.section);
848
result.section(&self.core_imports);
849
result.section(&funcs);
850
result.section(&exports);
851
result.section(&code);
852
result.finish()
853
}
854
855
/// Returns the imports that were used, in order, to create this adapter
856
/// module.
857
pub fn imports(&self) -> &[Import] {
858
&self.imports
859
}
860
}
861
862
/// Possible imports into an adapter module.
863
#[derive(Clone)]
864
pub enum Import {
865
/// A definition required in the configuration of an `Adapter`.
866
CoreDef(CoreDef),
867
/// A transcoding function from the host to convert between string encodings.
868
Transcode {
869
/// The transcoding operation this performs.
870
op: Transcode,
871
/// The memory being read
872
from: CoreDef,
873
/// Whether or not `from` is a 64-bit memory
874
from64: bool,
875
/// The memory being written
876
to: CoreDef,
877
/// Whether or not `to` is a 64-bit memory
878
to64: bool,
879
},
880
/// Transfers an owned resource from one table to another.
881
ResourceTransferOwn,
882
/// Transfers a borrowed resource from one table to another.
883
ResourceTransferBorrow,
884
/// Sets up entry metadata for a borrow resources when a call starts.
885
ResourceEnterCall,
886
/// Tears down a previous entry and handles checking borrow-related
887
/// metadata.
888
ResourceExitCall,
889
/// An intrinsic used by FACT-generated modules to begin a call involving
890
/// an async-lowered import and/or an async-lifted export.
891
PrepareCall {
892
/// The memory used to verify that the memory specified for the
893
/// `task.return` that is called at runtime (if any) matches the one
894
/// specified in the lifted export.
895
memory: Option<CoreDef>,
896
},
897
/// An intrinsic used by FACT-generated modules to complete a call involving
898
/// a sync-lowered import and async-lifted export.
899
SyncStartCall {
900
/// The callee's callback function, if any.
901
callback: Option<CoreDef>,
902
},
903
/// An intrinsic used by FACT-generated modules to complete a call involving
904
/// an async-lowered import function.
905
AsyncStartCall {
906
/// The callee's callback function, if any.
907
callback: Option<CoreDef>,
908
909
/// The callee's post-return function, if any.
910
post_return: Option<CoreDef>,
911
},
912
/// An intrinisic used by FACT-generated modules to (partially or entirely) transfer
913
/// ownership of a `future`.
914
FutureTransfer,
915
/// An intrinisic used by FACT-generated modules to (partially or entirely) transfer
916
/// ownership of a `stream`.
917
StreamTransfer,
918
/// An intrinisic used by FACT-generated modules to (partially or entirely) transfer
919
/// ownership of an `error-context`.
920
ErrorContextTransfer,
921
/// An intrinsic for trapping the instance with a specific trap code.
922
Trap,
923
/// An intrinsic used by FACT-generated modules to check whether an instance
924
/// may be entered for a sync-to-sync call and push a task onto the stack if
925
/// so.
926
EnterSyncCall,
927
/// An intrinsic used by FACT-generated modules to pop the task previously
928
/// pushed by `EnterSyncCall`.
929
ExitSyncCall,
930
}
931
932
impl Options {
933
fn flat_types<'a>(
934
&self,
935
ty: &InterfaceType,
936
types: &'a ComponentTypesBuilder,
937
) -> Option<&'a [FlatType]> {
938
let flat = types.flat_types(ty)?;
939
match self.data_model {
940
DataModel::Gc {} => todo!("CM+GC"),
941
DataModel::LinearMemory(mem_opts) => Some(if mem_opts.memory64 {
942
flat.memory64
943
} else {
944
flat.memory32
945
}),
946
}
947
}
948
}
949
950
/// Temporary index which is not the same as `FuncIndex`.
951
///
952
/// This represents the nth generated function in the adapter module where the
953
/// final index of the function is not known at the time of generation since
954
/// more imports may be discovered (specifically string transcoders).
955
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
956
struct FunctionId(u32);
957
cranelift_entity::entity_impl!(FunctionId);
958
959
/// A generated function to be added to an adapter module.
960
///
961
/// At least one function is created per-adapter and depending on the type
962
/// hierarchy multiple functions may be generated per-adapter.
963
struct Function {
964
/// Whether or not the `body` has been finished.
965
///
966
/// Functions are added to a `Module` before they're defined so this is used
967
/// to assert that the function was in fact actually filled in by the
968
/// time we reach `Module::encode`.
969
filled_in: bool,
970
971
/// The type signature that this function has, as an index into the core
972
/// wasm type index space of the generated adapter module.
973
ty: u32,
974
975
/// The locals that are used by this function, organized by the number of
976
/// types of each local.
977
locals: Vec<(u32, ValType)>,
978
979
/// If specified, the export name of this function.
980
export: Option<String>,
981
982
/// The contents of the function.
983
///
984
/// See `Body` for more information, and the `Vec` here represents the
985
/// concatenation of all the `Body` fragments.
986
body: Vec<Body>,
987
}
988
989
/// Representation of a fragment of the body of a core wasm function generated
990
/// for adapters.
991
///
992
/// This variant comes in one of two flavors:
993
///
994
/// 1. First a `Raw` variant is used to contain general instructions for the
995
/// wasm function. This is populated by `Compiler::instruction` primarily.
996
///
997
/// 2. A `Call` instruction variant for a `FunctionId` where the final
998
/// `FuncIndex` isn't known until emission time.
999
///
1000
/// The purpose of this representation is the `Body::Call` variant. This can't
1001
/// be encoded as an instruction when it's generated due to not knowing the
1002
/// final index of the function being called. During `Module::encode`, however,
1003
/// all indices are known and `Body::Call` is turned into a final
1004
/// `Instruction::Call`.
1005
///
1006
/// One other possible representation in the future would be to encode a `Call`
1007
/// instruction with a 5-byte leb to fill in later, but for now this felt
1008
/// easier to represent. A 5-byte leb may be more efficient at compile-time if
1009
/// necessary, however.
1010
enum Body {
1011
Raw(Vec<u8>),
1012
Call(FunctionId),
1013
RefFunc(FunctionId),
1014
}
1015
1016
impl Function {
1017
fn new(export: Option<String>, ty: u32) -> Function {
1018
Function {
1019
filled_in: false,
1020
ty,
1021
locals: Vec::new(),
1022
export,
1023
body: Vec::new(),
1024
}
1025
}
1026
}
1027
1028
impl Helper {
1029
fn core_type(
1030
&self,
1031
types: &ComponentTypesBuilder,
1032
core_types: &mut core_types::CoreTypes,
1033
) -> u32 {
1034
let mut params = Vec::new();
1035
let mut results = Vec::new();
1036
// The source type being translated is always pushed onto the
1037
// parameters first, either a pointer for memory or its flat
1038
// representation.
1039
self.src.push_flat(&mut params, types);
1040
1041
// The destination type goes into the parameter list if it's from
1042
// memory or otherwise is the result of the function itself for a
1043
// stack-based representation.
1044
match self.dst.loc {
1045
HelperLocation::Stack => self.dst.push_flat(&mut results, types),
1046
HelperLocation::Memory => params.push(self.dst.opts.data_model.unwrap_memory().ptr()),
1047
HelperLocation::StructField | HelperLocation::ArrayElement => todo!("CM+GC"),
1048
}
1049
1050
core_types.function(&params, &results)
1051
}
1052
}
1053
1054
impl HelperType {
1055
fn push_flat(&self, dst: &mut Vec<ValType>, types: &ComponentTypesBuilder) {
1056
match self.loc {
1057
HelperLocation::Stack => {
1058
for ty in self.opts.flat_types(&self.ty, types).unwrap() {
1059
dst.push((*ty).into());
1060
}
1061
}
1062
HelperLocation::Memory => {
1063
dst.push(self.opts.data_model.unwrap_memory().ptr());
1064
}
1065
HelperLocation::StructField | HelperLocation::ArrayElement => todo!("CM+GC"),
1066
}
1067
}
1068
}
1069
1070