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