//! Wasmtime's Fused Adapter Compiler of Trampolines (FACT)1//!2//! This module contains a compiler which emits trampolines to implement fused3//! adapters for the component model. A fused adapter is when a core wasm4//! function is lifted from one component instance and then lowered into another5//! component instance. This communication between components is well-defined by6//! the spec and ends up creating what's called a "fused adapter".7//!8//! Adapters are currently implemented with WebAssembly modules. This submodule9//! will generate a core wasm binary which contains the adapters specified10//! during compilation. The actual wasm is then later processed by standard11//! paths in Wasmtime to create native machine code and runtime representations12//! of modules.13//!14//! Note that identification of precisely what goes into an adapter module is15//! not handled in this file, instead that's all done in `translate/adapt.rs`.16//! Otherwise this module is only responsible for taking a set of adapters and17//! their imports and then generating a core wasm module to implement all of18//! that.1920use crate::component::dfg::CoreDef;21use crate::component::{22Adapter, AdapterOptions as AdapterOptionsDfg, ComponentTypesBuilder, FlatType, InterfaceType,23RuntimeComponentInstanceIndex, StringEncoding, Transcode, TypeFuncIndex,24};25use crate::fact::transcode::Transcoder;26use crate::{EntityRef, FuncIndex, GlobalIndex, MemoryIndex, PrimaryMap};27use crate::{ModuleInternedTypeIndex, prelude::*};28use std::borrow::Cow;29use std::collections::HashMap;30use wasm_encoder::*;3132mod core_types;33mod signature;34mod trampoline;35mod transcode;36mod traps;3738/// Fixed parameter types for the `prepare_call` built-in function.39///40/// Note that `prepare_call` also takes a variable number of parameters in41/// addition to these, determined by the signature of the function for which42/// we're generating an adapter.43pub static PREPARE_CALL_FIXED_PARAMS: &[ValType] = &[44ValType::FUNCREF, // start45ValType::FUNCREF, // return46ValType::I32, // caller_instance47ValType::I32, // callee_instance48ValType::I32, // task_return_type49ValType::I32, // string_encoding50ValType::I32, // result_count_or_max_if_async51];5253/// Representation of an adapter module.54pub struct Module<'a> {55/// Whether or not debug code is inserted into the adapters themselves.56debug: bool,57/// Type information from the creator of this `Module`58types: &'a ComponentTypesBuilder,5960/// Core wasm type section that's incrementally built61core_types: core_types::CoreTypes,6263/// Core wasm import section which is built as adapters are inserted. Note64/// that imports here are intern'd to avoid duplicate imports of the same65/// item.66core_imports: ImportSection,67/// Final list of imports that this module ended up using, in the same order68/// as the imports in the import section.69imports: Vec<Import>,70/// Intern'd imports and what index they were assigned. Note that this map71/// covers all the index spaces for imports, not just one.72imported: HashMap<CoreDef, usize>,73/// Intern'd transcoders and what index they were assigned.74imported_transcoders: HashMap<Transcoder, FuncIndex>,7576/// Cached versions of imported trampolines for working with resources.77imported_resource_transfer_own: Option<FuncIndex>,78imported_resource_transfer_borrow: Option<FuncIndex>,79imported_resource_enter_call: Option<FuncIndex>,80imported_resource_exit_call: Option<FuncIndex>,8182// Cached versions of imported trampolines for working with the async ABI.83imported_async_start_calls: HashMap<(Option<FuncIndex>, Option<FuncIndex>), FuncIndex>,8485// Cached versions of imported trampolines for working with `stream`s,86// `future`s, and `error-context`s.87imported_future_transfer: Option<FuncIndex>,88imported_stream_transfer: Option<FuncIndex>,89imported_error_context_transfer: Option<FuncIndex>,9091// Current status of index spaces from the imports generated so far.92imported_funcs: PrimaryMap<FuncIndex, Option<CoreDef>>,93imported_memories: PrimaryMap<MemoryIndex, CoreDef>,94imported_globals: PrimaryMap<GlobalIndex, CoreDef>,9596funcs: PrimaryMap<FunctionId, Function>,97helper_funcs: HashMap<Helper, FunctionId>,98helper_worklist: Vec<(FunctionId, Helper)>,99100exports: Vec<(u32, String)>,101}102103struct AdapterData {104/// Export name of this adapter105name: String,106/// Options specified during the `canon lift` operation107lift: AdapterOptions,108/// Options specified during the `canon lower` operation109lower: AdapterOptions,110/// The core wasm function that this adapter will be calling (the original111/// function that was `canon lift`'d)112callee: FuncIndex,113/// FIXME(#4185) should be plumbed and handled as part of the new reentrance114/// rules not yet implemented here.115called_as_export: bool,116}117118/// Configuration options which apply at the "global adapter" level.119///120/// These options are typically unique per-adapter and generally aren't needed121/// when translating recursive types within an adapter.122struct AdapterOptions {123instance: RuntimeComponentInstanceIndex,124/// The ascribed type of this adapter.125ty: TypeFuncIndex,126/// The global that represents the instance flags for where this adapter127/// came from.128flags: GlobalIndex,129/// The configured post-return function, if any.130post_return: Option<FuncIndex>,131/// Other, more general, options configured.132options: Options,133}134135#[derive(PartialEq, Eq, Hash, Copy, Clone)]136/// Linear memory.137struct LinearMemoryOptions {138/// Whether or not the `memory` field, if present, is a 64-bit memory.139memory64: bool,140/// An optionally-specified memory where values may travel through for141/// types like lists.142memory: Option<MemoryIndex>,143/// An optionally-specified function to be used to allocate space for144/// types such as strings as they go into a module.145realloc: Option<FuncIndex>,146}147148impl LinearMemoryOptions {149fn ptr(&self) -> ValType {150if self.memory64 {151ValType::I64152} else {153ValType::I32154}155}156157fn ptr_size(&self) -> u8 {158if self.memory64 { 8 } else { 4 }159}160}161162/// The data model for objects passed through an adapter.163#[derive(PartialEq, Eq, Hash, Copy, Clone)]164enum DataModel {165Gc {},166LinearMemory(LinearMemoryOptions),167}168169impl DataModel {170#[track_caller]171fn unwrap_memory(&self) -> &LinearMemoryOptions {172match self {173DataModel::Gc {} => panic!("`unwrap_memory` on GC"),174DataModel::LinearMemory(opts) => opts,175}176}177}178179/// This type is split out of `AdapterOptions` and is specifically used to180/// deduplicate translation functions within a module. Consequently this has181/// as few fields as possible to minimize the number of functions generated182/// within an adapter module.183#[derive(PartialEq, Eq, Hash, Copy, Clone)]184struct Options {185/// The encoding that strings use from this adapter.186string_encoding: StringEncoding,187callback: Option<FuncIndex>,188async_: bool,189core_type: ModuleInternedTypeIndex,190data_model: DataModel,191}192193/// Representation of a "helper function" which may be generated as part of194/// generating an adapter trampoline.195///196/// Helper functions are created when inlining the translation for a type in its197/// entirety would make a function excessively large. This is currently done via198/// a simple fuel/cost heuristic based on the type being translated but may get199/// fancier over time.200#[derive(Copy, Clone, PartialEq, Eq, Hash)]201struct Helper {202/// Metadata about the source type of what's being translated.203src: HelperType,204/// Metadata about the destination type which is being translated to.205dst: HelperType,206}207208/// Information about a source or destination type in a `Helper` which is209/// generated.210#[derive(Copy, Clone, PartialEq, Eq, Hash)]211struct HelperType {212/// The concrete type being translated.213ty: InterfaceType,214/// The configuration options (memory, etc) for the adapter.215opts: Options,216/// Where the type is located (either the stack or in memory)217loc: HelperLocation,218}219220/// Where a `HelperType` is located, dictating the signature of the helper221/// function.222#[derive(Copy, Clone, PartialEq, Eq, Hash)]223enum HelperLocation {224/// Located on the stack in wasm locals.225Stack,226/// Located in linear memory as configured by `opts`.227Memory,228/// Located in a GC struct field.229#[expect(dead_code, reason = "CM+GC is still WIP")]230StructField,231/// Located in a GC array element.232#[expect(dead_code, reason = "CM+GC is still WIP")]233ArrayElement,234}235236impl<'a> Module<'a> {237/// Creates an empty module.238pub fn new(types: &'a ComponentTypesBuilder, debug: bool) -> Module<'a> {239Module {240debug,241types,242core_types: Default::default(),243core_imports: Default::default(),244imported: Default::default(),245imports: Default::default(),246imported_transcoders: Default::default(),247imported_funcs: PrimaryMap::new(),248imported_memories: PrimaryMap::new(),249imported_globals: PrimaryMap::new(),250funcs: PrimaryMap::new(),251helper_funcs: HashMap::new(),252helper_worklist: Vec::new(),253imported_resource_transfer_own: None,254imported_resource_transfer_borrow: None,255imported_resource_enter_call: None,256imported_resource_exit_call: None,257imported_async_start_calls: HashMap::new(),258imported_future_transfer: None,259imported_stream_transfer: None,260imported_error_context_transfer: None,261exports: Vec::new(),262}263}264265/// Registers a new adapter within this adapter module.266///267/// The `name` provided is the export name of the adapter from the final268/// module, and `adapter` contains all metadata necessary for compilation.269pub fn adapt(&mut self, name: &str, adapter: &Adapter) {270// Import any items required by the various canonical options271// (memories, reallocs, etc)272let mut lift = self.import_options(adapter.lift_ty, &adapter.lift_options);273let lower = self.import_options(adapter.lower_ty, &adapter.lower_options);274275// Lowering options are not allowed to specify post-return as per the276// current canonical abi specification.277assert!(adapter.lower_options.post_return.is_none());278279// Import the core wasm function which was lifted using its appropriate280// signature since the exported function this adapter generates will281// call the lifted function.282let signature = self.types.signature(&lift);283let ty = self284.core_types285.function(&signature.params, &signature.results);286let callee = self.import_func("callee", name, ty, adapter.func.clone());287288// Handle post-return specifically here where we have `core_ty` and the289// results of `core_ty` are the parameters to the post-return function.290lift.post_return = adapter.lift_options.post_return.as_ref().map(|func| {291let ty = self.core_types.function(&signature.results, &[]);292self.import_func("post_return", name, ty, func.clone())293});294295// This will internally create the adapter as specified and append296// anything necessary to `self.funcs`.297trampoline::compile(298self,299&AdapterData {300name: name.to_string(),301lift,302lower,303callee,304// FIXME(#4185) should be plumbed and handled as part of the new305// reentrance rules not yet implemented here.306called_as_export: true,307},308);309310while let Some((result, helper)) = self.helper_worklist.pop() {311trampoline::compile_helper(self, result, helper);312}313}314315fn import_options(&mut self, ty: TypeFuncIndex, options: &AdapterOptionsDfg) -> AdapterOptions {316let AdapterOptionsDfg {317instance,318string_encoding,319post_return: _, // handled above320callback,321async_,322core_type,323data_model,324} = options;325326let flags = self.import_global(327"flags",328&format!("instance{}", instance.as_u32()),329GlobalType {330val_type: ValType::I32,331mutable: true,332shared: false,333},334CoreDef::InstanceFlags(*instance),335);336337let data_model = match data_model {338crate::component::DataModel::Gc {} => DataModel::Gc {},339crate::component::DataModel::LinearMemory {340memory,341memory64,342realloc,343} => {344let memory = memory.as_ref().map(|memory| {345self.import_memory(346"memory",347&format!("m{}", self.imported_memories.len()),348MemoryType {349minimum: 0,350maximum: None,351shared: false,352memory64: *memory64,353page_size_log2: None,354},355memory.clone().into(),356)357});358let realloc = realloc.as_ref().map(|func| {359let ptr = if *memory64 {360ValType::I64361} else {362ValType::I32363};364let ty = self.core_types.function(&[ptr, ptr, ptr, ptr], &[ptr]);365self.import_func(366"realloc",367&format!("f{}", self.imported_funcs.len()),368ty,369func.clone(),370)371});372DataModel::LinearMemory(LinearMemoryOptions {373memory64: *memory64,374memory,375realloc,376})377}378};379380let callback = callback.as_ref().map(|func| {381let ty = self382.core_types383.function(&[ValType::I32, ValType::I32, ValType::I32], &[ValType::I32]);384self.import_func(385"callback",386&format!("f{}", self.imported_funcs.len()),387ty,388func.clone(),389)390});391392AdapterOptions {393instance: *instance,394ty,395flags,396post_return: None,397options: Options {398string_encoding: *string_encoding,399callback,400async_: *async_,401core_type: *core_type,402data_model,403},404}405}406407fn import_func(&mut self, module: &str, name: &str, ty: u32, def: CoreDef) -> FuncIndex {408self.import(module, name, EntityType::Function(ty), def, |m| {409&mut m.imported_funcs410})411}412413fn import_global(414&mut self,415module: &str,416name: &str,417ty: GlobalType,418def: CoreDef,419) -> GlobalIndex {420self.import(module, name, EntityType::Global(ty), def, |m| {421&mut m.imported_globals422})423}424425fn import_memory(426&mut self,427module: &str,428name: &str,429ty: MemoryType,430def: CoreDef,431) -> MemoryIndex {432self.import(module, name, EntityType::Memory(ty), def, |m| {433&mut m.imported_memories434})435}436437fn import<K: EntityRef, V: From<CoreDef>>(438&mut self,439module: &str,440name: &str,441ty: EntityType,442def: CoreDef,443map: impl FnOnce(&mut Self) -> &mut PrimaryMap<K, V>,444) -> K {445if let Some(prev) = self.imported.get(&def) {446return K::new(*prev);447}448let idx = map(self).push(def.clone().into());449self.core_imports.import(module, name, ty);450self.imported.insert(def.clone(), idx.index());451self.imports.push(Import::CoreDef(def));452idx453}454455fn import_transcoder(&mut self, transcoder: transcode::Transcoder) -> FuncIndex {456*self457.imported_transcoders458.entry(transcoder)459.or_insert_with(|| {460// Add the import to the core wasm import section...461let name = transcoder.name();462let ty = transcoder.ty(&mut self.core_types);463self.core_imports.import("transcode", &name, ty);464465// ... and also record the metadata for what this import466// corresponds to.467let from = self.imported_memories[transcoder.from_memory].clone();468let to = self.imported_memories[transcoder.to_memory].clone();469self.imports.push(Import::Transcode {470op: transcoder.op,471from,472from64: transcoder.from_memory64,473to,474to64: transcoder.to_memory64,475});476477self.imported_funcs.push(None)478})479}480481fn import_simple(482&mut self,483module: &str,484name: &str,485params: &[ValType],486results: &[ValType],487import: Import,488get: impl Fn(&mut Self) -> &mut Option<FuncIndex>,489) -> FuncIndex {490self.import_simple_get_and_set(491module,492name,493params,494results,495import,496|me| *get(me),497|me, v| *get(me) = Some(v),498)499}500501fn import_simple_get_and_set(502&mut self,503module: &str,504name: &str,505params: &[ValType],506results: &[ValType],507import: Import,508get: impl Fn(&mut Self) -> Option<FuncIndex>,509set: impl Fn(&mut Self, FuncIndex),510) -> FuncIndex {511if let Some(idx) = get(self) {512return idx;513}514let ty = self.core_types.function(params, results);515let ty = EntityType::Function(ty);516self.core_imports.import(module, name, ty);517518self.imports.push(import);519let idx = self.imported_funcs.push(None);520set(self, idx);521idx522}523524/// Import a host built-in function to set up a subtask for a sync-lowered525/// import call to an async-lifted export.526///527/// Given that the callee may exert backpressure before the host can copy528/// the parameters, the adapter must use this function to set up the subtask529/// and stash the parameters as part of that subtask until any backpressure530/// has cleared.531fn import_prepare_call(532&mut self,533suffix: &str,534params: &[ValType],535memory: Option<MemoryIndex>,536) -> FuncIndex {537let ty = self.core_types.function(538&PREPARE_CALL_FIXED_PARAMS539.iter()540.copied()541.chain(params.iter().copied())542.collect::<Vec<_>>(),543&[],544);545self.core_imports.import(546"sync",547&format!("[prepare-call]{suffix}"),548EntityType::Function(ty),549);550let import = Import::PrepareCall {551memory: memory.map(|v| self.imported_memories[v].clone()),552};553self.imports.push(import);554self.imported_funcs.push(None)555}556557/// Import a host built-in function to start a subtask for a sync-lowered558/// import call to an async-lifted export.559///560/// This call with block until the subtask has produced result(s) via the561/// `task.return` intrinsic.562///563/// Note that this could potentially be combined with the `sync-prepare`564/// built-in into a single built-in function that does both jobs. However,565/// we've kept them separate to allow a future optimization where the caller566/// calls the callee directly rather than using `sync-start` to have the host567/// do it.568fn import_sync_start_call(569&mut self,570suffix: &str,571callback: Option<FuncIndex>,572results: &[ValType],573) -> FuncIndex {574let ty = self575.core_types576.function(&[ValType::FUNCREF, ValType::I32], results);577self.core_imports.import(578"sync",579&format!("[start-call]{suffix}"),580EntityType::Function(ty),581);582let import = Import::SyncStartCall {583callback: callback584.map(|callback| self.imported_funcs.get(callback).unwrap().clone().unwrap()),585};586self.imports.push(import);587self.imported_funcs.push(None)588}589590/// Import a host built-in function to start a subtask for an async-lowered591/// import call to an async- or sync-lifted export.592///593/// Note that this could potentially be combined with the `async-prepare`594/// built-in into a single built-in function that does both jobs. However,595/// we've kept them separate to allow a future optimization where the caller596/// calls the callee directly rather than using `async-start` to have the597/// host do it.598fn import_async_start_call(599&mut self,600suffix: &str,601callback: Option<FuncIndex>,602post_return: Option<FuncIndex>,603) -> FuncIndex {604self.import_simple_get_and_set(605"async",606&format!("[start-call]{suffix}"),607&[ValType::FUNCREF, ValType::I32, ValType::I32, ValType::I32],608&[ValType::I32],609Import::AsyncStartCall {610callback: callback611.map(|callback| self.imported_funcs.get(callback).unwrap().clone().unwrap()),612post_return: post_return.map(|post_return| {613self.imported_funcs614.get(post_return)615.unwrap()616.clone()617.unwrap()618}),619},620|me| {621me.imported_async_start_calls622.get(&(callback, post_return))623.copied()624},625|me, v| {626assert!(627me.imported_async_start_calls628.insert((callback, post_return), v)629.is_none()630)631},632)633}634635fn import_future_transfer(&mut self) -> FuncIndex {636self.import_simple(637"future",638"transfer",639&[ValType::I32; 3],640&[ValType::I32],641Import::FutureTransfer,642|me| &mut me.imported_future_transfer,643)644}645646fn import_stream_transfer(&mut self) -> FuncIndex {647self.import_simple(648"stream",649"transfer",650&[ValType::I32; 3],651&[ValType::I32],652Import::StreamTransfer,653|me| &mut me.imported_stream_transfer,654)655}656657fn import_error_context_transfer(&mut self) -> FuncIndex {658self.import_simple(659"error-context",660"transfer",661&[ValType::I32; 3],662&[ValType::I32],663Import::ErrorContextTransfer,664|me| &mut me.imported_error_context_transfer,665)666}667668fn import_resource_transfer_own(&mut self) -> FuncIndex {669self.import_simple(670"resource",671"transfer-own",672&[ValType::I32, ValType::I32, ValType::I32],673&[ValType::I32],674Import::ResourceTransferOwn,675|me| &mut me.imported_resource_transfer_own,676)677}678679fn import_resource_transfer_borrow(&mut self) -> FuncIndex {680self.import_simple(681"resource",682"transfer-borrow",683&[ValType::I32, ValType::I32, ValType::I32],684&[ValType::I32],685Import::ResourceTransferBorrow,686|me| &mut me.imported_resource_transfer_borrow,687)688}689690fn import_resource_enter_call(&mut self) -> FuncIndex {691self.import_simple(692"resource",693"enter-call",694&[],695&[],696Import::ResourceEnterCall,697|me| &mut me.imported_resource_enter_call,698)699}700701fn import_resource_exit_call(&mut self) -> FuncIndex {702self.import_simple(703"resource",704"exit-call",705&[],706&[],707Import::ResourceExitCall,708|me| &mut me.imported_resource_exit_call,709)710}711712fn translate_helper(&mut self, helper: Helper) -> FunctionId {713*self.helper_funcs.entry(helper).or_insert_with(|| {714// Generate a fresh `Function` with a unique id for what we're about to715// generate.716let ty = helper.core_type(self.types, &mut self.core_types);717let id = self.funcs.push(Function::new(None, ty));718self.helper_worklist.push((id, helper));719id720})721}722723/// Encodes this module into a WebAssembly binary.724pub fn encode(&mut self) -> Vec<u8> {725// Build the function/export sections of the wasm module in a first pass726// which will assign a final `FuncIndex` to all functions defined in727// `self.funcs`.728let mut funcs = FunctionSection::new();729let mut exports = ExportSection::new();730let mut id_to_index = PrimaryMap::<FunctionId, FuncIndex>::new();731for (id, func) in self.funcs.iter() {732assert!(func.filled_in);733let idx = FuncIndex::from_u32(self.imported_funcs.next_key().as_u32() + id.as_u32());734let id2 = id_to_index.push(idx);735assert_eq!(id2, id);736737funcs.function(func.ty);738739if let Some(name) = &func.export {740exports.export(name, ExportKind::Func, idx.as_u32());741}742}743for (idx, name) in &self.exports {744exports.export(name, ExportKind::Func, *idx);745}746747// With all functions numbered the fragments of the body of each748// function can be assigned into one final adapter function.749let mut code = CodeSection::new();750let mut traps = traps::TrapSection::default();751for (id, func) in self.funcs.iter() {752let mut func_traps = Vec::new();753let mut body = Vec::new();754755// Encode all locals used for this function756func.locals.len().encode(&mut body);757for (count, ty) in func.locals.iter() {758count.encode(&mut body);759ty.encode(&mut body);760}761762// Then encode each "chunk" of a body which may have optional traps763// specified within it. Traps get offset by the current length of764// the body and otherwise our `Call` instructions are "relocated"765// here to the final function index.766for chunk in func.body.iter() {767match chunk {768Body::Raw(code, traps) => {769let start = body.len();770body.extend_from_slice(code);771for (offset, trap) in traps {772func_traps.push((start + offset, *trap));773}774}775Body::Call(id) => {776Instruction::Call(id_to_index[*id].as_u32()).encode(&mut body);777}778Body::RefFunc(id) => {779Instruction::RefFunc(id_to_index[*id].as_u32()).encode(&mut body);780}781}782}783code.raw(&body);784traps.append(id_to_index[id].as_u32(), func_traps);785}786787let traps = traps.finish();788789let mut result = wasm_encoder::Module::new();790result.section(&self.core_types.section);791result.section(&self.core_imports);792result.section(&funcs);793result.section(&exports);794result.section(&code);795if self.debug {796result.section(&CustomSection {797name: "wasmtime-trampoline-traps".into(),798data: Cow::Borrowed(&traps),799});800}801result.finish()802}803804/// Returns the imports that were used, in order, to create this adapter805/// module.806pub fn imports(&self) -> &[Import] {807&self.imports808}809}810811/// Possible imports into an adapter module.812#[derive(Clone)]813pub enum Import {814/// A definition required in the configuration of an `Adapter`.815CoreDef(CoreDef),816/// A transcoding function from the host to convert between string encodings.817Transcode {818/// The transcoding operation this performs.819op: Transcode,820/// The memory being read821from: CoreDef,822/// Whether or not `from` is a 64-bit memory823from64: bool,824/// The memory being written825to: CoreDef,826/// Whether or not `to` is a 64-bit memory827to64: bool,828},829/// Transfers an owned resource from one table to another.830ResourceTransferOwn,831/// Transfers a borrowed resource from one table to another.832ResourceTransferBorrow,833/// Sets up entry metadata for a borrow resources when a call starts.834ResourceEnterCall,835/// Tears down a previous entry and handles checking borrow-related836/// metadata.837ResourceExitCall,838/// An intrinsic used by FACT-generated modules to begin a call involving839/// an async-lowered import and/or an async-lifted export.840PrepareCall {841/// The memory used to verify that the memory specified for the842/// `task.return` that is called at runtime (if any) matches the one843/// specified in the lifted export.844memory: Option<CoreDef>,845},846/// An intrinsic used by FACT-generated modules to complete a call involving847/// a sync-lowered import and async-lifted export.848SyncStartCall {849/// The callee's callback function, if any.850callback: Option<CoreDef>,851},852/// An intrinsic used by FACT-generated modules to complete a call involving853/// an async-lowered import function.854AsyncStartCall {855/// The callee's callback function, if any.856callback: Option<CoreDef>,857858/// The callee's post-return function, if any.859post_return: Option<CoreDef>,860},861/// An intrinisic used by FACT-generated modules to (partially or entirely) transfer862/// ownership of a `future`.863FutureTransfer,864/// An intrinisic used by FACT-generated modules to (partially or entirely) transfer865/// ownership of a `stream`.866StreamTransfer,867/// An intrinisic used by FACT-generated modules to (partially or entirely) transfer868/// ownership of an `error-context`.869ErrorContextTransfer,870}871872impl Options {873fn flat_types<'a>(874&self,875ty: &InterfaceType,876types: &'a ComponentTypesBuilder,877) -> Option<&'a [FlatType]> {878let flat = types.flat_types(ty)?;879match self.data_model {880DataModel::Gc {} => todo!("CM+GC"),881DataModel::LinearMemory(mem_opts) => Some(if mem_opts.memory64 {882flat.memory64883} else {884flat.memory32885}),886}887}888}889890/// Temporary index which is not the same as `FuncIndex`.891///892/// This represents the nth generated function in the adapter module where the893/// final index of the function is not known at the time of generation since894/// more imports may be discovered (specifically string transcoders).895#[derive(Debug, Copy, Clone, PartialEq, Eq)]896struct FunctionId(u32);897cranelift_entity::entity_impl!(FunctionId);898899/// A generated function to be added to an adapter module.900///901/// At least one function is created per-adapter and depending on the type902/// hierarchy multiple functions may be generated per-adapter.903struct Function {904/// Whether or not the `body` has been finished.905///906/// Functions are added to a `Module` before they're defined so this is used907/// to assert that the function was in fact actually filled in by the908/// time we reach `Module::encode`.909filled_in: bool,910911/// The type signature that this function has, as an index into the core912/// wasm type index space of the generated adapter module.913ty: u32,914915/// The locals that are used by this function, organized by the number of916/// types of each local.917locals: Vec<(u32, ValType)>,918919/// If specified, the export name of this function.920export: Option<String>,921922/// The contents of the function.923///924/// See `Body` for more information, and the `Vec` here represents the925/// concatenation of all the `Body` fragments.926body: Vec<Body>,927}928929/// Representation of a fragment of the body of a core wasm function generated930/// for adapters.931///932/// This variant comes in one of two flavors:933///934/// 1. First a `Raw` variant is used to contain general instructions for the935/// wasm function. This is populated by `Compiler::instruction` primarily.936/// This also comes with a list of traps. and the byte offset within the937/// first vector of where the trap information applies to.938///939/// 2. A `Call` instruction variant for a `FunctionId` where the final940/// `FuncIndex` isn't known until emission time.941///942/// The purpose of this representation is the `Body::Call` variant. This can't943/// be encoded as an instruction when it's generated due to not knowing the944/// final index of the function being called. During `Module::encode`, however,945/// all indices are known and `Body::Call` is turned into a final946/// `Instruction::Call`.947///948/// One other possible representation in the future would be to encode a `Call`949/// instruction with a 5-byte leb to fill in later, but for now this felt950/// easier to represent. A 5-byte leb may be more efficient at compile-time if951/// necessary, however.952enum Body {953Raw(Vec<u8>, Vec<(usize, traps::Trap)>),954Call(FunctionId),955RefFunc(FunctionId),956}957958impl Function {959fn new(export: Option<String>, ty: u32) -> Function {960Function {961filled_in: false,962ty,963locals: Vec::new(),964export,965body: Vec::new(),966}967}968}969970impl Helper {971fn core_type(972&self,973types: &ComponentTypesBuilder,974core_types: &mut core_types::CoreTypes,975) -> u32 {976let mut params = Vec::new();977let mut results = Vec::new();978// The source type being translated is always pushed onto the979// parameters first, either a pointer for memory or its flat980// representation.981self.src.push_flat(&mut params, types);982983// The destination type goes into the parameter list if it's from984// memory or otherwise is the result of the function itself for a985// stack-based representation.986match self.dst.loc {987HelperLocation::Stack => self.dst.push_flat(&mut results, types),988HelperLocation::Memory => params.push(self.dst.opts.data_model.unwrap_memory().ptr()),989HelperLocation::StructField | HelperLocation::ArrayElement => todo!("CM+GC"),990}991992core_types.function(¶ms, &results)993}994}995996impl HelperType {997fn push_flat(&self, dst: &mut Vec<ValType>, types: &ComponentTypesBuilder) {998match self.loc {999HelperLocation::Stack => {1000for ty in self.opts.flat_types(&self.ty, types).unwrap() {1001dst.push((*ty).into());1002}1003}1004HelperLocation::Memory => {1005dst.push(self.opts.data_model.unwrap_memory().ptr());1006}1007HelperLocation::StructField | HelperLocation::ArrayElement => todo!("CM+GC"),1008}1009}1010}101110121013