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