Skip to main content

wasmtime_environ/fact/
trampoline.rs

1//! Low-level compilation of an fused adapter function.
2//!
3//! This module is tasked with the top-level `compile` function which creates a
4//! single WebAssembly function which will perform the steps of the fused
5//! adapter for an `AdapterData` provided. This is the "meat" of compilation
6//! where the validation of the canonical ABI or similar all happens to
7//! translate arguments from one module to another.
8//!
9//! ## Traps and their ordering
10//!
11//! Currently this compiler is pretty "loose" about the ordering of precisely
12//! what trap happens where. The main reason for this is that to core wasm all
13//! traps are the same and for fused adapters if a trap happens no intermediate
14//! side effects are visible (as designed by the canonical ABI itself). For this
15//! it's important to note that some of the precise choices of control flow here
16//! can be somewhat arbitrary, an intentional decision.
17
18use crate::component::{
19    CanonicalAbiInfo, ComponentTypesBuilder, FixedEncoding as FE, FlatType, InterfaceType,
20    MAX_FLAT_ASYNC_PARAMS, MAX_FLAT_PARAMS, PREPARE_ASYNC_NO_RESULT, PREPARE_ASYNC_WITH_RESULT,
21    START_FLAG_ASYNC_CALLEE, START_FLAG_ASYNC_CALLER, StringEncoding, Transcode,
22    TypeComponentLocalErrorContextTableIndex, TypeEnumIndex, TypeFixedLengthListIndex,
23    TypeFlagsIndex, TypeFutureTableIndex, TypeListIndex, TypeMapIndex, TypeOptionIndex,
24    TypeRecordIndex, TypeResourceTableIndex, TypeResultIndex, TypeStreamTableIndex, TypeTupleIndex,
25    TypeVariantIndex, VariantInfo,
26};
27use crate::fact::signature::Signature;
28use crate::fact::transcode::Transcoder;
29use crate::fact::{
30    AdapterData, Body, Function, FunctionId, Helper, HelperLocation, HelperType,
31    LinearMemoryOptions, Module, Options,
32};
33use crate::prelude::*;
34use crate::{FuncIndex, GlobalIndex, IndexType, NUM_COMPONENT_CONTEXT_SLOTS, Trap};
35use std::collections::HashMap;
36use std::mem;
37use std::ops::Range;
38use wasm_encoder::{BlockType, Catch, Encode, Instruction, Instruction::*, MemArg, ValType};
39use wasmtime_component_util::{DiscriminantSize, FlagsSize};
40
41use super::DataModel;
42
43const MAX_STRING_BYTE_LENGTH: u32 = (1 << 31) - 1;
44const UTF16_TAG: u32 = 1 << 31;
45
46/// This value is arbitrarily chosen and should be fine to change at any time,
47/// it just seemed like a halfway reasonable starting point.
48const INITIAL_FUEL: usize = 1_000;
49
50struct Compiler<'a, 'b> {
51    types: &'a ComponentTypesBuilder,
52    module: &'b mut Module<'a>,
53    result: FunctionId,
54
55    /// The encoded WebAssembly function body so far, not including locals.
56    code: Vec<u8>,
57
58    /// Total number of locals generated so far.
59    nlocals: u32,
60
61    /// Locals partitioned by type which are not currently in use.
62    free_locals: HashMap<ValType, Vec<u32>>,
63
64    /// A heuristic which is intended to limit the size of a generated function
65    /// to a certain maximum to avoid generating arbitrarily large functions.
66    ///
67    /// This fuel counter is decremented each time `translate` is called and
68    /// when fuel is entirely consumed further translations, if necessary, will
69    /// be done through calls to other functions in the module. This is intended
70    /// to be a heuristic to split up the main function into theoretically
71    /// reusable portions.
72    fuel: usize,
73
74    /// Indicates whether an "enter call" should be emitted in the generated
75    /// function with a call to `Resource{Enter,Exit}Call` at the beginning and
76    /// end of the function for tracking of information related to borrowed
77    /// resources.
78    emit_resource_call: bool,
79}
80
81pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) {
82    fn compiler<'a, 'b>(
83        module: &'b mut Module<'a>,
84        adapter: &AdapterData,
85    ) -> (Compiler<'a, 'b>, Signature, Signature) {
86        let lower_sig = module.types.signature(&adapter.lower);
87        let lift_sig = module.types.signature(&adapter.lift);
88        let ty = module
89            .core_types
90            .function(&lower_sig.params, &lower_sig.results);
91        let result = module
92            .funcs
93            .push(Function::new(Some(adapter.name.clone()), ty));
94
95        // If this type signature contains any borrowed resources then invocations
96        // of enter/exit call for resource-related metadata tracking must be used.
97        // It shouldn't matter whether the lower/lift signature is used here as both
98        // should return the same answer.
99        let emit_resource_call = module.types.contains_borrow_resource(&adapter.lower);
100        assert_eq!(
101            emit_resource_call,
102            module.types.contains_borrow_resource(&adapter.lift)
103        );
104
105        (
106            Compiler::new(
107                module,
108                result,
109                lower_sig.params.len() as u32,
110                emit_resource_call,
111            ),
112            lower_sig,
113            lift_sig,
114        )
115    }
116
117    // Disallow same-instance adapters. This is, as of the time of this writing,
118    // technically not spec-conformant. This is something we'll want to
119    // reconcile with the upstream specification because without this check the
120    // precise order of lifts/lowers is visible to the guest which is generally
121    // not something that the spec desires.
122    if adapter.lift.instance == adapter.lower.instance {
123        let (mut compiler, _, _) = compiler(module, adapter);
124        compiler.trap(Trap::CannotEnterComponent);
125        compiler.finish();
126        return;
127    }
128
129    // This closure compiles a function to be exported to the host which host to
130    // lift the parameters from the caller and lower them to the callee.
131    //
132    // This allows the host to delay copying the parameters until the callee
133    // signals readiness by clearing its backpressure flag.
134    let async_start_adapter = |module: &mut Module| {
135        let sig = module
136            .types
137            .async_start_signature(&adapter.lower, &adapter.lift);
138        let ty = module.core_types.function(&sig.params, &sig.results);
139        let result = module.funcs.push(Function::new(
140            Some(format!("[async-start]{}", adapter.name)),
141            ty,
142        ));
143
144        Compiler::new(module, result, sig.params.len() as u32, false)
145            .compile_async_start_adapter(adapter, &sig);
146
147        result
148    };
149
150    // This closure compiles a function to be exported by the adapter module and
151    // called by the host to lift the results from the callee and lower them to
152    // the caller.
153    //
154    // Given that async-lifted exports return their results via the
155    // `task.return` intrinsic, the host will need to copy the results from
156    // callee to caller when that intrinsic is called rather than when the
157    // callee task fully completes (which may happen much later).
158    let async_return_adapter = |module: &mut Module| {
159        let sig = module
160            .types
161            .async_return_signature(&adapter.lower, &adapter.lift);
162        let ty = module.core_types.function(&sig.params, &sig.results);
163        let result = module.funcs.push(Function::new(
164            Some(format!("[async-return]{}", adapter.name)),
165            ty,
166        ));
167
168        Compiler::new(module, result, sig.params.len() as u32, false)
169            .compile_async_return_adapter(adapter, &sig);
170
171        result
172    };
173
174    let lower_async = adapter.lower.options.async_;
175    let lift_async = adapter.lift.options.async_;
176    if !lower_async && !lift_async && !module.types[adapter.lift.ty].async_ {
177        // We can adapt sync->sync case with only minimal use of intrinsics,
178        // e.g. resource enter and exit calls as needed.
179        let (compiler, lower_sig, lift_sig) = compiler(module, adapter);
180        compiler.compile_sync_to_sync_adapter(adapter, &lower_sig, &lift_sig);
181        return;
182    }
183
184    assert!(module.tunables.concurrency_support);
185
186    // All other cases are mediated by the host: the caller and/or callee use
187    // the async ABI, or the callee has an async function type (in which case
188    // it's subject to backpressure and exclusive-entry checks even if both
189    // sides are otherwise sync).  For these we compile a couple of helper
190    // functions:
191    //
192    // - `async-start`: copies the parameters from the caller to the callee
193    // - `async-return`: copies the result from the callee to the caller
194    //
195    // The host calls `async-start` once the callee is ready to handle a new
196    // call (e.g. it has no backpressure and, if required, its instance isn't
197    // exclusively held by another task), which may not be immediately.  It
198    // calls `async-return` once the callee has produced its result, either
199    // via `task.return` (if async-lifted) or by returning (if sync-lifted).
200    //
201    // In the meantime, an async caller gets control back immediately so it can
202    // do other work, and is later notified of the subtask's progress via
203    // events.  A sync caller instead blocks until the callee has produced its
204    // result.  Since a sync caller may have passed its parameters on the stack
205    // rather than in linear memory, the host stores them in a task-local
206    // location until `async-start` is called, and similarly stores the results
207    // until the caller is resumed.
208    let start = async_start_adapter(module);
209    let return_ = async_return_adapter(module);
210    let (compiler, lower_sig, lift_sig) = compiler(module, adapter);
211    compiler.compile_host_mediated_adapter(adapter, start, return_, &lower_sig, &lift_sig);
212}
213
214/// Compiles a helper function as specified by the `Helper` configuration.
215///
216/// This function is invoked when the translation process runs out of fuel for
217/// some prior function which enqueues a helper to get translated later. This
218/// translation function will perform one type translation as specified by
219/// `Helper` which can either be in the stack or memory for each side.
220pub(super) fn compile_helper(module: &mut Module<'_>, result: FunctionId, helper: Helper) {
221    let mut nlocals = 0;
222    let src_flat;
223    let src = match helper.src.loc {
224        // If the source is on the stack then it's specified in the parameters
225        // to the function, so this creates the flattened representation and
226        // then lists those as the locals with appropriate types for the source
227        // values.
228        HelperLocation::Stack => {
229            src_flat = module
230                .types
231                .flatten_types(&helper.src.opts, usize::MAX, [helper.src.ty])
232                .unwrap()
233                .iter()
234                .enumerate()
235                .map(|(i, ty)| (i as u32, *ty))
236                .collect::<Vec<_>>();
237            nlocals += src_flat.len() as u32;
238            Source::Stack(Stack {
239                locals: &src_flat,
240                opts: &helper.src.opts,
241            })
242        }
243        // If the source is in memory then that's just propagated here as the
244        // first local is the pointer to the source.
245        HelperLocation::Memory => {
246            nlocals += 1;
247            Source::Memory(Memory {
248                opts: &helper.src.opts,
249                addr: TempLocal::new(0, helper.src.opts.data_model.unwrap_memory().ptr()),
250                offset: 0,
251            })
252        }
253        HelperLocation::StructField | HelperLocation::ArrayElement => todo!("CM+GC"),
254    };
255    let dst_flat;
256    let dst = match helper.dst.loc {
257        // This is the same as the stack-based source although `Destination` is
258        // configured slightly differently.
259        HelperLocation::Stack => {
260            dst_flat = module
261                .types
262                .flatten_types(&helper.dst.opts, usize::MAX, [helper.dst.ty])
263                .unwrap();
264            Destination::Stack(&dst_flat, &helper.dst.opts)
265        }
266        // This is the same as a memory-based source but note that the address
267        // of the destination is passed as the final parameter to the function.
268        HelperLocation::Memory => {
269            nlocals += 1;
270            Destination::Memory(Memory {
271                opts: &helper.dst.opts,
272                addr: TempLocal::new(
273                    nlocals - 1,
274                    helper.dst.opts.data_model.unwrap_memory().ptr(),
275                ),
276                offset: 0,
277            })
278        }
279        HelperLocation::StructField | HelperLocation::ArrayElement => todo!("CM+GC"),
280    };
281    let mut compiler = Compiler {
282        types: module.types,
283        module,
284        code: Vec::new(),
285        nlocals,
286        free_locals: HashMap::new(),
287        result,
288        fuel: INITIAL_FUEL,
289        // This is a helper function and only the top-level function is
290        // responsible for emitting these intrinsic calls.
291        emit_resource_call: false,
292    };
293    compiler.translate(&helper.src.ty, &src, &helper.dst.ty, &dst);
294    compiler.finish();
295}
296
297/// Possible ways that a interface value is represented in the core wasm
298/// canonical ABI.
299enum Source<'a> {
300    /// This value is stored on the "stack" in wasm locals.
301    ///
302    /// This could mean that it's inline from the parameters to the function or
303    /// that after a function call the results were stored in locals and the
304    /// locals are the inline results.
305    Stack(Stack<'a>),
306
307    /// This value is stored in linear memory described by the `Memory`
308    /// structure.
309    Memory(Memory<'a>),
310
311    /// This value is stored in a GC struct field described by the `GcStruct`
312    /// structure.
313    #[allow(dead_code, reason = "CM+GC is still WIP")]
314    Struct(GcStruct<'a>),
315
316    /// This value is stored in a GC array element described by the `GcArray`
317    /// structure.
318    #[allow(dead_code, reason = "CM+GC is still WIP")]
319    Array(GcArray<'a>),
320}
321
322/// Same as `Source` but for where values are translated into.
323enum Destination<'a> {
324    /// This value is destined for the WebAssembly stack which means that
325    /// results are simply pushed as we go along.
326    ///
327    /// The types listed are the types that are expected to be on the stack at
328    /// the end of translation.
329    Stack(&'a [ValType], &'a Options),
330
331    /// This value is to be placed in linear memory described by `Memory`.
332    Memory(Memory<'a>),
333
334    /// This value is to be placed in a GC struct field described by the
335    /// `GcStruct` structure.
336    #[allow(dead_code, reason = "CM+GC is still WIP")]
337    Struct(GcStruct<'a>),
338
339    /// This value is to be placed in a GC array element described by the
340    /// `GcArray` structure.
341    #[allow(dead_code, reason = "CM+GC is still WIP")]
342    Array(GcArray<'a>),
343}
344
345struct Stack<'a> {
346    /// The locals that comprise a particular value.
347    ///
348    /// The length of this list represents the flattened list of types that make
349    /// up the component value. Each list has the index of the local being
350    /// accessed as well as the type of the local itself.
351    locals: &'a [(u32, ValType)],
352    /// The lifting/lowering options for where this stack of values comes from
353    opts: &'a Options,
354}
355
356/// Representation of where a value is going to be stored in linear memory.
357struct Memory<'a> {
358    /// The lifting/lowering options with memory configuration
359    opts: &'a Options,
360    /// The index of the local that contains the base address of where the
361    /// storage is happening.
362    addr: TempLocal,
363    /// A "static" offset that will be baked into wasm instructions for where
364    /// memory loads/stores happen.
365    offset: u32,
366}
367
368impl<'a> Memory<'a> {
369    fn mem_opts(&self) -> &'a LinearMemoryOptions {
370        self.opts.data_model.unwrap_memory()
371    }
372}
373
374/// Representation of where a value is coming from or going to in a GC struct.
375struct GcStruct<'a> {
376    opts: &'a Options,
377    // TODO: more fields to come in the future.
378}
379
380/// Representation of where a value is coming from or going to in a GC array.
381struct GcArray<'a> {
382    opts: &'a Options,
383    // TODO: more fields to come in the future.
384}
385
386impl<'a, 'b> Compiler<'a, 'b> {
387    fn new(
388        module: &'b mut Module<'a>,
389        result: FunctionId,
390        nlocals: u32,
391        emit_resource_call: bool,
392    ) -> Self {
393        Self {
394            types: module.types,
395            module,
396            result,
397            code: Vec::new(),
398            nlocals,
399            free_locals: HashMap::new(),
400            fuel: INITIAL_FUEL,
401            emit_resource_call,
402        }
403    }
404
405    /// Compile an adapter function for a call which is mediated by the host,
406    /// i.e. any call other than a sync->sync call to a sync-typed function.
407    ///
408    /// This uses a pair of `prepare-call` and `start-call` built-in functions
409    /// to set up and start a subtask, respectively.  `prepare-call` accepts
410    /// `start` and `return_` functions which copy the parameters and results,
411    /// respectively; the host will call the former when the callee may be
412    /// entered (e.g. it has no backpressure) and the latter when the callee has
413    /// produced its result.
414    ///
415    /// For an async caller, `start-call` returns the subtask's status.  For a
416    /// sync caller, it blocks until the callee has produced its result and
417    /// returns the lowered result, if any.
418    fn compile_host_mediated_adapter(
419        mut self,
420        adapter: &AdapterData,
421        start: FunctionId,
422        return_: FunctionId,
423        lower_sig: &Signature,
424        lift_sig: &Signature,
425    ) {
426        let async_caller = adapter.lower.options.async_;
427        let start_call = self.module.import_start_call(
428            &adapter.name,
429            adapter.lift.options.callback,
430            adapter.lift.post_return,
431            if async_caller {
432                &[ValType::I32]
433            } else {
434                &lower_sig.results
435            },
436        );
437
438        self.call_prepare(adapter, start, return_, lower_sig, !async_caller);
439
440        // TODO: As an optimization, consider checking the backpressure flag on
441        // the callee instance and, if it's unset _and_ the callee uses a
442        // callback, translate the params and call the callee function directly
443        // here (and make sure `start_call` knows _not_ to call it in that case).
444
445        // We export this function so we can pass a funcref to the host.
446        //
447        // TODO: Use a declarative element segment instead of exporting this.
448        self.module.exports.push((
449            adapter.callee.as_u32(),
450            format!("[adapter-callee]{}", adapter.name),
451        ));
452
453        let mut flags = 0;
454        if async_caller {
455            flags |= START_FLAG_ASYNC_CALLER;
456        }
457        if adapter.lift.options.async_ {
458            flags |= START_FLAG_ASYNC_CALLEE;
459        }
460
461        self.instruction(RefFunc(adapter.callee.as_u32()));
462        self.instruction(I32Const(i32::try_from(lift_sig.params.len()).unwrap()));
463        self.instruction(I32Const(i32::try_from(lift_sig.results.len()).unwrap()));
464        self.instruction(I32Const(flags));
465        self.instruction(Call(start_call.as_u32()));
466
467        self.finish()
468    }
469
470    /// Invokes the `prepare_call` builtin with the provided parameters for this
471    /// adapter.
472    ///
473    /// This is part of a async lower and/or async lift adapter. This is not
474    /// used for a sync->sync function call. This is done to create the task on
475    /// the host side of the runtime and such. This will notably invoke a
476    /// Cranelift builtin which will spill all wasm-level parameters to the
477    /// stack to handle variadic signatures.
478    ///
479    /// Note that the `prepare_sync` parameter here configures the
480    /// `result_count_or_max_if_async` parameter to indicate whether this is a
481    /// sync or async prepare.
482    fn call_prepare(
483        &mut self,
484        adapter: &AdapterData,
485        start: FunctionId,
486        return_: FunctionId,
487        lower_sig: &Signature,
488        prepare_sync: bool,
489    ) {
490        // Make sure the caller can indeed leave its instance before going
491        // further.
492        let saved = self.trap_if_not_may_leave(adapter.lower.flags, Trap::CannotLeaveComponent);
493        self.free_temp_local(saved);
494
495        let prepare = self.module.import_prepare_call(
496            &adapter.name,
497            &lower_sig.params,
498            match adapter.lift.options.data_model {
499                DataModel::Gc {} => todo!("CM+GC"),
500                DataModel::LinearMemory(LinearMemoryOptions { memory, .. }) => memory.map(|m| m.0),
501            },
502        );
503
504        self.flush_code();
505        self.module.funcs[self.result]
506            .body
507            .push(Body::RefFunc(start));
508        self.module.funcs[self.result]
509            .body
510            .push(Body::RefFunc(return_));
511        self.instruction(I32Const(
512            i32::try_from(adapter.lower.instance.as_u32()).unwrap(),
513        ));
514        self.instruction(I32Const(
515            i32::try_from(adapter.lift.instance.as_u32()).unwrap(),
516        ));
517        self.instruction(I32Const(
518            i32::try_from(self.types[adapter.lift.ty].results.as_u32()).unwrap(),
519        ));
520        self.instruction(I32Const(if self.types[adapter.lift.ty].async_ {
521            1
522        } else {
523            0
524        }));
525        self.instruction(I32Const(i32::from(
526            adapter.lift.options.string_encoding as u8,
527        )));
528
529        // flag this as a preparation for either an async call or sync call,
530        // depending on `prepare_sync`
531        let result_types = &self.types[self.types[adapter.lower.ty].results].types;
532        if prepare_sync {
533            self.instruction(I32Const(
534                i32::try_from(
535                    self.types
536                        .flatten_types(
537                            &adapter.lower.options,
538                            usize::MAX,
539                            result_types.iter().copied(),
540                        )
541                        .map(|v| v.len())
542                        .unwrap_or(usize::try_from(i32::MAX).unwrap()),
543                )
544                .unwrap(),
545            ));
546        } else {
547            if result_types.len() > 0 {
548                self.instruction(I32Const(PREPARE_ASYNC_WITH_RESULT.cast_signed()));
549            } else {
550                self.instruction(I32Const(PREPARE_ASYNC_NO_RESULT.cast_signed()));
551            }
552        }
553
554        // forward all our own arguments on to the host stub
555        for index in 0..lower_sig.params.len() {
556            self.instruction(LocalGet(u32::try_from(index).unwrap()));
557        }
558        self.instruction(Call(prepare.as_u32()));
559    }
560
561    /// Compiles a function to be exported to the host which host to lift the
562    /// parameters from the caller and lower them to the callee.
563    ///
564    /// This allows the host to delay copying the parameters until the callee
565    /// signals readiness by clearing its backpressure flag.
566    fn compile_async_start_adapter(mut self, adapter: &AdapterData, sig: &Signature) {
567        // Note that unlike `compile_sync_to_sync_adapter` no exception
568        // barrier is emitted here: this function is invoked by the host, so
569        // an exception thrown by any guest code it calls (e.g. `realloc`)
570        // unwinds to the host rather than into another component, and the
571        // host already catches it at that boundary.
572        let param_locals = sig
573            .params
574            .iter()
575            .enumerate()
576            .map(|(i, ty)| (i as u32, *ty))
577            .collect::<Vec<_>>();
578
579        let saved = self.clear_may_leave(adapter.lift.flags);
580        self.translate_params(adapter, &param_locals);
581        self.restore_may_leave(adapter.lift.flags, saved);
582
583        self.finish();
584    }
585
586    /// Compiles a function to be exported by the adapter module and called by
587    /// the host to lift the results from the callee and lower them to the
588    /// caller.
589    ///
590    /// Given that async-lifted exports return their results via the
591    /// `task.return` intrinsic, the host will need to copy the results from
592    /// callee to caller when that intrinsic is called rather than when the
593    /// callee task fully completes (which may happen much later).
594    fn compile_async_return_adapter(mut self, adapter: &AdapterData, sig: &Signature) {
595        // As with `compile_async_start_adapter`, no exception barrier is
596        // emitted here: the host invokes this function and already catches
597        // exceptions unwinding out of it.
598        let param_locals = sig
599            .params
600            .iter()
601            .enumerate()
602            .map(|(i, ty)| (i as u32, *ty))
603            .collect::<Vec<_>>();
604
605        let saved = self.clear_may_leave(adapter.lower.flags);
606        // Note that we pass `param_locals` as _both_ the `param_locals` and
607        // `result_locals` parameters to `translate_results`.  That's because
608        // the _parameters_ to `task.return` are actually the _results_ that the
609        // caller is waiting for.
610        //
611        // Additionally, the host will append a return
612        // pointer to the end of that list before calling this adapter's
613        // `async-return` function if the results exceed `MAX_FLAT_RESULTS` or
614        // the import is lowered async, in which case `translate_results` will
615        // use that pointer to store the results.
616        self.translate_results(adapter, &param_locals, &param_locals);
617        self.restore_may_leave(adapter.lower.flags, saved);
618
619        self.finish()
620    }
621
622    /// Compile an adapter function supporting a sync-lowered import to a
623    /// sync-lifted export.
624    ///
625    /// Unlike calls involving async-lowered imports or async-lifted exports,
626    /// this adapter need not involve host built-ins except possibly for
627    /// resource bookkeeping.
628    fn compile_sync_to_sync_adapter(
629        mut self,
630        adapter: &AdapterData,
631        lower_sig: &Signature,
632        lift_sig: &Signature,
633    ) {
634        self.enter_exception_barrier(&lower_sig.results);
635
636        // Check the instance flags required for this trampoline.
637        //
638        // This inserts the initial check required by `canon_lower` that the
639        // caller instance can be left and additionally checks the
640        // flags on the callee if necessary whether it can be entered.
641        //
642        // The loaded `may_leave` value is saved into `saved_lower_may_leave`
643        // so that it can be restored after results are translated below
644        // without reloading the global.
645        let saved_lower_may_leave =
646            self.trap_if_not_may_leave(adapter.lower.flags, Trap::CannotLeaveComponent);
647
648        // If nothing that this adapter can reach is able to observe or mutate
649        // the thread state that `enter-sync-call`/`exit-sync-call` maintain then
650        // none of its bookkeeping is necessary.
651        //
652        // See `crates/environ/src/component/thread_transparency.rs` for details.
653        debug_assert!(
654            !(adapter.thread_transparent && self.emit_resource_call),
655            "resources are not thread transparent",
656        );
657        let needs_thread_state =
658            self.module.tunables.concurrency_support && !adapter.thread_transparent;
659
660        if needs_thread_state {
661            // Push a task onto the current task stack.
662            //
663            // Note that for sync-to-sync calls, we replace this call with
664            // inline code for lazy/deferred task creation during translation to
665            // CLIF. This avoids task creation and out-of-line calls in the
666            // adapter for most sync-to-sync calls, since most sync-to-sync
667            // calls do not do anything to force the task's creation
668            // (e.g. adjust backpressure).
669            self.instruction(I32Const(if self.types[adapter.lift.ty].async_ {
670                1
671            } else {
672                0
673            }));
674            self.instruction(I32Const(
675                i32::try_from(adapter.lift.instance.as_u32()).unwrap(),
676            ));
677            let enter_sync_call = self.module.import_enter_sync_call();
678            self.instruction(Call(enter_sync_call.as_u32()));
679        } else if self.emit_resource_call {
680            assert!(!self.types[adapter.lift.ty].async_);
681            self.instruction(I32Const(0));
682            self.instruction(I32Const(
683                i32::try_from(adapter.lift.instance.as_u32()).unwrap(),
684            ));
685            let enter_sync_call = self.module.import_enter_sync_call();
686            self.instruction(Call(enter_sync_call.as_u32()));
687        }
688
689        // Perform the translation of arguments. Note that the `may_leave` flag
690        // is cleared around this invocation for the callee as per the
691        // `canon_lift` definition in the spec. Additionally note that the
692        // precise ordering of traps here is not required since internal state
693        // is not visible to either instance and a trap will "lock down" both
694        // instances to no longer be visible. This means that we're free to
695        // reorder lifts/lowers and flags and such as is necessary and
696        // convenient here.
697        //
698        // The clear-and-restore is structured (a constant `0` store to clear,
699        // then a store of the saved original value to restore) so that if
700        // translation doesn't actually call any functions in either instance
701        // then a future dead-store elimination pass in Cranelift can remove all
702        // the flag juggling entirely (other than trapping when `!may_leave`):
703        //
704        //     may_leave = load vmctx+MAY_LEAVE_OFFSET      ;; (0)
705        //     trapz may_leave
706        //
707        //     ...
708        //
709        //     zero = iconst 0
710        //     store zero, vmctx+MAY_LEAVE_OFFSET           ;; (1)
711        //
712        //     ...
713        //
714        //     store may_leave, vmctx+MAY_LEAVE_OFFSET      ;; (2)
715        //
716        // First, the dead-store elimination pass will see that the the store at
717        // (1) is dead and remove it. Then, the idempotent-store eliminator will
718        // recognize that the store at (2) is storing the same value that the
719        // memory location already contains and it will also be removed. The
720        // more we can reuse locals to make this idempotency obvious, rather
721        // than force Cranelift's optimizer to rediscover this information, the
722        // better.
723        let saved_lift_may_leave = self.clear_may_leave(adapter.lift.flags);
724        let param_locals = lower_sig
725            .params
726            .iter()
727            .enumerate()
728            .map(|(i, ty)| (i as u32, *ty))
729            .collect::<Vec<_>>();
730        self.translate_params(adapter, &param_locals);
731        self.restore_may_leave(adapter.lift.flags, saved_lift_may_leave);
732
733        // With all the arguments on the stack the actual target function is
734        // now invoked. The core wasm results of the function are then placed
735        // into locals for result translation afterwards.
736
737        self.instruction(Call(adapter.callee.as_u32()));
738
739        let mut result_locals = Vec::with_capacity(lift_sig.results.len());
740        let mut temps = Vec::new();
741        for ty in lift_sig.results.iter().rev() {
742            let local = self.local_set_new_tmp(*ty);
743            result_locals.push((local.idx, *ty));
744            temps.push(local);
745        }
746        result_locals.reverse();
747
748        // The `exit-sync-call` intrinsic below will clobber this task's context
749        // slots, but if we've got a post-return we'll want to restore them
750        // temporarily for that. Save them if it's necessary.
751        let callee_context = if adapter.lift.post_return.is_some() {
752            self.save_context()
753        } else {
754            Vec::new()
755        };
756
757        // Handle a few things related to the concurrent task infrastructure
758        // after the callee has finished, such as:
759        //
760        // * Validate that the callee dropped all its borrows
761        // * Transition the current running task back to the caller.
762        //
763        // This is not necessary if there are no resources in this call, nor if
764        // concurrency support is disabled (no tasks). Note that this must
765        // happen before lowering below because semantically that's where the
766        // "you forgot to drop borrows" trap shows up and additionally the
767        // lowering below may call realloc which is in the context of the
768        // caller's task, not the callee.
769        //
770        // Note that for sync-to-sync calls, we will emit inline code during
771        // translation to CLIF to avoid actually calling out to a libcall when
772        // the deferred task's allocation was never forced.
773        if self.emit_resource_call || needs_thread_state {
774            let exit_sync_call = self.module.import_exit_sync_call();
775            self.instruction(Call(exit_sync_call.as_u32()));
776        }
777
778        // Like above during the translation of results the caller cannot be
779        // left (as we might invoke things like `realloc`). Again the precise
780        // order of everything doesn't matter since intermediate states cannot
781        // be witnessed, hence the setting of flags here to encapsulate both
782        // liftings and lowerings.
783        self.set_may_leave_false(adapter.lower.flags);
784        self.translate_results(adapter, &param_locals, &result_locals);
785        self.restore_may_leave(adapter.lower.flags, saved_lower_may_leave);
786
787        // And finally post-return state is handled here once all results/etc
788        // are all translated.
789        //
790        // Note that for this call the callee's previous context is shuffled
791        // in-and-then-back-out after the call. Additionally the callee cannot
792        // leave its instance during post-return, so `may_leave` is cleared.
793        if let Some(func) = adapter.lift.post_return {
794            let caller_context = self.save_context();
795            self.restore_context(callee_context);
796            let saved_lift_may_leave = self.clear_may_leave(adapter.lift.flags);
797            for (result, _) in result_locals.iter() {
798                self.instruction(LocalGet(*result));
799            }
800            self.instruction(Call(func.as_u32()));
801            self.restore_may_leave(adapter.lift.flags, saved_lift_may_leave);
802            self.restore_context(caller_context);
803        } else {
804            assert!(callee_context.is_empty());
805        }
806
807        for tmp in temps {
808            self.free_temp_local(tmp);
809        }
810
811        self.exit_exception_barrier();
812
813        self.finish()
814    }
815
816    fn translate_params(&mut self, adapter: &AdapterData, param_locals: &[(u32, ValType)]) {
817        let src_tys = self.types[adapter.lower.ty].params;
818        let src_tys = self.types[src_tys]
819            .types
820            .iter()
821            .copied()
822            .collect::<Vec<_>>();
823        let dst_tys = self.types[adapter.lift.ty].params;
824        let dst_tys = self.types[dst_tys]
825            .types
826            .iter()
827            .copied()
828            .collect::<Vec<_>>();
829        let lift_opts = &adapter.lift.options;
830        let lower_opts = &adapter.lower.options;
831
832        // TODO: handle subtyping
833        assert_eq!(src_tys.len(), dst_tys.len());
834
835        // Async lowered functions have a smaller limit on flat parameters, but
836        // their destination, a lifted function, does not have a different limit
837        // than sync functions.
838        let max_flat_params = if adapter.lower.options.async_ {
839            MAX_FLAT_ASYNC_PARAMS
840        } else {
841            MAX_FLAT_PARAMS
842        };
843        let src_flat =
844            self.types
845                .flatten_types(lower_opts, max_flat_params, src_tys.iter().copied());
846        let dst_flat =
847            self.types
848                .flatten_types(lift_opts, MAX_FLAT_PARAMS, dst_tys.iter().copied());
849
850        let src = if let Some(flat) = &src_flat {
851            Source::Stack(Stack {
852                locals: &param_locals[..flat.len()],
853                opts: lower_opts,
854            })
855        } else {
856            // If there are too many parameters then that means the parameters
857            // are actually a tuple stored in linear memory addressed by the
858            // first parameter local.
859            let lower_mem_opts = lower_opts.data_model.unwrap_memory();
860            let (addr, ty) = param_locals[0];
861            assert_eq!(ty, lower_mem_opts.ptr());
862            let abi = CanonicalAbiInfo::record(src_tys.iter().map(|t| self.types.canonical_abi(t)));
863            Source::Memory(self.memory_operand_abi(
864                lower_opts,
865                TempLocal::new(addr, ty),
866                &abi,
867                Trap::MemoryOutOfBounds,
868            ))
869        };
870
871        let dst = if let Some(flat) = &dst_flat {
872            Destination::Stack(flat, lift_opts)
873        } else {
874            // If there are too many parameters then space is allocated in the
875            // destination module for the parameters via its `realloc` function.
876            let abi = CanonicalAbiInfo::record(dst_tys.iter().map(|t| self.types.canonical_abi(t)));
877            Destination::Memory(self.malloc_abi(lift_opts, &abi, Trap::MemoryOutOfBounds))
878        };
879
880        let srcs = src
881            .record_field_srcs(self.types, src_tys.iter().copied())
882            .zip(src_tys.iter());
883        let dsts = dst
884            .record_field_dsts(self.types, dst_tys.iter().copied())
885            .zip(dst_tys.iter());
886        for ((src, src_ty), (dst, dst_ty)) in srcs.zip(dsts) {
887            self.translate(&src_ty, &src, &dst_ty, &dst);
888        }
889
890        // If the destination was linear memory instead of the stack then the
891        // actual parameter that we're passing is the address of the values
892        // stored, so ensure that's happening in the wasm body here.
893        if let Destination::Memory(mem) = dst {
894            self.instruction(LocalGet(mem.addr.idx));
895            self.free_temp_local(mem.addr);
896        }
897    }
898
899    fn translate_results(
900        &mut self,
901        adapter: &AdapterData,
902        param_locals: &[(u32, ValType)],
903        result_locals: &[(u32, ValType)],
904    ) {
905        let src_tys = self.types[adapter.lift.ty].results;
906        let src_tys = self.types[src_tys]
907            .types
908            .iter()
909            .copied()
910            .collect::<Vec<_>>();
911        let dst_tys = self.types[adapter.lower.ty].results;
912        let dst_tys = self.types[dst_tys]
913            .types
914            .iter()
915            .copied()
916            .collect::<Vec<_>>();
917        let lift_opts = &adapter.lift.options;
918        let lower_opts = &adapter.lower.options;
919
920        let src_flat = self
921            .types
922            .flatten_lifting_types(lift_opts, src_tys.iter().copied());
923        let dst_flat = self
924            .types
925            .flatten_lowering_types(lower_opts, dst_tys.iter().copied());
926
927        let src = if src_flat.is_some() {
928            Source::Stack(Stack {
929                locals: result_locals,
930                opts: lift_opts,
931            })
932        } else {
933            // The original results to read from in this case come from the
934            // return value of the function itself. The imported function will
935            // return a linear memory address at which the values can be read
936            // from.
937            let abi = CanonicalAbiInfo::record(src_tys.iter().map(|t| self.types.canonical_abi(t)));
938            assert_eq!(
939                result_locals.len(),
940                if lower_opts.async_ || lift_opts.async_ || self.types[adapter.lift.ty].async_ {
941                    2
942                } else {
943                    1
944                }
945            );
946            let (addr, ty) = result_locals[0];
947            assert_eq!(ty, lift_opts.data_model.unwrap_memory().ptr());
948            Source::Memory(self.memory_operand_abi(
949                lift_opts,
950                TempLocal::new(addr, ty),
951                &abi,
952                Trap::MemoryOutOfBounds,
953            ))
954        };
955
956        let dst = if let Some(flat) = &dst_flat {
957            Destination::Stack(flat, lower_opts)
958        } else {
959            // This is slightly different than `translate_params` where the
960            // return pointer was provided by the caller of this function
961            // meaning the last parameter local is a pointer into linear memory.
962            let abi = CanonicalAbiInfo::record(dst_tys.iter().map(|t| self.types.canonical_abi(t)));
963            let (addr, ty) = *param_locals.last().expect("no retptr");
964            assert_eq!(ty, lower_opts.data_model.unwrap_memory().ptr());
965            Destination::Memory(self.memory_operand_abi(
966                lower_opts,
967                TempLocal::new(addr, ty),
968                &abi,
969                Trap::MemoryOutOfBounds,
970            ))
971        };
972
973        let srcs = src
974            .record_field_srcs(self.types, src_tys.iter().copied())
975            .zip(src_tys.iter());
976        let dsts = dst
977            .record_field_dsts(self.types, dst_tys.iter().copied())
978            .zip(dst_tys.iter());
979        for ((src, src_ty), (dst, dst_ty)) in srcs.zip(dsts) {
980            self.translate(&src_ty, &src, &dst_ty, &dst);
981        }
982    }
983
984    fn translate(
985        &mut self,
986        src_ty: &InterfaceType,
987        src: &Source<'_>,
988        dst_ty: &InterfaceType,
989        dst: &Destination,
990    ) {
991        if let Source::Memory(mem) = src {
992            self.assert_aligned(src_ty, mem);
993        }
994        if let Destination::Memory(mem) = dst {
995            self.assert_aligned(dst_ty, mem);
996        }
997
998        // Calculate a cost heuristic for what the translation of this specific
999        // layer of the type is going to incur. The purpose of this cost is that
1000        // we'll deduct it from `self.fuel` and if no fuel is remaining then
1001        // translation is outlined into a separate function rather than being
1002        // translated into this function.
1003        //
1004        // The general goal is to avoid creating an exponentially sized function
1005        // for a linearly sized input (the type section). By outlining helper
1006        // functions there will ideally be a constant set of helper functions
1007        // per type (to accommodate in-memory or on-stack transfers as well as
1008        // src/dst options) which means that each function is at most a certain
1009        // size and we have a linear number of functions which should guarantee
1010        // an overall linear size of the output.
1011        //
1012        // To implement this the current heuristic is that each layer of
1013        // translating a type has a cost associated with it and this cost is
1014        // accounted for in `self.fuel`. Some conversions are considered free as
1015        // they generate basically as much code as the `call` to the translation
1016        // function while other are considered proportionally expensive to the
1017        // size of the type. The hope is that some upper layers are of a type's
1018        // translation are all inlined into one function but bottom layers end
1019        // up getting outlined to separate functions. Theoretically, again this
1020        // is built on hopes and dreams, the outlining can be shared amongst
1021        // tightly-intertwined type hierarchies which will reduce the size of
1022        // the output module due to the helpers being used.
1023        //
1024        // This heuristic of how to split functions has changed a few times in
1025        // the past and this isn't necessarily guaranteed to be the final
1026        // iteration.
1027        let cost = match src_ty {
1028            // These types are all quite simple to load/store and equate to
1029            // basically the same cost of the `call` instruction to call an
1030            // out-of-line translation function, so give them 0 cost.
1031            InterfaceType::Bool
1032            | InterfaceType::U8
1033            | InterfaceType::S8
1034            | InterfaceType::U16
1035            | InterfaceType::S16
1036            | InterfaceType::U32
1037            | InterfaceType::S32
1038            | InterfaceType::U64
1039            | InterfaceType::S64
1040            | InterfaceType::Float32
1041            | InterfaceType::Float64 => 0,
1042
1043            // This has a small amount of validation associated with it, so
1044            // give it a cost of 1.
1045            InterfaceType::Char => 1,
1046
1047            // This has a fair bit of code behind it depending on the
1048            // strings/encodings in play, so arbitrarily assign it this cost.
1049            InterfaceType::String => 40,
1050
1051            // Iteration of a loop is along the lines of the cost of a string
1052            // so give it the same cost
1053            InterfaceType::List(_) => 40,
1054            // Maps are similar to lists in terms of iteration cost
1055            InterfaceType::Map(_) => 40,
1056
1057            InterfaceType::Flags(i) => {
1058                let count = self.module.types[*i].names.len();
1059                match FlagsSize::from_count(count) {
1060                    FlagsSize::Size0 => 0,
1061                    FlagsSize::Size1 | FlagsSize::Size2 => 1,
1062                    FlagsSize::Size4Plus(n) => n.into(),
1063                }
1064            }
1065
1066            InterfaceType::Record(i) => self.types[*i].fields.len(),
1067            InterfaceType::Tuple(i) => self.types[*i].types.len(),
1068            InterfaceType::Variant(i) => self.types[*i].cases.len(),
1069            InterfaceType::Enum(i) => self.types[*i].names.len(),
1070
1071            // 2 cases to consider for each of these variants.
1072            InterfaceType::Option(_) | InterfaceType::Result(_) => 2,
1073
1074            // TODO(#6696) - something nonzero, is 1 right?
1075            InterfaceType::Own(_)
1076            | InterfaceType::Borrow(_)
1077            | InterfaceType::Future(_)
1078            | InterfaceType::Stream(_)
1079            | InterfaceType::ErrorContext(_) => 1,
1080            InterfaceType::FixedLengthList(i) => self.types[*i].size as usize,
1081        };
1082
1083        // If this function has the initial set of fuel then we want to be sure
1084        // to translate at least one type, even if it's a huge one,
1085        // unconditionally allow this type to get translate.d Here
1086        // `saturating_sub` will clamp at 0 if `cost` is higher than `fuel`,
1087        // which is what we want anyway where if this type is huge it just
1088        // prevents other translations in this function.
1089        let remaining_fuel = if self.fuel == INITIAL_FUEL {
1090            Some(self.fuel.saturating_sub(cost))
1091        } else {
1092            self.fuel.checked_sub(cost)
1093        };
1094
1095        match remaining_fuel {
1096            // This function has enough fuel to perform the layer of translation
1097            // necessary for this type, so the fuel is updated in-place and
1098            // translation continues. Note that the recursion here is bounded by
1099            // the static recursion limit for all interface types as imposed
1100            // during the translation phase.
1101            Some(n) => {
1102                self.fuel = n;
1103                match src_ty {
1104                    InterfaceType::Bool => self.translate_bool(src, dst_ty, dst),
1105                    InterfaceType::U8 => self.translate_u8(src, dst_ty, dst),
1106                    InterfaceType::S8 => self.translate_s8(src, dst_ty, dst),
1107                    InterfaceType::U16 => self.translate_u16(src, dst_ty, dst),
1108                    InterfaceType::S16 => self.translate_s16(src, dst_ty, dst),
1109                    InterfaceType::U32 => self.translate_u32(src, dst_ty, dst),
1110                    InterfaceType::S32 => self.translate_s32(src, dst_ty, dst),
1111                    InterfaceType::U64 => self.translate_u64(src, dst_ty, dst),
1112                    InterfaceType::S64 => self.translate_s64(src, dst_ty, dst),
1113                    InterfaceType::Float32 => self.translate_f32(src, dst_ty, dst),
1114                    InterfaceType::Float64 => self.translate_f64(src, dst_ty, dst),
1115                    InterfaceType::Char => self.translate_char(src, dst_ty, dst),
1116                    InterfaceType::String => self.translate_string(src, dst_ty, dst),
1117                    InterfaceType::List(t) => self.translate_list(*t, src, dst_ty, dst),
1118                    InterfaceType::Map(t) => self.translate_map(*t, src, dst_ty, dst),
1119                    InterfaceType::Record(t) => self.translate_record(*t, src, dst_ty, dst),
1120                    InterfaceType::Flags(f) => self.translate_flags(*f, src, dst_ty, dst),
1121                    InterfaceType::Tuple(t) => self.translate_tuple(*t, src, dst_ty, dst),
1122                    InterfaceType::Variant(v) => self.translate_variant(*v, src, dst_ty, dst),
1123                    InterfaceType::Enum(t) => self.translate_enum(*t, src, dst_ty, dst),
1124                    InterfaceType::Option(t) => self.translate_option(*t, src, dst_ty, dst),
1125                    InterfaceType::Result(t) => self.translate_result(*t, src, dst_ty, dst),
1126                    InterfaceType::Own(t) => self.translate_own(*t, src, dst_ty, dst),
1127                    InterfaceType::Borrow(t) => self.translate_borrow(*t, src, dst_ty, dst),
1128                    InterfaceType::Future(t) => self.translate_future(*t, src, dst_ty, dst),
1129                    InterfaceType::Stream(t) => self.translate_stream(*t, src, dst_ty, dst),
1130                    InterfaceType::ErrorContext(t) => {
1131                        self.translate_error_context(*t, src, dst_ty, dst)
1132                    }
1133                    InterfaceType::FixedLengthList(t) => {
1134                        self.translate_fixed_length_list(*t, src, dst_ty, dst);
1135                    }
1136                }
1137            }
1138
1139            // This function does not have enough fuel left to perform this
1140            // layer of translation so the translation is deferred to a helper
1141            // function. The actual translation here is then done by marshalling
1142            // the src/dst into the function we're calling and then processing
1143            // the results.
1144            None => {
1145                let src_loc = match src {
1146                    // If the source is on the stack then `stack_get` is used to
1147                    // convert everything to the appropriate flat representation
1148                    // for the source type.
1149                    Source::Stack(stack) => {
1150                        for (i, ty) in stack
1151                            .opts
1152                            .flat_types(src_ty, self.types)
1153                            .unwrap()
1154                            .iter()
1155                            .enumerate()
1156                        {
1157                            let stack = stack.slice(i..i + 1);
1158                            self.stack_get(&stack, (*ty).into());
1159                        }
1160                        HelperLocation::Stack
1161                    }
1162                    // If the source is in memory then the pointer is passed
1163                    // through, but note that the offset must be factored in
1164                    // here since the translation function will start from
1165                    // offset 0.
1166                    Source::Memory(mem) => {
1167                        self.push_mem_addr(mem);
1168                        HelperLocation::Memory
1169                    }
1170                    Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1171                };
1172                let dst_loc = match dst {
1173                    Destination::Stack(..) => HelperLocation::Stack,
1174                    Destination::Memory(mem) => {
1175                        self.push_mem_addr(mem);
1176                        HelperLocation::Memory
1177                    }
1178                    Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1179                };
1180                // Generate a `FunctionId` corresponding to the `Helper`
1181                // configuration that is necessary here. This will ideally be a
1182                // "cache hit" and use a preexisting helper which represents
1183                // outlining what would otherwise be duplicate code within a
1184                // function to one function.
1185                let helper = self.module.translate_helper(Helper {
1186                    src: HelperType {
1187                        ty: *src_ty,
1188                        opts: *src.opts(),
1189                        loc: src_loc,
1190                    },
1191                    dst: HelperType {
1192                        ty: *dst_ty,
1193                        opts: *dst.opts(),
1194                        loc: dst_loc,
1195                    },
1196                });
1197                // Emit a `call` instruction which will get "relocated" to a
1198                // function index once translation has completely finished.
1199                self.flush_code();
1200                self.module.funcs[self.result].body.push(Body::Call(helper));
1201
1202                // If the destination of the translation was on the stack then
1203                // the types on the stack need to be optionally converted to
1204                // different types (e.g. if the result here is part of a variant
1205                // somewhere else).
1206                //
1207                // This translation happens inline here by popping the results
1208                // into new locals and then using those locals to do a
1209                // `stack_set`.
1210                if let Destination::Stack(tys, opts) = dst {
1211                    let flat = self
1212                        .types
1213                        .flatten_types(opts, usize::MAX, [*dst_ty])
1214                        .unwrap();
1215                    assert_eq!(flat.len(), tys.len());
1216                    let locals = flat
1217                        .iter()
1218                        .rev()
1219                        .map(|ty| self.local_set_new_tmp(*ty))
1220                        .collect::<Vec<_>>();
1221                    for (ty, local) in tys.iter().zip(locals.into_iter().rev()) {
1222                        self.instruction(LocalGet(local.idx));
1223                        self.stack_set(std::slice::from_ref(ty), local.ty);
1224                        self.free_temp_local(local);
1225                    }
1226                }
1227            }
1228        }
1229    }
1230
1231    fn push_mem_addr(&mut self, mem: &Memory<'_>) {
1232        self.instruction(LocalGet(mem.addr.idx));
1233        if mem.offset != 0 {
1234            self.ptr_uconst(mem.mem_opts(), mem.offset);
1235            self.ptr_add(mem.mem_opts());
1236        }
1237    }
1238
1239    fn translate_bool(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1240        // TODO: subtyping
1241        assert!(matches!(dst_ty, InterfaceType::Bool));
1242        self.push_dst_addr(dst);
1243
1244        // Booleans are canonicalized to 0 or 1 as they pass through the
1245        // component boundary, so use a `select` instruction to do so.
1246        self.instruction(I32Const(1));
1247        self.instruction(I32Const(0));
1248        match src {
1249            Source::Memory(mem) => self.i32_load8u(mem),
1250            Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1251            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1252        }
1253        self.instruction(Select);
1254
1255        match dst {
1256            Destination::Memory(mem) => self.i32_store8(mem),
1257            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1258            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1259        }
1260    }
1261
1262    fn translate_u8(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1263        // TODO: subtyping
1264        assert!(matches!(dst_ty, InterfaceType::U8));
1265        self.convert_u8_mask(src, dst, 0xff);
1266    }
1267
1268    fn convert_u8_mask(&mut self, src: &Source<'_>, dst: &Destination<'_>, mask: u8) {
1269        self.push_dst_addr(dst);
1270        let mut needs_mask = true;
1271        match src {
1272            Source::Memory(mem) => {
1273                self.i32_load8u(mem);
1274                needs_mask = mask != 0xff;
1275            }
1276            Source::Stack(stack) => {
1277                self.stack_get(stack, ValType::I32);
1278            }
1279            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1280        }
1281        if needs_mask {
1282            self.instruction(I32Const(i32::from(mask)));
1283            self.instruction(I32And);
1284        }
1285        match dst {
1286            Destination::Memory(mem) => self.i32_store8(mem),
1287            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1288            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1289        }
1290    }
1291
1292    fn translate_s8(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1293        // TODO: subtyping
1294        assert!(matches!(dst_ty, InterfaceType::S8));
1295        self.push_dst_addr(dst);
1296        match src {
1297            Source::Memory(mem) => self.i32_load8s(mem),
1298            Source::Stack(stack) => {
1299                self.stack_get(stack, ValType::I32);
1300                self.instruction(I32Extend8S);
1301            }
1302            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1303        }
1304        match dst {
1305            Destination::Memory(mem) => self.i32_store8(mem),
1306            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1307            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1308        }
1309    }
1310
1311    fn translate_u16(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1312        // TODO: subtyping
1313        assert!(matches!(dst_ty, InterfaceType::U16));
1314        self.convert_u16_mask(src, dst, 0xffff);
1315    }
1316
1317    fn convert_u16_mask(&mut self, src: &Source<'_>, dst: &Destination<'_>, mask: u16) {
1318        self.push_dst_addr(dst);
1319        let mut needs_mask = true;
1320        match src {
1321            Source::Memory(mem) => {
1322                self.i32_load16u(mem);
1323                needs_mask = mask != 0xffff;
1324            }
1325            Source::Stack(stack) => {
1326                self.stack_get(stack, ValType::I32);
1327            }
1328            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1329        }
1330        if needs_mask {
1331            self.instruction(I32Const(i32::from(mask)));
1332            self.instruction(I32And);
1333        }
1334        match dst {
1335            Destination::Memory(mem) => self.i32_store16(mem),
1336            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1337            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1338        }
1339    }
1340
1341    fn translate_s16(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1342        // TODO: subtyping
1343        assert!(matches!(dst_ty, InterfaceType::S16));
1344        self.push_dst_addr(dst);
1345        match src {
1346            Source::Memory(mem) => self.i32_load16s(mem),
1347            Source::Stack(stack) => {
1348                self.stack_get(stack, ValType::I32);
1349                self.instruction(I32Extend16S);
1350            }
1351            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1352        }
1353        match dst {
1354            Destination::Memory(mem) => self.i32_store16(mem),
1355            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1356            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1357        }
1358    }
1359
1360    fn translate_u32(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1361        // TODO: subtyping
1362        assert!(matches!(dst_ty, InterfaceType::U32));
1363        self.convert_u32_mask(src, dst, 0xffffffff)
1364    }
1365
1366    fn convert_u32_mask(&mut self, src: &Source<'_>, dst: &Destination<'_>, mask: u32) {
1367        self.push_dst_addr(dst);
1368        match src {
1369            Source::Memory(mem) => self.i32_load(mem),
1370            Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1371            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1372        }
1373        if mask != 0xffffffff {
1374            self.instruction(I32Const(mask as i32));
1375            self.instruction(I32And);
1376        }
1377        match dst {
1378            Destination::Memory(mem) => self.i32_store(mem),
1379            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1380            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1381        }
1382    }
1383
1384    fn translate_s32(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1385        // TODO: subtyping
1386        assert!(matches!(dst_ty, InterfaceType::S32));
1387        self.push_dst_addr(dst);
1388        match src {
1389            Source::Memory(mem) => self.i32_load(mem),
1390            Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1391            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1392        }
1393        match dst {
1394            Destination::Memory(mem) => self.i32_store(mem),
1395            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1396            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1397        }
1398    }
1399
1400    fn translate_u64(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1401        // TODO: subtyping
1402        assert!(matches!(dst_ty, InterfaceType::U64));
1403        self.push_dst_addr(dst);
1404        match src {
1405            Source::Memory(mem) => self.i64_load(mem),
1406            Source::Stack(stack) => self.stack_get(stack, ValType::I64),
1407            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1408        }
1409        match dst {
1410            Destination::Memory(mem) => self.i64_store(mem),
1411            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I64),
1412            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1413        }
1414    }
1415
1416    fn translate_s64(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1417        // TODO: subtyping
1418        assert!(matches!(dst_ty, InterfaceType::S64));
1419        self.push_dst_addr(dst);
1420        match src {
1421            Source::Memory(mem) => self.i64_load(mem),
1422            Source::Stack(stack) => self.stack_get(stack, ValType::I64),
1423            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1424        }
1425        match dst {
1426            Destination::Memory(mem) => self.i64_store(mem),
1427            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I64),
1428            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1429        }
1430    }
1431
1432    fn translate_f32(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1433        // TODO: subtyping
1434        assert!(matches!(dst_ty, InterfaceType::Float32));
1435        self.push_dst_addr(dst);
1436        match src {
1437            Source::Memory(mem) => self.f32_load(mem),
1438            Source::Stack(stack) => self.stack_get(stack, ValType::F32),
1439            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1440        }
1441        match dst {
1442            Destination::Memory(mem) => self.f32_store(mem),
1443            Destination::Stack(stack, _) => self.stack_set(stack, ValType::F32),
1444            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1445        }
1446    }
1447
1448    fn translate_f64(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1449        // TODO: subtyping
1450        assert!(matches!(dst_ty, InterfaceType::Float64));
1451        self.push_dst_addr(dst);
1452        match src {
1453            Source::Memory(mem) => self.f64_load(mem),
1454            Source::Stack(stack) => self.stack_get(stack, ValType::F64),
1455            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1456        }
1457        match dst {
1458            Destination::Memory(mem) => self.f64_store(mem),
1459            Destination::Stack(stack, _) => self.stack_set(stack, ValType::F64),
1460            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1461        }
1462    }
1463
1464    fn translate_char(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1465        assert!(matches!(dst_ty, InterfaceType::Char));
1466        match src {
1467            Source::Memory(mem) => self.i32_load(mem),
1468            Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1469            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1470        }
1471        let local = self.local_set_new_tmp(ValType::I32);
1472
1473        // This sequence is copied from the output of LLVM for:
1474        //
1475        //      pub extern "C" fn foo(x: u32) -> char {
1476        //          char::try_from(x)
1477        //              .unwrap_or_else(|_| std::arch::wasm32::unreachable())
1478        //      }
1479        //
1480        // Apparently this does what's required by the canonical ABI:
1481        //
1482        //    def i32_to_char(opts, i):
1483        //      trap_if(i >= 0x110000)
1484        //      trap_if(0xD800 <= i <= 0xDFFF)
1485        //      return chr(i)
1486        //
1487        // ... but I don't know how it works other than "well I trust LLVM"
1488        self.instruction(Block(BlockType::Empty));
1489        self.instruction(Block(BlockType::Empty));
1490        self.instruction(LocalGet(local.idx));
1491        self.instruction(I32Const(0xd800));
1492        self.instruction(I32Xor);
1493        self.instruction(I32Const(-0x110000));
1494        self.instruction(I32Add);
1495        self.instruction(I32Const(-0x10f800));
1496        self.instruction(I32LtU);
1497        self.instruction(BrIf(0));
1498        self.instruction(LocalGet(local.idx));
1499        self.instruction(I32Const(0x110000));
1500        self.instruction(I32Ne);
1501        self.instruction(BrIf(1));
1502        self.instruction(End);
1503        self.trap(Trap::InvalidChar);
1504        self.instruction(End);
1505
1506        self.push_dst_addr(dst);
1507        self.instruction(LocalGet(local.idx));
1508        match dst {
1509            Destination::Memory(mem) => {
1510                self.i32_store(mem);
1511            }
1512            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1513            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1514        }
1515
1516        self.free_temp_local(local);
1517    }
1518
1519    fn translate_string(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1520        assert!(matches!(dst_ty, InterfaceType::String));
1521        let src_opts = src.opts();
1522        let dst_opts = dst.opts();
1523
1524        let src_mem_opts = match &src_opts.data_model {
1525            DataModel::Gc {} => todo!("CM+GC"),
1526            DataModel::LinearMemory(opts) => opts,
1527        };
1528        let dst_mem_opts = match &dst_opts.data_model {
1529            DataModel::Gc {} => todo!("CM+GC"),
1530            DataModel::LinearMemory(opts) => opts,
1531        };
1532
1533        // Load the pointer/length of this string into temporary locals. These
1534        // will be referenced a good deal so this just makes it easier to deal
1535        // with them consistently below rather than trying to reload from memory
1536        // for example.
1537        match src {
1538            Source::Stack(s) => {
1539                assert_eq!(s.locals.len(), 2);
1540                self.stack_get(&s.slice(0..1), src_mem_opts.ptr());
1541                self.stack_get(&s.slice(1..2), src_mem_opts.ptr());
1542            }
1543            Source::Memory(mem) => {
1544                self.ptr_load(mem);
1545                self.ptr_load(&mem.bump(src_mem_opts.ptr_size().into()));
1546            }
1547            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1548        }
1549        let src_len = self.local_set_new_tmp(src_mem_opts.ptr());
1550        let src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
1551        let src_str = WasmString {
1552            ptr: src_ptr,
1553            len: src_len,
1554            opts: src_opts,
1555        };
1556
1557        let dst_str = match src_opts.string_encoding {
1558            StringEncoding::Utf8 => {
1559                self.validate_guest_pointer(
1560                    src_opts,
1561                    &src_str.ptr,
1562                    &AllocSize::Local(src_str.len.idx),
1563                    1,
1564                    Trap::StringOutOfBounds,
1565                );
1566                match dst_opts.string_encoding {
1567                    StringEncoding::Utf8 => {
1568                        self.string_copy(&src_str, FE::Utf8, dst_opts, FE::Utf8)
1569                    }
1570                    StringEncoding::Utf16 => self.string_utf8_to_utf16(&src_str, dst_opts),
1571                    StringEncoding::CompactUtf16 => {
1572                        self.string_to_compact(&src_str, FE::Utf8, dst_opts)
1573                    }
1574                }
1575            }
1576
1577            StringEncoding::Utf16 => {
1578                self.validate_guest_pointer(
1579                    src_opts,
1580                    &src_str.ptr,
1581                    &AllocSize::DoubleLocal(src_str.len.idx),
1582                    2,
1583                    Trap::StringOutOfBounds,
1584                );
1585                match dst_opts.string_encoding {
1586                    StringEncoding::Utf8 => {
1587                        self.string_deflate_to_utf8(&src_str, FE::Utf16, dst_opts)
1588                    }
1589                    StringEncoding::Utf16 => {
1590                        self.string_copy(&src_str, FE::Utf16, dst_opts, FE::Utf16)
1591                    }
1592                    StringEncoding::CompactUtf16 => {
1593                        self.string_to_compact(&src_str, FE::Utf16, dst_opts)
1594                    }
1595                }
1596            }
1597
1598            StringEncoding::CompactUtf16 => {
1599                // Test the tag big to see if this is a utf16 or a latin1 string
1600                // at runtime...
1601                self.instruction(LocalGet(src_str.len.idx));
1602                self.ptr_uconst(src_mem_opts, UTF16_TAG);
1603                self.ptr_and(src_mem_opts);
1604                self.ptr_if(src_mem_opts, BlockType::Empty);
1605
1606                // In the utf16 block unset the upper bit from the length local
1607                // so further calculations have the right value. Afterwards the
1608                // string transcode proceeds assuming utf16.
1609                self.instruction(LocalGet(src_str.len.idx));
1610                self.ptr_uconst(src_mem_opts, UTF16_TAG);
1611                self.ptr_xor(src_mem_opts);
1612                self.instruction(LocalSet(src_str.len.idx));
1613
1614                // Now that we dynamically know this is utf16 perform a
1615                // validation of the guest's pointer to ensure it's aligned and
1616                // in-bounds.
1617                self.validate_guest_pointer(
1618                    src_opts,
1619                    &src_str.ptr,
1620                    &AllocSize::DoubleLocal(src_str.len.idx),
1621                    2,
1622                    Trap::StringOutOfBounds,
1623                );
1624
1625                let s1 = match dst_opts.string_encoding {
1626                    StringEncoding::Utf8 => {
1627                        self.string_deflate_to_utf8(&src_str, FE::Utf16, dst_opts)
1628                    }
1629                    StringEncoding::Utf16 => {
1630                        self.string_copy(&src_str, FE::Utf16, dst_opts, FE::Utf16)
1631                    }
1632                    StringEncoding::CompactUtf16 => {
1633                        self.string_compact_utf16_to_compact(&src_str, dst_opts)
1634                    }
1635                };
1636
1637                self.instruction(Else);
1638
1639                // Now that we dynamically know this is latin1 perform the
1640                // same validation above, but with a different byte length.
1641                self.validate_guest_pointer(
1642                    src_opts,
1643                    &src_str.ptr,
1644                    &AllocSize::Local(src_str.len.idx),
1645                    2,
1646                    Trap::StringOutOfBounds,
1647                );
1648
1649                // In the latin1 block the `src_len` local is already the number
1650                // of code units, so the string transcoding is all that needs to
1651                // happen.
1652                let s2 = match dst_opts.string_encoding {
1653                    StringEncoding::Utf16 => {
1654                        self.string_copy(&src_str, FE::Latin1, dst_opts, FE::Utf16)
1655                    }
1656                    StringEncoding::Utf8 => {
1657                        self.string_deflate_to_utf8(&src_str, FE::Latin1, dst_opts)
1658                    }
1659                    StringEncoding::CompactUtf16 => {
1660                        self.string_copy(&src_str, FE::Latin1, dst_opts, FE::Latin1)
1661                    }
1662                };
1663                // Set our `s2` generated locals to the `s2` generated locals
1664                // as the resulting pointer of this transcode.
1665                self.instruction(LocalGet(s2.ptr.idx));
1666                self.instruction(LocalSet(s1.ptr.idx));
1667                self.instruction(LocalGet(s2.len.idx));
1668                self.instruction(LocalSet(s1.len.idx));
1669                self.instruction(End);
1670                self.free_temp_local(s2.ptr);
1671                self.free_temp_local(s2.len);
1672                s1
1673            }
1674        };
1675
1676        // Store the ptr/length in the desired destination
1677        match dst {
1678            Destination::Stack(s, _) => {
1679                self.instruction(LocalGet(dst_str.ptr.idx));
1680                self.stack_set(&s[..1], dst_mem_opts.ptr());
1681                self.instruction(LocalGet(dst_str.len.idx));
1682                self.stack_set(&s[1..], dst_mem_opts.ptr());
1683            }
1684            Destination::Memory(mem) => {
1685                self.instruction(LocalGet(mem.addr.idx));
1686                self.instruction(LocalGet(dst_str.ptr.idx));
1687                self.ptr_store(mem);
1688                self.instruction(LocalGet(mem.addr.idx));
1689                self.instruction(LocalGet(dst_str.len.idx));
1690                self.ptr_store(&mem.bump(dst_mem_opts.ptr_size().into()));
1691            }
1692            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1693        }
1694
1695        self.free_temp_local(src_str.ptr);
1696        self.free_temp_local(src_str.len);
1697        self.free_temp_local(dst_str.ptr);
1698        self.free_temp_local(dst_str.len);
1699    }
1700
1701    // Corresponding function for `store_string_copy` in the spec.
1702    //
1703    // This performs a transcoding of the string with a one-pass copy from
1704    // the `src` encoding to the `dst` encoding. This is only possible for
1705    // fixed encodings where the first allocation is guaranteed to be an
1706    // appropriate fit so it's not suitable for all encodings.
1707    //
1708    // Imported host transcoding functions here take the src/dst pointers as
1709    // well as the number of code units in the source (which always matches
1710    // the number of code units in the destination). There is no return
1711    // value from the transcode function since the encoding should always
1712    // work on the first pass.
1713    fn string_copy<'c>(
1714        &mut self,
1715        src: &WasmString<'_>,
1716        src_enc: FE,
1717        dst_opts: &'c Options,
1718        dst_enc: FE,
1719    ) -> WasmString<'c> {
1720        assert!(dst_enc.width() >= src_enc.width());
1721
1722        // Validate the string's length is in-bounds. Note that `dst_enc` is
1723        // specifically used here since it's the larger of the two encodings.
1724        // The code-unit size of the src/dst is going to be the same so this is
1725        // the encoding to validate.
1726        self.validate_string_length(src, dst_enc);
1727
1728        let src_mem_opts = {
1729            match &src.opts.data_model {
1730                DataModel::Gc {} => todo!("CM+GC"),
1731                DataModel::LinearMemory(opts) => opts,
1732            }
1733        };
1734        let dst_mem_opts = {
1735            match &dst_opts.data_model {
1736                DataModel::Gc {} => todo!("CM+GC"),
1737                DataModel::LinearMemory(opts) => opts,
1738            }
1739        };
1740
1741        // Convert the source code units length to the destination byte
1742        // length type.
1743        self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
1744        let dst_len = self.local_tee_new_tmp(dst_mem_opts.ptr());
1745        if dst_enc.width() > 1 {
1746            assert_eq!(dst_enc.width(), 2);
1747            self.ptr_uconst(dst_mem_opts, 1);
1748            self.ptr_shl(dst_mem_opts);
1749        }
1750        let dst_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
1751
1752        // Allocate space in the destination using the calculated byte
1753        // length.
1754        let dst = {
1755            let dst_mem = self.malloc(
1756                dst_opts,
1757                AllocSize::Local(dst_byte_len.idx),
1758                dst_enc.align().into(),
1759                Trap::StringOutOfBounds,
1760            );
1761            WasmString {
1762                ptr: dst_mem.addr,
1763                len: dst_len,
1764                opts: dst_opts,
1765            }
1766        };
1767
1768        // If the validations pass then the host `transcode` intrinsic
1769        // is invoked. This will either raise a trap or otherwise succeed
1770        // in which case we're done.
1771        let op = if src_enc == dst_enc {
1772            Transcode::Copy(src_enc)
1773        } else {
1774            assert_eq!(src_enc, FE::Latin1);
1775            assert_eq!(dst_enc, FE::Utf16);
1776            Transcode::Latin1ToUtf16
1777        };
1778        let transcode = self.transcoder(src, &dst, op);
1779        self.instruction(LocalGet(src.ptr.idx));
1780        self.instruction(LocalGet(src.len.idx));
1781        self.instruction(LocalGet(dst.ptr.idx));
1782        self.instruction(Call(transcode.as_u32()));
1783
1784        self.free_temp_local(dst_byte_len);
1785
1786        dst
1787    }
1788
1789    // Corresponding function for `store_string_to_utf8` in the spec.
1790    //
1791    // This translation works by possibly performing a number of
1792    // reallocations. First a buffer of size input-code-units is used to try
1793    // to get the transcoding correct on the first try. If that fails the
1794    // maximum worst-case size is used and then that is resized down if it's
1795    // too large.
1796    //
1797    // The host transcoding function imported here will receive src ptr/len
1798    // and dst ptr/len and return how many code units were consumed on both
1799    // sides. The amount of code units consumed in the source dictates which
1800    // branches are taken in this conversion.
1801    fn string_deflate_to_utf8<'c>(
1802        &mut self,
1803        src: &WasmString<'_>,
1804        src_enc: FE,
1805        dst_opts: &'c Options,
1806    ) -> WasmString<'c> {
1807        let src_mem_opts = match &src.opts.data_model {
1808            DataModel::Gc {} => todo!("CM+GC"),
1809            DataModel::LinearMemory(opts) => opts,
1810        };
1811        let dst_mem_opts = match &dst_opts.data_model {
1812            DataModel::Gc {} => todo!("CM+GC"),
1813            DataModel::LinearMemory(opts) => opts,
1814        };
1815
1816        self.validate_string_length(src, src_enc);
1817
1818        // Optimistically assume that the code unit length of the source is
1819        // all that's needed in the destination. Perform that allocation
1820        // here and proceed to transcoding below.
1821        self.convert_src_len_to_dst(
1822            src.len.idx,
1823            src.opts.data_model.unwrap_memory().ptr(),
1824            dst_opts.data_model.unwrap_memory().ptr(),
1825        );
1826        let dst_len = self.local_tee_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
1827        let dst_byte_len = self.local_set_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
1828
1829        let dst = {
1830            let dst_mem = self.malloc(
1831                dst_opts,
1832                AllocSize::Local(dst_byte_len.idx),
1833                1,
1834                Trap::StringOutOfBounds,
1835            );
1836            WasmString {
1837                ptr: dst_mem.addr,
1838                len: dst_len,
1839                opts: dst_opts,
1840            }
1841        };
1842
1843        // Perform the initial transcode
1844        let op = match src_enc {
1845            FE::Latin1 => Transcode::Latin1ToUtf8,
1846            FE::Utf16 => Transcode::Utf16ToUtf8,
1847            FE::Utf8 => unreachable!(),
1848        };
1849        let transcode = self.transcoder(src, &dst, op);
1850        self.instruction(LocalGet(src.ptr.idx));
1851        self.instruction(LocalGet(src.len.idx));
1852        self.instruction(LocalGet(dst.ptr.idx));
1853        self.instruction(LocalGet(dst_byte_len.idx));
1854        self.instruction(I32Const(1)); // first_pass = true
1855        self.instruction(Call(transcode.as_u32()));
1856        self.instruction(LocalSet(dst.len.idx));
1857        let src_len_tmp = self.local_set_new_tmp(src.opts.data_model.unwrap_memory().ptr());
1858
1859        // Test if the source was entirely transcoded by comparing
1860        // `src_len_tmp`, the number of code units transcoded from the
1861        // source, with `src_len`, the original number of code units.
1862        self.instruction(LocalGet(src_len_tmp.idx));
1863        self.instruction(LocalGet(src.len.idx));
1864        self.ptr_ne(src_mem_opts);
1865        self.instruction(If(BlockType::Empty));
1866
1867        // Check that the worst-case byte size fits within the maximum size of
1868        // strings.
1869        let factor = match src_enc {
1870            FE::Latin1 => 2,
1871            FE::Utf16 => 3,
1872            _ => unreachable!(),
1873        };
1874        self.validate_string_length_u8(src, factor);
1875        self.convert_src_len_to_dst(
1876            src.len.idx,
1877            src.opts.data_model.unwrap_memory().ptr(),
1878            dst_opts.data_model.unwrap_memory().ptr(),
1879        );
1880        self.ptr_uconst(dst_mem_opts, factor.into());
1881        self.ptr_mul(dst_mem_opts);
1882        let new_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
1883
1884        // Do a worst-case reallocation is performed to grow `dst_mem`.
1885        // Afterwards update our `dst_byte_len` local to reflect the new byte
1886        // length.
1887        self.realloc(
1888            dst_opts,
1889            &dst.ptr,
1890            AllocSize::Local(dst_byte_len.idx),
1891            AllocSize::Local(new_byte_len.idx),
1892            1,
1893            Trap::StringOutOfBounds,
1894        );
1895        self.instruction(LocalGet(new_byte_len.idx));
1896        self.instruction(LocalSet(dst_byte_len.idx));
1897        self.free_temp_local(new_byte_len);
1898
1899        // Perform another round of transcoding that should be guaranteed
1900        // to succeed. Note that all the parameters here are offset by the
1901        // results of the first transcoding to only perform the remaining
1902        // transcode on the final units.
1903        self.instruction(LocalGet(src.ptr.idx));
1904        self.instruction(LocalGet(src_len_tmp.idx));
1905        if let FE::Utf16 = src_enc {
1906            self.ptr_uconst(src_mem_opts, 1);
1907            self.ptr_shl(src_mem_opts);
1908        }
1909        self.ptr_add(src_mem_opts);
1910        self.instruction(LocalGet(src.len.idx));
1911        self.instruction(LocalGet(src_len_tmp.idx));
1912        self.ptr_sub(src_mem_opts);
1913        self.instruction(LocalGet(dst.ptr.idx));
1914        self.instruction(LocalGet(dst.len.idx));
1915        self.ptr_add(dst_mem_opts);
1916        self.instruction(LocalGet(dst_byte_len.idx));
1917        self.instruction(LocalGet(dst.len.idx));
1918        self.ptr_sub(dst_mem_opts);
1919        self.instruction(I32Const(0)); // first_pass = false
1920        self.instruction(Call(transcode.as_u32()));
1921
1922        // Add the second result, the amount of destination units encoded,
1923        // to `dst_len` so it's an accurate reflection of the final size of
1924        // the destination buffer.
1925        self.instruction(LocalGet(dst.len.idx));
1926        self.ptr_add(dst_mem_opts);
1927        self.instruction(LocalSet(dst.len.idx));
1928
1929        // In debug mode verify the first result consumed the entire string,
1930        // otherwise simply discard it.
1931        if self.module.tunables.debug_adapter_modules {
1932            self.instruction(LocalGet(src.len.idx));
1933            self.instruction(LocalGet(src_len_tmp.idx));
1934            self.ptr_sub(src_mem_opts);
1935            self.ptr_ne(src_mem_opts);
1936            self.instruction(If(BlockType::Empty));
1937            self.trap(Trap::DebugAssertStringEncodingFinished);
1938            self.instruction(End);
1939        } else {
1940            self.instruction(Drop);
1941        }
1942
1943        // Perform a downsizing if the worst-case size was too large
1944        self.instruction(LocalGet(dst.len.idx));
1945        self.instruction(LocalGet(dst_byte_len.idx));
1946        self.ptr_ne(dst_mem_opts);
1947        self.instruction(If(BlockType::Empty));
1948        self.realloc(
1949            dst_opts,
1950            &dst.ptr,
1951            AllocSize::Local(dst_byte_len.idx),
1952            AllocSize::Local(dst.len.idx),
1953            1,
1954            Trap::StringOutOfBounds,
1955        );
1956        self.instruction(End);
1957
1958        // If the first transcode was enough then assert that the returned
1959        // amount of destination items written equals the byte size.
1960        if self.module.tunables.debug_adapter_modules {
1961            self.instruction(Else);
1962
1963            self.instruction(LocalGet(dst.len.idx));
1964            self.instruction(LocalGet(dst_byte_len.idx));
1965            self.ptr_ne(dst_mem_opts);
1966            self.instruction(If(BlockType::Empty));
1967            self.trap(Trap::DebugAssertStringEncodingFinished);
1968            self.instruction(End);
1969        }
1970
1971        self.instruction(End); // end of "first transcode not enough"
1972
1973        self.free_temp_local(src_len_tmp);
1974        self.free_temp_local(dst_byte_len);
1975
1976        dst
1977    }
1978
1979    // Corresponds to the `store_utf8_to_utf16` function in the spec.
1980    //
1981    // When converting utf-8 to utf-16 a pessimistic allocation is
1982    // done which is twice the byte length of the utf-8 string.
1983    // The host then transcodes and returns how many code units were
1984    // actually used during the transcoding and if it's beneath the
1985    // pessimistic maximum then the buffer is reallocated down to
1986    // a smaller amount.
1987    //
1988    // The host-imported transcoding function takes the src/dst pointer as
1989    // well as the code unit size of both the source and destination. The
1990    // destination should always be big enough to hold the result of the
1991    // transcode and so the result of the host function is how many code
1992    // units were written to the destination.
1993    fn string_utf8_to_utf16<'c>(
1994        &mut self,
1995        src: &WasmString<'_>,
1996        dst_opts: &'c Options,
1997    ) -> WasmString<'c> {
1998        let src_mem_opts = match &src.opts.data_model {
1999            DataModel::Gc {} => todo!("CM+GC"),
2000            DataModel::LinearMemory(opts) => opts,
2001        };
2002        let dst_mem_opts = match &dst_opts.data_model {
2003            DataModel::Gc {} => todo!("CM+GC"),
2004            DataModel::LinearMemory(opts) => opts,
2005        };
2006
2007        self.validate_string_length(src, FE::Utf16);
2008        self.convert_src_len_to_dst(
2009            src.len.idx,
2010            src_mem_opts.ptr(),
2011            dst_opts.data_model.unwrap_memory().ptr(),
2012        );
2013        let dst_len = self.local_tee_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
2014        self.ptr_uconst(dst_mem_opts, 1);
2015        self.ptr_shl(dst_mem_opts);
2016        let dst_byte_len = self.local_set_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
2017        let dst = {
2018            let dst_mem = self.malloc(
2019                dst_opts,
2020                AllocSize::Local(dst_byte_len.idx),
2021                2,
2022                Trap::StringOutOfBounds,
2023            );
2024            WasmString {
2025                ptr: dst_mem.addr,
2026                len: dst_len,
2027                opts: dst_opts,
2028            }
2029        };
2030
2031        let transcode = self.transcoder(src, &dst, Transcode::Utf8ToUtf16);
2032        self.instruction(LocalGet(src.ptr.idx));
2033        self.instruction(LocalGet(src.len.idx));
2034        self.instruction(LocalGet(dst.ptr.idx));
2035        self.instruction(Call(transcode.as_u32()));
2036        self.instruction(LocalSet(dst.len.idx));
2037
2038        // If the number of code units returned by transcode is not
2039        // equal to the original number of code units then
2040        // the buffer must be shrunk.
2041        //
2042        // Note that the byte length of the final allocation we
2043        // want is twice the code unit length returned by the
2044        // transcoding function.
2045        self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2046        self.instruction(LocalGet(dst.len.idx));
2047        self.ptr_ne(dst_mem_opts);
2048        self.instruction(If(BlockType::Empty));
2049        self.realloc(
2050            dst.opts,
2051            &dst.ptr,
2052            AllocSize::Local(dst_byte_len.idx),
2053            AllocSize::DoubleLocal(dst.len.idx),
2054            2,
2055            Trap::StringOutOfBounds,
2056        );
2057        self.instruction(End); // end of shrink-to-fit
2058
2059        self.free_temp_local(dst_byte_len);
2060
2061        dst
2062    }
2063
2064    // Corresponds to `store_probably_utf16_to_latin1_or_utf16` in the spec.
2065    //
2066    // This will try to transcode the input utf16 string to utf16 in the
2067    // destination. If utf16 isn't needed though and latin1 could be used
2068    // then that's used instead and a reallocation to downsize occurs
2069    // afterwards.
2070    //
2071    // The host transcode function here will take the src/dst pointers as
2072    // well as src length. The destination byte length is twice the src code
2073    // unit length. The return value is the tagged length of the returned
2074    // string. If the upper bit is set then utf16 was used and the
2075    // conversion is done. If the upper bit is not set then latin1 was used
2076    // and a downsizing needs to happen.
2077    fn string_compact_utf16_to_compact<'c>(
2078        &mut self,
2079        src: &WasmString<'_>,
2080        dst_opts: &'c Options,
2081    ) -> WasmString<'c> {
2082        let src_mem_opts = match &src.opts.data_model {
2083            DataModel::Gc {} => todo!("CM+GC"),
2084            DataModel::LinearMemory(opts) => opts,
2085        };
2086        let dst_mem_opts = match &dst_opts.data_model {
2087            DataModel::Gc {} => todo!("CM+GC"),
2088            DataModel::LinearMemory(opts) => opts,
2089        };
2090
2091        self.validate_string_length(src, FE::Utf16);
2092        self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2093        let dst_len = self.local_tee_new_tmp(dst_mem_opts.ptr());
2094        self.ptr_uconst(dst_mem_opts, 1);
2095        self.ptr_shl(dst_mem_opts);
2096        let dst_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
2097        let dst = {
2098            let dst_mem = self.malloc(
2099                dst_opts,
2100                AllocSize::Local(dst_byte_len.idx),
2101                2,
2102                Trap::StringOutOfBounds,
2103            );
2104            WasmString {
2105                ptr: dst_mem.addr,
2106                len: dst_len,
2107                opts: dst_opts,
2108            }
2109        };
2110
2111        self.convert_src_len_to_dst(
2112            dst_byte_len.idx,
2113            dst.opts.data_model.unwrap_memory().ptr(),
2114            src_mem_opts.ptr(),
2115        );
2116        let src_byte_len = self.local_set_new_tmp(src_mem_opts.ptr());
2117
2118        let transcode = self.transcoder(src, &dst, Transcode::Utf16ToCompactProbablyUtf16);
2119        self.instruction(LocalGet(src.ptr.idx));
2120        self.instruction(LocalGet(src.len.idx));
2121        self.instruction(LocalGet(dst.ptr.idx));
2122        self.instruction(Call(transcode.as_u32()));
2123        self.instruction(LocalSet(dst.len.idx));
2124
2125        // Assert that the untagged code unit length is the same as the
2126        // source code unit length.
2127        if self.module.tunables.debug_adapter_modules {
2128            self.instruction(LocalGet(dst.len.idx));
2129            self.ptr_uconst(dst_mem_opts, !UTF16_TAG);
2130            self.ptr_and(dst_mem_opts);
2131            self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2132            self.ptr_ne(dst_mem_opts);
2133            self.instruction(If(BlockType::Empty));
2134            self.trap(Trap::DebugAssertEqualCodeUnits);
2135            self.instruction(End);
2136        }
2137
2138        // If the UTF16_TAG is set then utf16 was used and the destination
2139        // should be appropriately sized. Bail out of the "is this string
2140        // empty" block and fall through otherwise to resizing.
2141        self.instruction(LocalGet(dst.len.idx));
2142        self.ptr_uconst(dst_mem_opts, UTF16_TAG);
2143        self.ptr_and(dst_mem_opts);
2144        self.ptr_br_if(dst_mem_opts, 0);
2145
2146        // Here `realloc` is used to downsize the string
2147        self.realloc(
2148            dst.opts,
2149            &dst.ptr,
2150            AllocSize::Local(dst_byte_len.idx),
2151            AllocSize::Local(dst.len.idx),
2152            2,
2153            Trap::StringOutOfBounds,
2154        );
2155
2156        self.free_temp_local(dst_byte_len);
2157        self.free_temp_local(src_byte_len);
2158
2159        dst
2160    }
2161
2162    // Corresponds to `store_string_to_latin1_or_utf16` in the spec.
2163    //
2164    // This will attempt a first pass of transcoding to latin1 and on
2165    // failure a larger buffer is allocated for utf16 and then utf16 is
2166    // encoded in-place into the buffer. After either latin1 or utf16 the
2167    // buffer is then resized to fit the final string allocation.
2168    fn string_to_compact<'c>(
2169        &mut self,
2170        src: &WasmString<'_>,
2171        src_enc: FE,
2172        dst_opts: &'c Options,
2173    ) -> WasmString<'c> {
2174        let src_mem_opts = match &src.opts.data_model {
2175            DataModel::Gc {} => todo!("CM+GC"),
2176            DataModel::LinearMemory(opts) => opts,
2177        };
2178        let dst_mem_opts = match &dst_opts.data_model {
2179            DataModel::Gc {} => todo!("CM+GC"),
2180            DataModel::LinearMemory(opts) => opts,
2181        };
2182
2183        self.validate_string_length(src, src_enc);
2184
2185        self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2186        let dst_len = self.local_tee_new_tmp(dst_mem_opts.ptr());
2187        let dst_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
2188        let dst = {
2189            let dst_mem = self.malloc(
2190                dst_opts,
2191                AllocSize::Local(dst_byte_len.idx),
2192                2,
2193                Trap::StringOutOfBounds,
2194            );
2195            WasmString {
2196                ptr: dst_mem.addr,
2197                len: dst_len,
2198                opts: dst_opts,
2199            }
2200        };
2201
2202        // Perform the initial latin1 transcode. This returns the number of
2203        // source code units consumed and the number of destination code
2204        // units (bytes) written.
2205        let (latin1, utf16) = match src_enc {
2206            FE::Utf8 => (Transcode::Utf8ToLatin1, Transcode::Utf8ToCompactUtf16),
2207            FE::Utf16 => (Transcode::Utf16ToLatin1, Transcode::Utf16ToCompactUtf16),
2208            FE::Latin1 => unreachable!(),
2209        };
2210        let transcode_latin1 = self.transcoder(src, &dst, latin1);
2211        let transcode_utf16 = self.transcoder(src, &dst, utf16);
2212        self.instruction(LocalGet(src.ptr.idx));
2213        self.instruction(LocalGet(src.len.idx));
2214        self.instruction(LocalGet(dst.ptr.idx));
2215        self.instruction(Call(transcode_latin1.as_u32()));
2216        self.instruction(LocalSet(dst.len.idx));
2217        let src_len_tmp = self.local_set_new_tmp(src_mem_opts.ptr());
2218
2219        // If the source was entirely consumed then the transcode completed
2220        // and all that's necessary is to optionally shrink the buffer.
2221        self.instruction(LocalGet(src_len_tmp.idx));
2222        self.instruction(LocalGet(src.len.idx));
2223        self.ptr_eq(src_mem_opts);
2224        self.instruction(If(BlockType::Empty)); // if latin1-or-utf16 block
2225
2226        // Test if the original byte length of the allocation is the same as
2227        // the number of written bytes, and if not then shrink the buffer
2228        // with a call to `realloc`.
2229        self.instruction(LocalGet(dst_byte_len.idx));
2230        self.instruction(LocalGet(dst.len.idx));
2231        self.ptr_ne(dst_mem_opts);
2232        self.instruction(If(BlockType::Empty));
2233        self.realloc(
2234            dst.opts,
2235            &dst.ptr,
2236            AllocSize::Local(dst_byte_len.idx),
2237            AllocSize::Local(dst.len.idx),
2238            2,
2239            Trap::StringOutOfBounds,
2240        );
2241        self.instruction(End);
2242
2243        // In this block the latin1 encoding failed. The host transcode
2244        // returned how many units were consumed from the source and how
2245        // many bytes were written to the destination. Here the buffer is
2246        // inflated and sized and the second utf16 intrinsic is invoked to
2247        // perform the final inflation.
2248        self.instruction(Else); // else latin1-or-utf16 block
2249
2250        // For utf8 validate that the inflated size is still within bounds.
2251        if src_enc.width() == 1 {
2252            self.validate_string_length_u8(src, 2);
2253        }
2254
2255        // Reallocate the buffer with twice the source code units in byte
2256        // size.
2257        self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2258        self.ptr_uconst(dst_mem_opts, 1);
2259        self.ptr_shl(dst_mem_opts);
2260        let new_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
2261        self.realloc(
2262            dst.opts,
2263            &dst.ptr,
2264            AllocSize::Local(dst_byte_len.idx),
2265            AllocSize::Local(new_byte_len.idx),
2266            2,
2267            Trap::StringOutOfBounds,
2268        );
2269        self.instruction(LocalGet(new_byte_len.idx));
2270        self.instruction(LocalSet(dst_byte_len.idx));
2271        self.free_temp_local(new_byte_len);
2272
2273        // Call the host utf16 transcoding function. This will inflate the
2274        // prior latin1 bytes and then encode the rest of the source string
2275        // as utf16 into the remaining space in the destination buffer.
2276        self.instruction(LocalGet(src.ptr.idx));
2277        self.instruction(LocalGet(src_len_tmp.idx));
2278        if let FE::Utf16 = src_enc {
2279            self.ptr_uconst(src_mem_opts, 1);
2280            self.ptr_shl(src_mem_opts);
2281        }
2282        self.ptr_add(src_mem_opts);
2283        self.instruction(LocalGet(src.len.idx));
2284        self.instruction(LocalGet(src_len_tmp.idx));
2285        self.ptr_sub(src_mem_opts);
2286        self.instruction(LocalGet(dst.ptr.idx));
2287        self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2288        self.instruction(LocalGet(dst.len.idx));
2289        self.instruction(Call(transcode_utf16.as_u32()));
2290        self.instruction(LocalSet(dst.len.idx));
2291
2292        // If the returned number of code units written to the destination
2293        // is not equal to the size of the allocation then the allocation is
2294        // resized down to the appropriate size.
2295        //
2296        // Note that the byte size desired is `2*dst_len` and the current
2297        // byte buffer size is `2*src_len` so the `2` factor isn't checked
2298        // here, just the lengths.
2299        self.instruction(LocalGet(dst.len.idx));
2300        self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2301        self.ptr_ne(dst_mem_opts);
2302        self.instruction(If(BlockType::Empty));
2303        self.realloc(
2304            dst.opts,
2305            &dst.ptr,
2306            AllocSize::Local(dst_byte_len.idx),
2307            AllocSize::DoubleLocal(dst.len.idx),
2308            2,
2309            Trap::StringOutOfBounds,
2310        );
2311        self.instruction(End);
2312
2313        // Tag the returned pointer as utf16
2314        self.instruction(LocalGet(dst.len.idx));
2315        self.ptr_uconst(dst_mem_opts, UTF16_TAG);
2316        self.ptr_or(dst_mem_opts);
2317        self.instruction(LocalSet(dst.len.idx));
2318
2319        self.instruction(End); // end latin1-or-utf16 block
2320
2321        self.free_temp_local(src_len_tmp);
2322        self.free_temp_local(dst_byte_len);
2323
2324        dst
2325    }
2326
2327    fn validate_string_length(&mut self, src: &WasmString<'_>, dst: FE) {
2328        self.validate_string_length_u8(src, dst.width())
2329    }
2330
2331    fn validate_string_length_u8(&mut self, s: &WasmString<'_>, dst: u8) {
2332        let mem_opts = match &s.opts.data_model {
2333            DataModel::Gc {} => todo!("CM+GC"),
2334            DataModel::LinearMemory(opts) => opts,
2335        };
2336
2337        // Check to see if the source byte length is out of bounds in
2338        // which case a trap is generated.
2339        self.instruction(LocalGet(s.len.idx));
2340        let max = MAX_STRING_BYTE_LENGTH / u32::from(dst);
2341        self.ptr_uconst(mem_opts, max);
2342        self.ptr_gt_u(mem_opts);
2343        self.instruction(If(BlockType::Empty));
2344        self.trap(Trap::StringOutOfBounds);
2345        self.instruction(End);
2346    }
2347
2348    fn transcoder(
2349        &mut self,
2350        src: &WasmString<'_>,
2351        dst: &WasmString<'_>,
2352        op: Transcode,
2353    ) -> FuncIndex {
2354        match (src.opts.data_model, dst.opts.data_model) {
2355            (DataModel::Gc {}, _) | (_, DataModel::Gc {}) => {
2356                todo!("CM+GC")
2357            }
2358            (
2359                DataModel::LinearMemory(LinearMemoryOptions {
2360                    memory: Some((src_mem, src_ty)),
2361                    realloc: _,
2362                }),
2363                DataModel::LinearMemory(LinearMemoryOptions {
2364                    memory: Some((dst_mem, dst_ty)),
2365                    realloc: _,
2366                }),
2367            ) => self.module.import_transcoder(Transcoder {
2368                from_memory: src_mem,
2369                from_memory64: src_ty.idx_type == IndexType::I64,
2370                to_memory: dst_mem,
2371                to_memory64: dst_ty.idx_type == IndexType::I64,
2372                op,
2373            }),
2374            (DataModel::LinearMemory(LinearMemoryOptions { memory: None, .. }), _)
2375            | (_, DataModel::LinearMemory(LinearMemoryOptions { memory: None, .. })) => {
2376                unreachable!()
2377            }
2378        }
2379    }
2380
2381    /// Shared preamble for translating list-like sequences (lists and maps).
2382    ///
2383    /// Emits: load ptr/len from source, compute byte lengths, malloc
2384    /// destination, validate bounds, and if element sizes are non-zero opens
2385    /// Block + Loop and initializes iteration locals.
2386    ///
2387    /// Returns a `SequenceTranslation` that the caller uses to emit the
2388    /// loop body before calling `end_translate_sequence`.
2389    fn begin_translate_sequence<'c>(
2390        &mut self,
2391        src: &Source<'c>,
2392        dst: &Destination<'c>,
2393        src_element_size: u32,
2394        src_element_align: u32,
2395        dst_element_size: u32,
2396        dst_element_align: u32,
2397    ) -> SequenceTranslation<'c> {
2398        let src_mem_opts = match &src.opts().data_model {
2399            DataModel::Gc {} => todo!("CM+GC"),
2400            DataModel::LinearMemory(opts) => opts,
2401        };
2402        let dst_mem_opts = match &dst.opts().data_model {
2403            DataModel::Gc {} => todo!("CM+GC"),
2404            DataModel::LinearMemory(opts) => opts,
2405        };
2406
2407        let src_opts = src.opts();
2408        let dst_opts = dst.opts();
2409
2410        // Load the pointer/length of this sequence into temporary locals.
2411        // These will be referenced a good deal so this just makes it easier
2412        // to deal with them consistently below rather than trying to reload
2413        // from memory for example.
2414        match src {
2415            Source::Stack(s) => {
2416                assert_eq!(s.locals.len(), 2);
2417                self.stack_get(&s.slice(0..1), src_mem_opts.ptr());
2418                self.stack_get(&s.slice(1..2), src_mem_opts.ptr());
2419            }
2420            Source::Memory(mem) => {
2421                self.ptr_load(mem);
2422                self.ptr_load(&mem.bump(src_mem_opts.ptr_size().into()));
2423            }
2424            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
2425        }
2426        let src_len = self.local_set_new_tmp(src_mem_opts.ptr());
2427        let src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
2428
2429        // Calculate the source/destination byte lengths into unique locals.
2430        let src_byte_len =
2431            self.calculate_list_byte_len(src_mem_opts, src_len.idx, src_element_size);
2432        let dst_byte_len = if src_element_size == dst_element_size {
2433            self.convert_src_len_to_dst(src_byte_len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2434            self.local_set_new_tmp(dst_mem_opts.ptr())
2435        } else if src_mem_opts.ptr() == dst_mem_opts.ptr() {
2436            self.calculate_list_byte_len(dst_mem_opts, src_len.idx, dst_element_size)
2437        } else {
2438            self.convert_src_len_to_dst(src_len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2439            let tmp = self.local_set_new_tmp(dst_mem_opts.ptr());
2440            let ret = self.calculate_list_byte_len(dst_mem_opts, tmp.idx, dst_element_size);
2441            self.free_temp_local(tmp);
2442            ret
2443        };
2444
2445        // Create a `Memory` operand which will internally assert that the
2446        // `src_ptr` value is properly aligned.
2447        let src_mem = self.memory_operand(
2448            src_opts,
2449            src_ptr,
2450            AllocSize::Local(src_byte_len.idx),
2451            src_element_align,
2452            Trap::ListOutOfBounds,
2453        );
2454
2455        // Here `realloc` is invoked (in a `malloc`-like fashion) to allocate
2456        // space for the sequence in the destination memory. This will also
2457        // internally insert checks that the returned pointer is aligned
2458        // correctly for the destination.
2459        let dst_mem = self.malloc(
2460            dst_opts,
2461            AllocSize::Local(dst_byte_len.idx),
2462            dst_element_align,
2463            Trap::ListOutOfBounds,
2464        );
2465
2466        self.free_temp_local(src_byte_len);
2467        self.free_temp_local(dst_byte_len);
2468
2469        // If both element sizes are 0 then there's nothing to copy so the
2470        // loop is skipped entirely. Otherwise open a Block (for early exit
2471        // on zero-length) and a Loop for the per-element iteration.
2472        let loop_state = if src_element_size > 0 || dst_element_size > 0 {
2473            self.instruction(Block(BlockType::Empty));
2474
2475            // Set the `remaining` local and only continue if it's > 0.
2476            self.instruction(LocalGet(src_len.idx));
2477            let remaining = self.local_tee_new_tmp(src_mem_opts.ptr());
2478            self.ptr_eqz(src_mem_opts);
2479            self.instruction(BrIf(0));
2480
2481            // Initialize the two iteration pointers to their starting values.
2482            self.instruction(LocalGet(src_mem.addr.idx));
2483            let cur_src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
2484            self.instruction(LocalGet(dst_mem.addr.idx));
2485            let cur_dst_ptr = self.local_set_new_tmp(dst_mem_opts.ptr());
2486
2487            self.instruction(Loop(BlockType::Empty));
2488
2489            Some(SequenceLoopState {
2490                remaining,
2491                cur_src_ptr,
2492                cur_dst_ptr,
2493            })
2494        } else {
2495            None
2496        };
2497
2498        SequenceTranslation {
2499            src_len,
2500            src_mem,
2501            dst_mem,
2502            src_opts,
2503            dst_opts,
2504            src_mem_opts,
2505            dst_mem_opts,
2506            loop_state,
2507        }
2508    }
2509
2510    /// Shared epilogue for translating list-like sequences.
2511    ///
2512    /// If a loop was opened, emits: decrement remaining, BrIf to loop
2513    /// head, End loop, End block, and frees iteration locals. Then stores
2514    /// the ptr/len pair into the destination and frees all temporaries.
2515    fn end_translate_sequence(&mut self, seq: SequenceTranslation<'_>, dst: &Destination) {
2516        if let Some(loop_state) = seq.loop_state {
2517            // Update the remaining count, falling through to break out if
2518            // it's zero now.
2519            self.instruction(LocalGet(loop_state.remaining.idx));
2520            self.ptr_iconst(seq.src_mem_opts, -1);
2521            self.ptr_add(seq.src_mem_opts);
2522            self.instruction(LocalTee(loop_state.remaining.idx));
2523            self.ptr_br_if(seq.src_mem_opts, 0);
2524            self.instruction(End); // end of loop
2525            self.instruction(End); // end of block
2526
2527            self.free_temp_local(loop_state.cur_dst_ptr);
2528            self.free_temp_local(loop_state.cur_src_ptr);
2529            self.free_temp_local(loop_state.remaining);
2530        }
2531
2532        // Store the ptr/length in the desired destination.
2533        match dst {
2534            Destination::Stack(s, _) => {
2535                self.instruction(LocalGet(seq.dst_mem.addr.idx));
2536                self.stack_set(&s[..1], seq.dst_mem_opts.ptr());
2537                self.convert_src_len_to_dst(
2538                    seq.src_len.idx,
2539                    seq.src_mem_opts.ptr(),
2540                    seq.dst_mem_opts.ptr(),
2541                );
2542                self.stack_set(&s[1..], seq.dst_mem_opts.ptr());
2543            }
2544            Destination::Memory(mem) => {
2545                self.instruction(LocalGet(mem.addr.idx));
2546                self.instruction(LocalGet(seq.dst_mem.addr.idx));
2547                self.ptr_store(mem);
2548                self.instruction(LocalGet(mem.addr.idx));
2549                self.convert_src_len_to_dst(
2550                    seq.src_len.idx,
2551                    seq.src_mem_opts.ptr(),
2552                    seq.dst_mem_opts.ptr(),
2553                );
2554                self.ptr_store(&mem.bump(seq.dst_mem_opts.ptr_size().into()));
2555            }
2556            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
2557        }
2558
2559        self.free_temp_local(seq.src_len);
2560        self.free_temp_local(seq.src_mem.addr);
2561        self.free_temp_local(seq.dst_mem.addr);
2562    }
2563
2564    fn translate_list(
2565        &mut self,
2566        src_ty: TypeListIndex,
2567        src: &Source<'_>,
2568        dst_ty: &InterfaceType,
2569        dst: &Destination,
2570    ) {
2571        let src_mem_opts = match &src.opts().data_model {
2572            DataModel::Gc {} => todo!("CM+GC"),
2573            DataModel::LinearMemory(opts) => opts,
2574        };
2575        let dst_mem_opts = match &dst.opts().data_model {
2576            DataModel::Gc {} => todo!("CM+GC"),
2577            DataModel::LinearMemory(opts) => opts,
2578        };
2579
2580        let src_element_ty = &self.types[src_ty].element;
2581        let dst_element_ty = match dst_ty {
2582            InterfaceType::List(r) => &self.types[*r].element,
2583            _ => panic!("expected a list"),
2584        };
2585        let (src_size, src_align) = self.types.size_align(src_mem_opts, src_element_ty);
2586        let (dst_size, dst_align) = self.types.size_align(dst_mem_opts, dst_element_ty);
2587
2588        let seq = self.begin_translate_sequence(src, dst, src_size, src_align, dst_size, dst_align);
2589
2590        if let Some(ref loop_state) = seq.loop_state {
2591            let element_src = Source::Memory(Memory {
2592                opts: seq.src_opts,
2593                offset: 0,
2594                addr: TempLocal::new(loop_state.cur_src_ptr.idx, loop_state.cur_src_ptr.ty),
2595            });
2596            let element_dst = Destination::Memory(Memory {
2597                opts: seq.dst_opts,
2598                offset: 0,
2599                addr: TempLocal::new(loop_state.cur_dst_ptr.idx, loop_state.cur_dst_ptr.ty),
2600            });
2601            self.translate(src_element_ty, &element_src, dst_element_ty, &element_dst);
2602
2603            if src_size > 0 {
2604                self.instruction(LocalGet(loop_state.cur_src_ptr.idx));
2605                self.ptr_uconst(src_mem_opts, src_size);
2606                self.ptr_add(src_mem_opts);
2607                self.instruction(LocalSet(loop_state.cur_src_ptr.idx));
2608            }
2609            if dst_size > 0 {
2610                self.instruction(LocalGet(loop_state.cur_dst_ptr.idx));
2611                self.ptr_uconst(dst_mem_opts, dst_size);
2612                self.ptr_add(dst_mem_opts);
2613                self.instruction(LocalSet(loop_state.cur_dst_ptr.idx));
2614            }
2615        }
2616
2617        self.end_translate_sequence(seq, dst);
2618    }
2619
2620    /// Translates a map from one component's memory to another.
2621    ///
2622    /// In the Component Model, a `map<K, V>` is stored in memory as
2623    /// `list<tuple<K, V>>`, so the translation reuses the same sequence
2624    /// scaffolding as lists but with a two-field (key, value) loop body.
2625    fn translate_map(
2626        &mut self,
2627        src_ty: TypeMapIndex,
2628        src: &Source<'_>,
2629        dst_ty: &InterfaceType,
2630        dst: &Destination,
2631    ) {
2632        let src_mem_opts = match &src.opts().data_model {
2633            DataModel::Gc {} => todo!("CM+GC"),
2634            DataModel::LinearMemory(opts) => opts,
2635        };
2636        let dst_mem_opts = match &dst.opts().data_model {
2637            DataModel::Gc {} => todo!("CM+GC"),
2638            DataModel::LinearMemory(opts) => opts,
2639        };
2640
2641        let src_map_ty = &self.types[src_ty];
2642        let dst_map_ty = match dst_ty {
2643            InterfaceType::Map(r) => &self.types[*r],
2644            _ => panic!("expected a map"),
2645        };
2646
2647        // Each map entry is a tuple<K, V> following record layout rules.
2648        let src_key_abi = self.types.canonical_abi(&src_map_ty.key);
2649        let src_value_abi = self.types.canonical_abi(&src_map_ty.value);
2650        let src_entry_abi = CanonicalAbiInfo::record([src_key_abi, src_value_abi].into_iter());
2651        let (src_tuple_size, src_entry_align) = src_mem_opts.sizealign(&src_entry_abi);
2652        let src_value_offset = {
2653            let mut offset = 0u32;
2654            if src_mem_opts.memory64() {
2655                src_key_abi.next_field64(&mut offset);
2656                src_value_abi.next_field64(&mut offset)
2657            } else {
2658                src_key_abi.next_field32(&mut offset);
2659                src_value_abi.next_field32(&mut offset)
2660            }
2661        };
2662
2663        let dst_key_abi = self.types.canonical_abi(&dst_map_ty.key);
2664        let dst_value_abi = self.types.canonical_abi(&dst_map_ty.value);
2665        let dst_entry_abi = CanonicalAbiInfo::record([dst_key_abi, dst_value_abi].into_iter());
2666        let (dst_tuple_size, dst_entry_align) = dst_mem_opts.sizealign(&dst_entry_abi);
2667        let dst_value_offset = {
2668            let mut offset = 0u32;
2669            if dst_mem_opts.memory64() {
2670                dst_key_abi.next_field64(&mut offset);
2671                dst_value_abi.next_field64(&mut offset)
2672            } else {
2673                dst_key_abi.next_field32(&mut offset);
2674                dst_value_abi.next_field32(&mut offset)
2675            }
2676        };
2677
2678        let seq = self.begin_translate_sequence(
2679            src,
2680            dst,
2681            src_tuple_size,
2682            src_entry_align,
2683            dst_tuple_size,
2684            dst_entry_align,
2685        );
2686
2687        if let Some(ref loop_state) = seq.loop_state {
2688            let key_src = Source::Memory(Memory {
2689                opts: seq.src_opts,
2690                offset: 0,
2691                addr: TempLocal::new(loop_state.cur_src_ptr.idx, src_mem_opts.ptr()),
2692            });
2693            let key_dst = Destination::Memory(Memory {
2694                opts: seq.dst_opts,
2695                offset: 0,
2696                addr: TempLocal::new(loop_state.cur_dst_ptr.idx, dst_mem_opts.ptr()),
2697            });
2698            self.translate(&src_map_ty.key, &key_src, &dst_map_ty.key, &key_dst);
2699
2700            let value_src = Source::Memory(Memory {
2701                opts: seq.src_opts,
2702                offset: src_value_offset,
2703                addr: TempLocal::new(loop_state.cur_src_ptr.idx, src_mem_opts.ptr()),
2704            });
2705            let value_dst = Destination::Memory(Memory {
2706                opts: seq.dst_opts,
2707                offset: dst_value_offset,
2708                addr: TempLocal::new(loop_state.cur_dst_ptr.idx, dst_mem_opts.ptr()),
2709            });
2710            self.translate(&src_map_ty.value, &value_src, &dst_map_ty.value, &value_dst);
2711
2712            // Advance past value + trailing padding to the next entry
2713            if src_tuple_size > 0 {
2714                self.instruction(LocalGet(loop_state.cur_src_ptr.idx));
2715                self.ptr_uconst(src_mem_opts, src_tuple_size);
2716                self.ptr_add(src_mem_opts);
2717                self.instruction(LocalSet(loop_state.cur_src_ptr.idx));
2718            }
2719            if dst_tuple_size > 0 {
2720                self.instruction(LocalGet(loop_state.cur_dst_ptr.idx));
2721                self.ptr_uconst(dst_mem_opts, dst_tuple_size);
2722                self.ptr_add(dst_mem_opts);
2723                self.instruction(LocalSet(loop_state.cur_dst_ptr.idx));
2724            }
2725        }
2726
2727        self.end_translate_sequence(seq, dst);
2728    }
2729
2730    fn calculate_list_byte_len(
2731        &mut self,
2732        opts: &LinearMemoryOptions,
2733        len_local: u32,
2734        elt_size: u32,
2735    ) -> TempLocal {
2736        // Zero-size types are easy to handle here because the byte size of the
2737        // destination is always zero.
2738        if elt_size == 0 {
2739            self.ptr_uconst(opts, 0);
2740            return self.local_set_new_tmp(opts.ptr());
2741        }
2742
2743        // For one-byte elements in the destination the check here can be a bit
2744        // more optimal than the general case below. In these situations if the
2745        // source pointer type is 32-bit then we're guaranteed to not overflow,
2746        // so the source length is simply casted to the destination's type.
2747        //
2748        // If the source is 64-bit then all that needs to be checked is to
2749        // ensure that it does not have the upper 32-bits set.
2750        if elt_size == 1 {
2751            if let ValType::I64 = opts.ptr() {
2752                self.instruction(LocalGet(len_local));
2753                self.instruction(I64Const(32));
2754                self.instruction(I64ShrU);
2755                self.instruction(I32WrapI64);
2756                self.instruction(If(BlockType::Empty));
2757                self.trap(Trap::ListOutOfBounds);
2758                self.instruction(End);
2759            }
2760            self.instruction(LocalGet(len_local));
2761            return self.local_set_new_tmp(opts.ptr());
2762        }
2763
2764        // The main check implemented by this function is to verify that
2765        // `src_len_local` does not exceed the 32-bit range. Byte sizes for
2766        // lists must always fit in 32-bits to get transferred to 32-bit
2767        // memories.
2768        self.instruction(Block(BlockType::Empty));
2769        self.instruction(Block(BlockType::Empty));
2770        self.instruction(LocalGet(len_local));
2771        match opts.ptr() {
2772            // The source's list length is guaranteed to be less than 32-bits
2773            // so simply extend it up to a 64-bit type for the multiplication
2774            // below.
2775            ValType::I32 => self.instruction(I64ExtendI32U),
2776
2777            // If the source is a 64-bit memory then if the item length doesn't
2778            // fit in 32-bits the byte length definitely won't, so generate a
2779            // branch to our overflow trap here if any of the upper 32-bits are set.
2780            ValType::I64 => {
2781                self.instruction(I64Const(32));
2782                self.instruction(I64ShrU);
2783                self.instruction(I32WrapI64);
2784                self.instruction(BrIf(0));
2785                self.instruction(LocalGet(len_local));
2786            }
2787
2788            _ => unreachable!(),
2789        }
2790
2791        // Next perform a 64-bit multiplication with the element byte size that
2792        // is itself guaranteed to fit in 32-bits. The result is then checked
2793        // to see if we overflowed the 32-bit space. The two input operands to
2794        // the multiplication are guaranteed to be 32-bits at most which means
2795        // that this multiplication shouldn't overflow.
2796        //
2797        // The result of the multiplication is saved into a local as well to
2798        // get the result afterwards.
2799        self.instruction(I64Const(elt_size.into()));
2800        self.instruction(I64Mul);
2801        let tmp = self.local_tee_new_tmp(ValType::I64);
2802        // Branch to success if the upper 32-bits are zero, otherwise
2803        // fall-through to the trap.
2804        self.instruction(I64Const(32));
2805        self.instruction(I64ShrU);
2806        self.instruction(I64Eqz);
2807        self.instruction(BrIf(1));
2808        self.instruction(End);
2809        self.trap(Trap::ListOutOfBounds);
2810        self.instruction(End);
2811
2812        // If a fresh local was used to store the result of the multiplication
2813        // then convert it down to 32-bits which should be guaranteed to not
2814        // lose information at this point.
2815        if opts.ptr() == ValType::I64 {
2816            tmp
2817        } else {
2818            self.instruction(LocalGet(tmp.idx));
2819            self.instruction(I32WrapI64);
2820            self.free_temp_local(tmp);
2821            self.local_set_new_tmp(ValType::I32)
2822        }
2823    }
2824
2825    fn convert_src_len_to_dst(
2826        &mut self,
2827        src_len_local: u32,
2828        src_ptr_ty: ValType,
2829        dst_ptr_ty: ValType,
2830    ) {
2831        self.instruction(LocalGet(src_len_local));
2832        match (src_ptr_ty, dst_ptr_ty) {
2833            (ValType::I32, ValType::I64) => self.instruction(I64ExtendI32U),
2834            (ValType::I64, ValType::I32) => self.instruction(I32WrapI64),
2835            (src, dst) => assert_eq!(src, dst),
2836        }
2837    }
2838
2839    fn translate_record(
2840        &mut self,
2841        src_ty: TypeRecordIndex,
2842        src: &Source<'_>,
2843        dst_ty: &InterfaceType,
2844        dst: &Destination,
2845    ) {
2846        let src_ty = &self.types[src_ty];
2847        let dst_ty = match dst_ty {
2848            InterfaceType::Record(r) => &self.types[*r],
2849            _ => panic!("expected a record"),
2850        };
2851
2852        // TODO: subtyping
2853        assert_eq!(src_ty.fields.len(), dst_ty.fields.len());
2854
2855        // First a map is made of the source fields to where they're coming
2856        // from (e.g. which offset or which locals). This map is keyed by the
2857        // fields' names
2858        let mut src_fields = HashMap::new();
2859        for (i, src) in src
2860            .record_field_srcs(self.types, src_ty.fields.iter().map(|f| f.ty))
2861            .enumerate()
2862        {
2863            let field = &src_ty.fields[i];
2864            src_fields.insert(&field.name, (src, &field.ty));
2865        }
2866
2867        // .. and next translation is performed in the order of the destination
2868        // fields in case the destination is the stack to ensure that the stack
2869        // has the fields all in the right order.
2870        //
2871        // Note that the lookup in `src_fields` is an infallible lookup which
2872        // will panic if the field isn't found.
2873        //
2874        // TODO: should that lookup be fallible with subtyping?
2875        for (i, dst) in dst
2876            .record_field_dsts(self.types, dst_ty.fields.iter().map(|f| f.ty))
2877            .enumerate()
2878        {
2879            let field = &dst_ty.fields[i];
2880            let (src, src_ty) = &src_fields[&field.name];
2881            self.translate(src_ty, src, &field.ty, &dst);
2882        }
2883    }
2884
2885    fn translate_flags(
2886        &mut self,
2887        src_ty: TypeFlagsIndex,
2888        src: &Source<'_>,
2889        dst_ty: &InterfaceType,
2890        dst: &Destination,
2891    ) {
2892        let src_ty = &self.types[src_ty];
2893        let dst_ty = match dst_ty {
2894            InterfaceType::Flags(r) => &self.types[*r],
2895            _ => panic!("expected a record"),
2896        };
2897
2898        // TODO: subtyping
2899        //
2900        // Notably this implementation does not support reordering flags from
2901        // the source to the destination nor having more flags in the
2902        // destination. Currently this is a copy from source to destination
2903        // in-bulk. Otherwise reordering indices would have to have some sort of
2904        // fancy bit twiddling tricks or something like that.
2905        assert_eq!(src_ty.names, dst_ty.names);
2906        let cnt = src_ty.names.len();
2907        match FlagsSize::from_count(cnt) {
2908            FlagsSize::Size0 => {}
2909            FlagsSize::Size1 => {
2910                let mask = if cnt == 8 { 0xff } else { (1 << cnt) - 1 };
2911                self.convert_u8_mask(src, dst, mask);
2912            }
2913            FlagsSize::Size2 => {
2914                let mask = if cnt == 16 { 0xffff } else { (1 << cnt) - 1 };
2915                self.convert_u16_mask(src, dst, mask);
2916            }
2917            FlagsSize::Size4Plus(n) => {
2918                let srcs = src.record_field_srcs(self.types, (0..n).map(|_| InterfaceType::U32));
2919                let dsts = dst.record_field_dsts(self.types, (0..n).map(|_| InterfaceType::U32));
2920                let n = usize::from(n);
2921                for (i, (src, dst)) in srcs.zip(dsts).enumerate() {
2922                    let mask = if i == n - 1 && (cnt % 32 != 0) {
2923                        (1 << (cnt % 32)) - 1
2924                    } else {
2925                        0xffffffff
2926                    };
2927                    self.convert_u32_mask(&src, &dst, mask);
2928                }
2929            }
2930        }
2931    }
2932
2933    fn translate_tuple(
2934        &mut self,
2935        src_ty: TypeTupleIndex,
2936        src: &Source<'_>,
2937        dst_ty: &InterfaceType,
2938        dst: &Destination,
2939    ) {
2940        let src_ty = &self.types[src_ty];
2941        let dst_ty = match dst_ty {
2942            InterfaceType::Tuple(t) => &self.types[*t],
2943            _ => panic!("expected a tuple"),
2944        };
2945
2946        // TODO: subtyping
2947        assert_eq!(src_ty.types.len(), dst_ty.types.len());
2948
2949        let srcs = src
2950            .record_field_srcs(self.types, src_ty.types.iter().copied())
2951            .zip(src_ty.types.iter());
2952        let dsts = dst
2953            .record_field_dsts(self.types, dst_ty.types.iter().copied())
2954            .zip(dst_ty.types.iter());
2955        for ((src, src_ty), (dst, dst_ty)) in srcs.zip(dsts) {
2956            self.translate(src_ty, &src, dst_ty, &dst);
2957        }
2958    }
2959
2960    fn translate_fixed_length_list(
2961        &mut self,
2962        src_ty: TypeFixedLengthListIndex,
2963        src: &Source<'_>,
2964        dst_ty: &InterfaceType,
2965        dst: &Destination,
2966    ) {
2967        let src_ty = &self.types[src_ty];
2968        let dst_ty = match dst_ty {
2969            InterfaceType::FixedLengthList(t) => &self.types[*t],
2970            _ => panic!("expected a fixed size list"),
2971        };
2972
2973        // TODO: subtyping
2974        assert_eq!(src_ty.size, dst_ty.size);
2975
2976        match (&src, &dst) {
2977            // Generate custom code for memory to memory copy
2978            (Source::Memory(src_mem), Destination::Memory(dst_mem)) => {
2979                let src_mem_opts = match &src_mem.opts.data_model {
2980                    DataModel::Gc {} => todo!("CM+GC"),
2981                    DataModel::LinearMemory(opts) => opts,
2982                };
2983                let dst_mem_opts = match &dst_mem.opts.data_model {
2984                    DataModel::Gc {} => todo!("CM+GC"),
2985                    DataModel::LinearMemory(opts) => opts,
2986                };
2987                let src_element_bytes = self.types.size_align(src_mem_opts, &src_ty.element).0;
2988                let dst_element_bytes = self.types.size_align(dst_mem_opts, &dst_ty.element).0;
2989                assert_ne!(src_element_bytes, 0);
2990                assert_ne!(dst_element_bytes, 0);
2991
2992                // because data is stored in-line, we assume that source and destination memory have been validated upstream
2993
2994                self.instruction(LocalGet(src_mem.addr.idx));
2995                if src_mem.offset != 0 {
2996                    self.ptr_uconst(src_mem_opts, src_mem.offset);
2997                    self.ptr_add(src_mem_opts);
2998                }
2999                let cur_src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
3000                self.instruction(LocalGet(dst_mem.addr.idx));
3001                if dst_mem.offset != 0 {
3002                    self.ptr_uconst(dst_mem_opts, dst_mem.offset);
3003                    self.ptr_add(dst_mem_opts);
3004                }
3005                let cur_dst_ptr = self.local_set_new_tmp(dst_mem_opts.ptr());
3006
3007                self.instruction(I32Const(src_ty.size as i32));
3008                let remaining = self.local_set_new_tmp(ValType::I32);
3009
3010                self.instruction(Loop(BlockType::Empty));
3011
3012                // Translate the next element in the list
3013                let element_src = Source::Memory(Memory {
3014                    opts: src_mem.opts,
3015                    offset: 0,
3016                    addr: TempLocal::new(cur_src_ptr.idx, cur_src_ptr.ty),
3017                });
3018                let element_dst = Destination::Memory(Memory {
3019                    opts: dst_mem.opts,
3020                    offset: 0,
3021                    addr: TempLocal::new(cur_dst_ptr.idx, cur_dst_ptr.ty),
3022                });
3023                self.translate(&src_ty.element, &element_src, &dst_ty.element, &element_dst);
3024
3025                // Update the two loop pointers
3026                self.instruction(LocalGet(cur_src_ptr.idx));
3027                self.ptr_uconst(src_mem_opts, src_element_bytes);
3028                self.ptr_add(src_mem_opts);
3029                self.instruction(LocalSet(cur_src_ptr.idx));
3030                self.instruction(LocalGet(cur_dst_ptr.idx));
3031                self.ptr_uconst(dst_mem_opts, dst_element_bytes);
3032                self.ptr_add(dst_mem_opts);
3033                self.instruction(LocalSet(cur_dst_ptr.idx));
3034
3035                // Update the remaining count, falling through to break out if it's zero
3036                // now.
3037                self.instruction(LocalGet(remaining.idx));
3038                self.ptr_iconst(src_mem_opts, -1);
3039                self.ptr_add(src_mem_opts);
3040                self.instruction(LocalTee(remaining.idx));
3041                self.ptr_br_if(src_mem_opts, 0);
3042                self.instruction(End); // end of loop
3043
3044                self.free_temp_local(cur_dst_ptr);
3045                self.free_temp_local(cur_src_ptr);
3046                self.free_temp_local(remaining);
3047                return;
3048            }
3049            // for the non-memory-to-memory case fall back to using generic tuple translation
3050            (_, _) => {
3051                // Assumes that the number of elements are small enough for this unrolling
3052                assert!(
3053                    src_ty.size as usize <= MAX_FLAT_PARAMS
3054                        && dst_ty.size as usize <= MAX_FLAT_PARAMS
3055                );
3056                let srcs =
3057                    src.record_field_srcs(self.types, (0..src_ty.size).map(|_| src_ty.element));
3058                let dsts =
3059                    dst.record_field_dsts(self.types, (0..dst_ty.size).map(|_| dst_ty.element));
3060                for (src, dst) in srcs.zip(dsts) {
3061                    self.translate(&src_ty.element, &src, &dst_ty.element, &dst);
3062                }
3063            }
3064        }
3065    }
3066
3067    fn translate_variant(
3068        &mut self,
3069        src_ty: TypeVariantIndex,
3070        src: &Source<'_>,
3071        dst_ty: &InterfaceType,
3072        dst: &Destination,
3073    ) {
3074        let src_ty = &self.types[src_ty];
3075        let dst_ty = match dst_ty {
3076            InterfaceType::Variant(t) => &self.types[*t],
3077            _ => panic!("expected a variant"),
3078        };
3079
3080        let src_info = variant_info(self.types, src_ty.cases.iter().map(|(_, c)| c.as_ref()));
3081        let dst_info = variant_info(self.types, dst_ty.cases.iter().map(|(_, c)| c.as_ref()));
3082
3083        let iter = src_ty
3084            .cases
3085            .iter()
3086            .enumerate()
3087            .map(|(src_i, (src_case, src_case_ty))| {
3088                let dst_i = dst_ty
3089                    .cases
3090                    .iter()
3091                    .position(|(c, _)| c == src_case)
3092                    .unwrap();
3093                let dst_case_ty = &dst_ty.cases[dst_i];
3094                let src_i = u32::try_from(src_i).unwrap();
3095                let dst_i = u32::try_from(dst_i).unwrap();
3096                VariantCase {
3097                    src_i,
3098                    src_ty: src_case_ty.as_ref(),
3099                    dst_i,
3100                    dst_ty: dst_case_ty.as_ref(),
3101                }
3102            });
3103        self.convert_variant(src, &src_info, dst, &dst_info, iter);
3104    }
3105
3106    fn translate_enum(
3107        &mut self,
3108        src_ty: TypeEnumIndex,
3109        src: &Source<'_>,
3110        dst_ty: &InterfaceType,
3111        dst: &Destination,
3112    ) {
3113        let src_ty = &self.types[src_ty];
3114        let dst_ty = match dst_ty {
3115            InterfaceType::Enum(t) => &self.types[*t],
3116            _ => panic!("expected an option"),
3117        };
3118
3119        debug_assert_eq!(src_ty.info.size, dst_ty.info.size);
3120        debug_assert_eq!(src_ty.names.len(), dst_ty.names.len());
3121        debug_assert!(
3122            src_ty
3123                .names
3124                .iter()
3125                .zip(dst_ty.names.iter())
3126                .all(|(a, b)| a == b)
3127        );
3128
3129        // Get the discriminant.
3130        match src {
3131            Source::Stack(s) => self.stack_get(&s.slice(0..1), ValType::I32),
3132            Source::Memory(mem) => match src_ty.info.size {
3133                DiscriminantSize::Size1 => self.i32_load8u(mem),
3134                DiscriminantSize::Size2 => self.i32_load16u(mem),
3135                DiscriminantSize::Size4 => self.i32_load(mem),
3136            },
3137            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
3138        }
3139        let tmp = self.local_tee_new_tmp(ValType::I32);
3140
3141        // Assert that the discriminant is valid.
3142        self.instruction(I32Const(i32::try_from(src_ty.names.len()).unwrap()));
3143        self.instruction(I32GeU);
3144        self.instruction(If(BlockType::Empty));
3145        self.trap(Trap::InvalidDiscriminant);
3146        self.instruction(End);
3147
3148        // Save the discriminant to the destination.
3149        match dst {
3150            Destination::Stack(stack, _) => {
3151                self.local_get_tmp(&tmp);
3152                self.stack_set(&stack[..1], ValType::I32)
3153            }
3154            Destination::Memory(mem) => {
3155                self.push_dst_addr(dst);
3156                self.local_get_tmp(&tmp);
3157                match dst_ty.info.size {
3158                    DiscriminantSize::Size1 => self.i32_store8(mem),
3159                    DiscriminantSize::Size2 => self.i32_store16(mem),
3160                    DiscriminantSize::Size4 => self.i32_store(mem),
3161                }
3162            }
3163            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3164        }
3165        self.free_temp_local(tmp);
3166    }
3167
3168    fn translate_option(
3169        &mut self,
3170        src_ty: TypeOptionIndex,
3171        src: &Source<'_>,
3172        dst_ty: &InterfaceType,
3173        dst: &Destination,
3174    ) {
3175        let src_ty = &self.types[src_ty].ty;
3176        let dst_ty = match dst_ty {
3177            InterfaceType::Option(t) => &self.types[*t].ty,
3178            _ => panic!("expected an option"),
3179        };
3180        let src_ty = Some(src_ty);
3181        let dst_ty = Some(dst_ty);
3182
3183        let src_info = variant_info(self.types, [None, src_ty]);
3184        let dst_info = variant_info(self.types, [None, dst_ty]);
3185
3186        self.convert_variant(
3187            src,
3188            &src_info,
3189            dst,
3190            &dst_info,
3191            [
3192                VariantCase {
3193                    src_i: 0,
3194                    dst_i: 0,
3195                    src_ty: None,
3196                    dst_ty: None,
3197                },
3198                VariantCase {
3199                    src_i: 1,
3200                    dst_i: 1,
3201                    src_ty,
3202                    dst_ty,
3203                },
3204            ]
3205            .into_iter(),
3206        );
3207    }
3208
3209    fn translate_result(
3210        &mut self,
3211        src_ty: TypeResultIndex,
3212        src: &Source<'_>,
3213        dst_ty: &InterfaceType,
3214        dst: &Destination,
3215    ) {
3216        let src_ty = &self.types[src_ty];
3217        let dst_ty = match dst_ty {
3218            InterfaceType::Result(t) => &self.types[*t],
3219            _ => panic!("expected a result"),
3220        };
3221
3222        let src_info = variant_info(self.types, [src_ty.ok.as_ref(), src_ty.err.as_ref()]);
3223        let dst_info = variant_info(self.types, [dst_ty.ok.as_ref(), dst_ty.err.as_ref()]);
3224
3225        self.convert_variant(
3226            src,
3227            &src_info,
3228            dst,
3229            &dst_info,
3230            [
3231                VariantCase {
3232                    src_i: 0,
3233                    dst_i: 0,
3234                    src_ty: src_ty.ok.as_ref(),
3235                    dst_ty: dst_ty.ok.as_ref(),
3236                },
3237                VariantCase {
3238                    src_i: 1,
3239                    dst_i: 1,
3240                    src_ty: src_ty.err.as_ref(),
3241                    dst_ty: dst_ty.err.as_ref(),
3242                },
3243            ]
3244            .into_iter(),
3245        );
3246    }
3247
3248    fn convert_variant<'c>(
3249        &mut self,
3250        src: &Source<'_>,
3251        src_info: &VariantInfo,
3252        dst: &Destination,
3253        dst_info: &VariantInfo,
3254        src_cases: impl ExactSizeIterator<Item = VariantCase<'c>>,
3255    ) {
3256        // The outermost block is special since it has the result type of the
3257        // translation here. That will depend on the `dst`.
3258        let outer_block_ty = match dst {
3259            Destination::Stack(dst_flat, _) => match dst_flat.len() {
3260                0 => BlockType::Empty,
3261                1 => BlockType::Result(dst_flat[0]),
3262                _ => {
3263                    let ty = self.module.core_types.function(&[], &dst_flat);
3264                    BlockType::FunctionType(ty)
3265                }
3266            },
3267            Destination::Memory(_) => BlockType::Empty,
3268            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3269        };
3270        self.instruction(Block(outer_block_ty));
3271
3272        // After the outermost block generate a new block for each of the
3273        // remaining cases.
3274        let src_cases_len = src_cases.len();
3275        for _ in 0..src_cases_len - 1 {
3276            self.instruction(Block(BlockType::Empty));
3277        }
3278
3279        // Generate a block for an invalid variant discriminant
3280        self.instruction(Block(BlockType::Empty));
3281
3282        // And generate one final block that we'll be jumping out of with the
3283        // `br_table`
3284        self.instruction(Block(BlockType::Empty));
3285
3286        // Load the discriminant
3287        match src {
3288            Source::Stack(s) => self.stack_get(&s.slice(0..1), ValType::I32),
3289            Source::Memory(mem) => match src_info.size {
3290                DiscriminantSize::Size1 => self.i32_load8u(mem),
3291                DiscriminantSize::Size2 => self.i32_load16u(mem),
3292                DiscriminantSize::Size4 => self.i32_load(mem),
3293            },
3294            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
3295        }
3296
3297        // Generate the `br_table` for the discriminant. Each case has an
3298        // offset of 1 to skip the trapping block.
3299        let mut targets = Vec::new();
3300        for i in 0..src_cases_len {
3301            targets.push((i + 1) as u32);
3302        }
3303        self.instruction(BrTable(targets[..].into(), 0));
3304        self.instruction(End); // end the `br_table` block
3305
3306        self.trap(Trap::InvalidDiscriminant);
3307        self.instruction(End); // end the "invalid discriminant" block
3308
3309        // Translate each case individually within its own block. Note that the
3310        // iteration order here places the first case in the innermost block
3311        // and the last case in the outermost block. This matches the order
3312        // of the jump targets in the `br_table` instruction.
3313        let src_cases_len = u32::try_from(src_cases_len).unwrap();
3314        for case in src_cases {
3315            let VariantCase {
3316                src_i,
3317                src_ty,
3318                dst_i,
3319                dst_ty,
3320            } = case;
3321
3322            // Translate the discriminant here, noting that `dst_i` may be
3323            // different than `src_i`.
3324            self.push_dst_addr(dst);
3325            self.instruction(I32Const(dst_i as i32));
3326            match dst {
3327                Destination::Stack(stack, _) => self.stack_set(&stack[..1], ValType::I32),
3328                Destination::Memory(mem) => match dst_info.size {
3329                    DiscriminantSize::Size1 => self.i32_store8(mem),
3330                    DiscriminantSize::Size2 => self.i32_store16(mem),
3331                    DiscriminantSize::Size4 => self.i32_store(mem),
3332                },
3333                Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3334            }
3335
3336            let src_payload = src.payload_src(self.types, src_info, src_ty);
3337            let dst_payload = dst.payload_dst(self.types, dst_info, dst_ty);
3338
3339            // Translate the payload of this case using the various types from
3340            // the dst/src.
3341            match (src_ty, dst_ty) {
3342                (Some(src_ty), Some(dst_ty)) => {
3343                    self.translate(src_ty, &src_payload, dst_ty, &dst_payload);
3344                }
3345                (None, None) => {}
3346                _ => unimplemented!(),
3347            }
3348
3349            // If the results of this translation were placed on the stack then
3350            // the stack values may need to be padded with more zeros due to
3351            // this particular case being possibly smaller than the entire
3352            // variant. That's handled here by pushing remaining zeros after
3353            // accounting for the discriminant pushed as well as the results of
3354            // this individual payload.
3355            if let Destination::Stack(payload_results, _) = dst_payload {
3356                if let Destination::Stack(dst_results, _) = dst {
3357                    let remaining = &dst_results[1..][payload_results.len()..];
3358                    for ty in remaining {
3359                        match ty {
3360                            ValType::I32 => self.instruction(I32Const(0)),
3361                            ValType::I64 => self.instruction(I64Const(0)),
3362                            ValType::F32 => self.instruction(F32Const(0.0.into())),
3363                            ValType::F64 => self.instruction(F64Const(0.0.into())),
3364                            _ => unreachable!(),
3365                        }
3366                    }
3367                }
3368            }
3369
3370            // Branch to the outermost block. Note that this isn't needed for
3371            // the outermost case since it simply falls through.
3372            if src_i != src_cases_len - 1 {
3373                self.instruction(Br(src_cases_len - src_i - 1));
3374            }
3375            self.instruction(End); // end this case's block
3376        }
3377    }
3378
3379    fn translate_future(
3380        &mut self,
3381        src_ty: TypeFutureTableIndex,
3382        src: &Source<'_>,
3383        dst_ty: &InterfaceType,
3384        dst: &Destination,
3385    ) {
3386        let dst_ty = match dst_ty {
3387            InterfaceType::Future(t) => *t,
3388            _ => panic!("expected a `Future`"),
3389        };
3390        let transfer = self.module.import_future_transfer();
3391        self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3392    }
3393
3394    fn translate_stream(
3395        &mut self,
3396        src_ty: TypeStreamTableIndex,
3397        src: &Source<'_>,
3398        dst_ty: &InterfaceType,
3399        dst: &Destination,
3400    ) {
3401        let dst_ty = match dst_ty {
3402            InterfaceType::Stream(t) => *t,
3403            _ => panic!("expected a `Stream`"),
3404        };
3405        let transfer = self.module.import_stream_transfer();
3406        self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3407    }
3408
3409    fn translate_error_context(
3410        &mut self,
3411        src_ty: TypeComponentLocalErrorContextTableIndex,
3412        src: &Source<'_>,
3413        dst_ty: &InterfaceType,
3414        dst: &Destination,
3415    ) {
3416        let dst_ty = match dst_ty {
3417            InterfaceType::ErrorContext(t) => *t,
3418            _ => panic!("expected an `ErrorContext`"),
3419        };
3420        let transfer = self.module.import_error_context_transfer();
3421        self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3422    }
3423
3424    fn translate_own(
3425        &mut self,
3426        src_ty: TypeResourceTableIndex,
3427        src: &Source<'_>,
3428        dst_ty: &InterfaceType,
3429        dst: &Destination,
3430    ) {
3431        let dst_ty = match dst_ty {
3432            InterfaceType::Own(t) => *t,
3433            _ => panic!("expected an `Own`"),
3434        };
3435        let transfer = self.module.import_resource_transfer_own();
3436        self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3437    }
3438
3439    fn translate_borrow(
3440        &mut self,
3441        src_ty: TypeResourceTableIndex,
3442        src: &Source<'_>,
3443        dst_ty: &InterfaceType,
3444        dst: &Destination,
3445    ) {
3446        let dst_ty = match dst_ty {
3447            InterfaceType::Borrow(t) => *t,
3448            _ => panic!("expected an `Borrow`"),
3449        };
3450
3451        let transfer = self.module.import_resource_transfer_borrow();
3452        self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3453    }
3454
3455    /// Translates the index `src`, which resides in the table `src_ty`, into
3456    /// and index within `dst_ty` and is stored at `dst`.
3457    ///
3458    /// Actual translation of the index happens in a wasmtime libcall, which a
3459    /// cranelift-generated trampoline to satisfy this import will call. The
3460    /// `transfer` function is an imported function which takes the src, src_ty,
3461    /// and dst_ty, and returns the dst index.
3462    fn translate_handle(
3463        &mut self,
3464        src_ty: u32,
3465        src: &Source<'_>,
3466        dst_ty: u32,
3467        dst: &Destination,
3468        transfer: FuncIndex,
3469    ) {
3470        self.push_dst_addr(dst);
3471        match src {
3472            Source::Memory(mem) => self.i32_load(mem),
3473            Source::Stack(stack) => self.stack_get(stack, ValType::I32),
3474            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
3475        }
3476        self.instruction(I32Const(src_ty as i32));
3477        self.instruction(I32Const(dst_ty as i32));
3478        self.instruction(Call(transfer.as_u32()));
3479        match dst {
3480            Destination::Memory(mem) => self.i32_store(mem),
3481            Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
3482            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3483        }
3484    }
3485
3486    /// Loads the `may_leave` flag for the given instance, traps with `trap` if
3487    /// it is not set, and returns a temporary local holding the loaded value
3488    /// so that it can later be restored with `restore_may_leave` without
3489    /// reloading the global.
3490    ///
3491    /// The `may_leave` flag is a boolean (0 or 1) so no masking is required.
3492    fn trap_if_not_may_leave(&mut self, flags_global: GlobalIndex, trap: Trap) -> TempLocal {
3493        self.instruction(Block(BlockType::Empty));
3494        self.instruction(GlobalGet(flags_global.as_u32()));
3495        // Save the flag's value (known to be `true` whenever the trap below is
3496        // not taken) into a temporary for later restoration.
3497        let saved = self.local_tee_new_tmp(ValType::I32);
3498        self.instruction(BrIf(0));
3499        self.trap(trap);
3500        self.instruction(End);
3501        saved
3502    }
3503
3504    /// Saves the current value of the `may_leave` flag into a fresh temporary
3505    /// local (returned) and then clears the flag to `false`.
3506    fn clear_may_leave(&mut self, flags_global: GlobalIndex) -> TempLocal {
3507        self.instruction(GlobalGet(flags_global.as_u32()));
3508        let saved = self.local_set_new_tmp(ValType::I32);
3509        self.set_may_leave_false(flags_global);
3510        saved
3511    }
3512
3513    /// Sets the `may_leave` flag to `false` by storing a constant `0`.
3514    ///
3515    /// Since there is only a single flag there is no need to reload the global
3516    /// and mask: storing `0` is sufficient.
3517    fn set_may_leave_false(&mut self, flags_global: GlobalIndex) {
3518        self.instruction(I32Const(0));
3519        self.instruction(GlobalSet(flags_global.as_u32()));
3520    }
3521
3522    /// Restores the `may_leave` flag to the value previously saved in `saved`
3523    /// (via `clear_may_leave` or `trap_if_not_may_leave`) and frees the
3524    /// temporary local.
3525    ///
3526    /// Storing the previously-loaded value (rather than reloading the global
3527    /// and or-ing in the flag bit) makes it clear in the generated CLIF that
3528    /// the same value that was there before is being written back. Combined
3529    /// with the constant `0` store in `set_may_leave_false`, this lets a future
3530    /// dead-store-elimination pass remove the clear-to-`false` store which will
3531    /// then allow our idempotent-store elimination to remove this restore for
3532    /// adapters whose body never touches the flag (e.g. simple inlined
3533    /// callees).
3534    fn restore_may_leave(&mut self, flags_global: GlobalIndex, saved: TempLocal) {
3535        self.instruction(LocalGet(saved.idx));
3536        self.instruction(GlobalSet(flags_global.as_u32()));
3537        self.free_temp_local(saved);
3538    }
3539
3540    fn assert_aligned(&mut self, ty: &InterfaceType, mem: &Memory) {
3541        let mem_opts = mem.mem_opts();
3542        if !self.module.tunables.debug_adapter_modules {
3543            return;
3544        }
3545        let align = self.types.align(mem_opts, ty);
3546        if align == 1 {
3547            return;
3548        }
3549        assert!(align.is_power_of_two());
3550        self.instruction(LocalGet(mem.addr.idx));
3551        self.ptr_uconst(mem_opts, mem.offset);
3552        self.ptr_add(mem_opts);
3553        self.ptr_uconst(mem_opts, align - 1);
3554        self.ptr_and(mem_opts);
3555        self.ptr_if(mem_opts, BlockType::Empty);
3556        self.trap(Trap::DebugAssertPointerAligned);
3557        self.instruction(End);
3558    }
3559
3560    /// Helper to invoke the guest's `realloc` function with a statically known
3561    /// `abi`.
3562    ///
3563    /// This will internally validate the return value is properly aligned and
3564    /// additionally within bounds of memory.
3565    fn malloc_abi<'c>(
3566        &mut self,
3567        opts: &'c Options,
3568        abi: &CanonicalAbiInfo,
3569        oob_trap: Trap,
3570    ) -> Memory<'c> {
3571        match &opts.data_model {
3572            DataModel::Gc {} => todo!("CM+GC"),
3573            DataModel::LinearMemory(mem_opts) => {
3574                let (size, align) = mem_opts.sizealign(abi);
3575                let size = AllocSize::Const(size);
3576                self.malloc(opts, size, align, oob_trap)
3577            }
3578        }
3579    }
3580
3581    /// Helper to invoke the guest's `realloc` function with the specified
3582    /// `size` and `align`.
3583    ///
3584    /// This will internally validate the return value is properly aligned and
3585    /// additionally within bounds of memory.
3586    fn malloc<'c>(
3587        &mut self,
3588        opts: &'c Options,
3589        size: AllocSize,
3590        align: u32,
3591        oob_trap: Trap,
3592    ) -> Memory<'c> {
3593        match &opts.data_model {
3594            DataModel::Gc {} => todo!("CM+GC"),
3595            DataModel::LinearMemory(mem_opts) => {
3596                let realloc = mem_opts.realloc.unwrap();
3597                self.ptr_uconst(mem_opts, 0);
3598                self.ptr_uconst(mem_opts, 0);
3599                self.ptr_uconst(mem_opts, align);
3600                self.alloc_size(mem_opts, &size);
3601                self.call_realloc(realloc);
3602                let addr = self.local_set_new_tmp(mem_opts.ptr());
3603                self.memory_operand(opts, addr, size, align, oob_trap)
3604            }
3605        }
3606    }
3607
3608    /// Helper to invoke the guest's `realloc` function with the specified
3609    /// arguments.
3610    ///
3611    /// This will internally validate the return value is properly aligned and
3612    /// additionally within bounds of memory.
3613    fn realloc(
3614        &mut self,
3615        opts: &Options,
3616        ptr: &TempLocal,
3617        prev_size: AllocSize,
3618        size: AllocSize,
3619        align: u32,
3620        oob_trap: Trap,
3621    ) {
3622        match &opts.data_model {
3623            DataModel::Gc {} => todo!("CM+GC"),
3624            DataModel::LinearMemory(mem_opts) => {
3625                let realloc = mem_opts.realloc.unwrap();
3626                self.instruction(LocalGet(ptr.idx));
3627                self.alloc_size(mem_opts, &prev_size);
3628                self.ptr_uconst(mem_opts, align);
3629                self.alloc_size(mem_opts, &size);
3630                self.call_realloc(realloc);
3631                self.instruction(LocalSet(ptr.idx));
3632                self.validate_guest_pointer(opts, &ptr, &size, align, oob_trap)
3633            }
3634        }
3635    }
3636
3637    /// Convenience helper aruond `memory_operand` which takes a
3638    /// statically known `abi` of the allocation.
3639    fn memory_operand_abi<'c>(
3640        &mut self,
3641        opts: &'c Options,
3642        addr: TempLocal,
3643        abi: &CanonicalAbiInfo,
3644        oob_trap: Trap,
3645    ) -> Memory<'c> {
3646        match &opts.data_model {
3647            DataModel::Gc {} => todo!("CM+GC"),
3648            DataModel::LinearMemory(mem_opts) => {
3649                let (size, align) = mem_opts.sizealign(abi);
3650                self.memory_operand(opts, addr, AllocSize::Const(size), align, oob_trap)
3651            }
3652        }
3653    }
3654
3655    /// Creates a `Memory` operand from the parts provided after validating
3656    /// that everything is in-bounds according to `validate_guest_pointer`.
3657    fn memory_operand<'c>(
3658        &mut self,
3659        opts: &'c Options,
3660        addr: TempLocal,
3661        size: AllocSize,
3662        align: u32,
3663        oob_trap: Trap,
3664    ) -> Memory<'c> {
3665        self.validate_guest_pointer(opts, &addr, &size, align, oob_trap);
3666        Memory {
3667            addr,
3668            opts,
3669            offset: 0,
3670        }
3671    }
3672
3673    /// Validates that the guest pointer `addr` is in-bounds for `size` amount
3674    /// of bytes.
3675    ///
3676    /// Additionally validates that `addr` is aligned to `align`.
3677    ///
3678    /// Traps with `oob_trap` if the `addr` value is not in-bounds for the
3679    /// linear memory specified by `opts`.
3680    fn validate_guest_pointer(
3681        &mut self,
3682        opts: &Options,
3683        addr: &TempLocal,
3684        size: &AllocSize,
3685        align: u32,
3686        oob_trap: Trap,
3687    ) {
3688        let mem_opts = match &opts.data_model {
3689            DataModel::Gc {} => todo!("CM+GC"),
3690            DataModel::LinearMemory(mem_opts) => mem_opts,
3691        };
3692
3693        // If the alignment is 1 then everything is trivially aligned and the
3694        // check can be omitted.
3695        if align != 1 {
3696            self.instruction(LocalGet(addr.idx));
3697            assert!(align.is_power_of_two());
3698            self.ptr_uconst(mem_opts, align - 1);
3699            self.ptr_and(mem_opts);
3700            self.ptr_if(mem_opts, BlockType::Empty);
3701            self.trap(Trap::UnalignedPointer);
3702            self.instruction(End);
3703        }
3704
3705        let extend_to_64 = |me: &mut Self| {
3706            if !mem_opts.memory64() {
3707                me.instruction(I64ExtendI32U);
3708            }
3709        };
3710
3711        self.instruction(Block(BlockType::Empty));
3712        self.instruction(Block(BlockType::Empty));
3713        let (memory, ty) = mem_opts.memory.unwrap();
3714
3715        // Calculate the full byte size of memory with `memory.size`. Note that
3716        // arithmetic here is done always in 64-bits to accommodate 4G memories.
3717        // Additionally it's assumed that 64-bit memories never fill up
3718        // entirely.
3719        self.instruction(MemorySize(memory.as_u32()));
3720        extend_to_64(self);
3721        self.instruction(I64Const(ty.page_size_log2.into()));
3722        self.instruction(I64Shl);
3723
3724        // Calculate the end address of the string. This is done by adding the
3725        // base pointer to the byte length. For 32-bit memories there's no need
3726        // to check for overflow since everything is extended to 64-bit, but for
3727        // 64-bit memories overflow is checked.
3728        self.instruction(LocalGet(addr.idx));
3729        extend_to_64(self);
3730        self.alloc_size(mem_opts, size);
3731        extend_to_64(self);
3732        self.instruction(I64Add);
3733        if mem_opts.memory64() {
3734            let tmp = self.local_tee_new_tmp(ValType::I64);
3735            self.instruction(LocalGet(addr.idx));
3736            self.ptr_lt_u(mem_opts);
3737            self.instruction(BrIf(0));
3738            self.instruction(LocalGet(tmp.idx));
3739            self.free_temp_local(tmp);
3740        }
3741
3742        // If the byte size of memory is greater than the final address of the
3743        // string then the string is invalid. Note that if it's precisely equal
3744        // then that's ok.
3745        self.instruction(I64GeU);
3746        self.instruction(BrIf(1));
3747
3748        self.instruction(End);
3749        self.trap(oob_trap);
3750        self.instruction(End);
3751    }
3752
3753    /// Generates a new local in this function of the `ty` specified,
3754    /// initializing it with the top value on the current wasm stack.
3755    ///
3756    /// The returned `TempLocal` must be freed after it is finished with
3757    /// `free_temp_local`.
3758    fn local_tee_new_tmp(&mut self, ty: ValType) -> TempLocal {
3759        self.gen_temp_local(ty, LocalTee)
3760    }
3761
3762    /// Same as `local_tee_new_tmp` but initializes the local with `LocalSet`
3763    /// instead of `LocalTee`.
3764    fn local_set_new_tmp(&mut self, ty: ValType) -> TempLocal {
3765        self.gen_temp_local(ty, LocalSet)
3766    }
3767
3768    fn local_get_tmp(&mut self, local: &TempLocal) {
3769        self.instruction(LocalGet(local.idx));
3770    }
3771
3772    fn gen_temp_local(&mut self, ty: ValType, insn: fn(u32) -> Instruction<'static>) -> TempLocal {
3773        // First check to see if any locals are available in this function which
3774        // were previously generated but are no longer in use.
3775        if let Some(idx) = self.free_locals.get_mut(&ty).and_then(|v| v.pop()) {
3776            self.instruction(insn(idx));
3777            return TempLocal {
3778                ty,
3779                idx,
3780                needs_free: true,
3781            };
3782        }
3783
3784        // Failing that generate a fresh new local.
3785        let locals = &mut self.module.funcs[self.result].locals;
3786        match locals.last_mut() {
3787            Some((cnt, prev_ty)) if ty == *prev_ty => *cnt += 1,
3788            _ => locals.push((1, ty)),
3789        }
3790        self.nlocals += 1;
3791        let idx = self.nlocals - 1;
3792        self.instruction(insn(idx));
3793        TempLocal {
3794            ty,
3795            idx,
3796            needs_free: true,
3797        }
3798    }
3799
3800    /// Used to release a `TempLocal` from a particular lexical scope to allow
3801    /// its possible reuse in later scopes.
3802    fn free_temp_local(&mut self, mut local: TempLocal) {
3803        assert!(local.needs_free);
3804        self.free_locals
3805            .entry(local.ty)
3806            .or_insert(Vec::new())
3807            .push(local.idx);
3808        local.needs_free = false;
3809    }
3810
3811    /// Reads all of the current task's `context.{get,set}` slots into fresh
3812    /// temporary locals which can later be handed to `restore_context`.
3813    fn save_context(&mut self) -> Vec<TempLocal> {
3814        if !self.module.tunables.concurrency_support {
3815            return Vec::new();
3816        }
3817        let mut saved = Vec::new();
3818        for slot in 0..NUM_COMPONENT_CONTEXT_SLOTS {
3819            let get = self.module.import_context_get(slot);
3820            self.instruction(Call(get.as_u32()));
3821            saved.push(self.local_set_new_tmp(ValType::I32));
3822        }
3823        saved
3824    }
3825
3826    /// Stores zero into all of the current task's `context.{get,set}` slots.
3827    fn clear_context(&mut self) {
3828        if !self.module.tunables.concurrency_support {
3829            return;
3830        }
3831        for slot in 0..NUM_COMPONENT_CONTEXT_SLOTS {
3832            let set = self.module.import_context_set(slot);
3833            self.instruction(I32Const(0));
3834            self.instruction(Call(set.as_u32()));
3835        }
3836    }
3837
3838    /// Stores the slot values previously read by `save_context` back into the
3839    /// current task's `context.{get,set}` slots.
3840    fn restore_context(&mut self, saved: Vec<TempLocal>) {
3841        for (slot, local) in saved.into_iter().enumerate() {
3842            let set = self.module.import_context_set(slot);
3843            self.instruction(LocalGet(local.idx));
3844            self.instruction(Call(set.as_u32()));
3845            self.free_temp_local(local);
3846        }
3847    }
3848
3849    /// Emits a call to a guest `realloc` function.
3850    ///
3851    /// Note that this has special handling of the current task's
3852    /// `context.{get,set}` slots, namely they're saved/restored around this
3853    /// call and zero'd out during the call.
3854    fn call_realloc(&mut self, realloc: FuncIndex) {
3855        let saved = self.save_context();
3856        self.clear_context();
3857        self.instruction(Call(realloc.as_u32()));
3858        self.restore_context(saved);
3859    }
3860
3861    fn instruction(&mut self, instr: Instruction) {
3862        instr.encode(&mut self.code);
3863    }
3864
3865    fn trap(&mut self, trap: Trap) {
3866        let trap_func = self.module.import_trap(trap);
3867        self.instruction(Call(trap_func.as_u32()));
3868        self.instruction(Unreachable);
3869    }
3870
3871    /// Emits the prologue of an exception barrier wrapping the body of a
3872    /// function.
3873    ///
3874    /// An adapter is the boundary between two components, and the
3875    /// component model's canonical ABI specifies that an exception
3876    /// which propagates out of a component without being caught
3877    /// becomes a trap rather than unwinding into the other
3878    /// component. To implement that, the entire body of an adapter
3879    /// function is wrapped in a `try_table` whose `catch_all` clause
3880    /// traps. This catches exceptions thrown not only by the callee
3881    /// itself but also by any other guest functions the adapter
3882    /// invokes (e.g. `realloc`).
3883    ///
3884    /// The generated structure, completed by `exit_exception_barrier`,
3885    /// is:
3886    ///
3887    /// ```wasm
3888    /// block (result ...)        ;; carries results past the handler
3889    ///   block                   ;; catch_all landing pad
3890    ///     try_table (result ...) (catch_all 0)
3891    ///       ;; ... body ...
3892    ///     end
3893    ///     br 1                  ;; done; carry results past the handler
3894    ///   end
3895    ///   ;; an exception was caught: raise a trap
3896    ///   unreachable
3897    /// end
3898    /// ```
3899    ///
3900    /// This is only done when the exceptions proposal is enabled.
3901    fn enter_exception_barrier(&mut self, results: &[ValType]) {
3902        if !self.module.features.exceptions() {
3903            return;
3904        }
3905        let block_ty = match results.len() {
3906            0 => BlockType::Empty,
3907            1 => BlockType::Result(results[0]),
3908            _ => BlockType::FunctionType(self.module.core_types.function(&[], results)),
3909        };
3910        // Outer block: carries the body's results past the handler.
3911        self.instruction(Block(block_ty));
3912        // Inner block: the landing pad targeted by the `catch_all` clause.
3913        self.instruction(Block(BlockType::Empty));
3914        self.instruction(TryTable(block_ty, vec![Catch::All { label: 0 }].into()));
3915    }
3916
3917    /// Emits the epilogue of an exception barrier started with
3918    /// `enter_exception_barrier`: the body's results jump past the
3919    /// `catch_all` landing pad, which turns a caught exception into a
3920    /// trap.
3921    fn exit_exception_barrier(&mut self) {
3922        if !self.module.features.exceptions() {
3923            return;
3924        }
3925        // End of the `try_table`.
3926        self.instruction(End);
3927        // Normal completion: jump over the handler, carrying the results.
3928        self.instruction(Br(1));
3929        // End of the inner block: the `catch_all` landing pad.
3930        self.instruction(End);
3931        self.trap(Trap::UncaughtException);
3932        // End of the outer block; the body's results flow out.
3933        self.instruction(End);
3934    }
3935
3936    /// Flushes out the current `code` instructions into the destination
3937    /// function.
3938    ///
3939    /// This is a noop if no instructions have been encoded yet.
3940    fn flush_code(&mut self) {
3941        if self.code.is_empty() {
3942            return;
3943        }
3944        self.module.funcs[self.result]
3945            .body
3946            .push(Body::Raw(mem::take(&mut self.code)));
3947    }
3948
3949    fn finish(mut self) {
3950        // Append the final `end` instruction which all functions require, and
3951        // then empty out the temporary buffer in `Compiler`.
3952        self.instruction(End);
3953        self.flush_code();
3954
3955        // Flag the function as "done" which helps with an assert later on in
3956        // emission that everything was eventually finished.
3957        self.module.funcs[self.result].filled_in = true;
3958    }
3959
3960    /// Fetches the value contained with the local specified by `stack` and
3961    /// converts it to `dst_ty`.
3962    ///
3963    /// This is only intended for use in primitive operations where `stack` is
3964    /// guaranteed to have only one local. The type of the local on the stack is
3965    /// then converted to `dst_ty` appropriately. Note that the types may be
3966    /// different due to the "flattening" of variant types.
3967    fn stack_get(&mut self, stack: &Stack<'_>, dst_ty: ValType) {
3968        assert_eq!(stack.locals.len(), 1);
3969        let (idx, src_ty) = stack.locals[0];
3970        self.instruction(LocalGet(idx));
3971        match (src_ty, dst_ty) {
3972            (ValType::I32, ValType::I32)
3973            | (ValType::I64, ValType::I64)
3974            | (ValType::F32, ValType::F32)
3975            | (ValType::F64, ValType::F64) => {}
3976
3977            (ValType::I32, ValType::F32) => self.instruction(F32ReinterpretI32),
3978            (ValType::I64, ValType::I32) => {
3979                self.assert_i64_upper_bits_not_set(idx);
3980                self.instruction(I32WrapI64);
3981            }
3982            (ValType::I64, ValType::F64) => self.instruction(F64ReinterpretI64),
3983            (ValType::I64, ValType::F32) => {
3984                self.assert_i64_upper_bits_not_set(idx);
3985                self.instruction(I32WrapI64);
3986                self.instruction(F32ReinterpretI32);
3987            }
3988
3989            // should not be possible given the `join` function for variants
3990            (ValType::I32, ValType::I64)
3991            | (ValType::I32, ValType::F64)
3992            | (ValType::F32, ValType::I32)
3993            | (ValType::F32, ValType::I64)
3994            | (ValType::F32, ValType::F64)
3995            | (ValType::F64, ValType::I32)
3996            | (ValType::F64, ValType::I64)
3997            | (ValType::F64, ValType::F32)
3998
3999            // not used in the component model
4000            | (ValType::Ref(_), _)
4001            | (_, ValType::Ref(_))
4002            | (ValType::V128, _)
4003            | (_, ValType::V128) => {
4004                panic!("cannot get {dst_ty:?} from {src_ty:?} local");
4005            }
4006        }
4007    }
4008
4009    fn assert_i64_upper_bits_not_set(&mut self, local: u32) {
4010        if !self.module.tunables.debug_adapter_modules {
4011            return;
4012        }
4013        self.instruction(LocalGet(local));
4014        self.instruction(I64Const(32));
4015        self.instruction(I64ShrU);
4016        self.instruction(I32WrapI64);
4017        self.instruction(If(BlockType::Empty));
4018        self.trap(Trap::DebugAssertUpperBitsUnset);
4019        self.instruction(End);
4020    }
4021
4022    /// Converts the top value on the WebAssembly stack which has type
4023    /// `src_ty` to `dst_tys[0]`.
4024    ///
4025    /// This is only intended for conversion of primitives where the `dst_tys`
4026    /// list is known to be of length 1.
4027    fn stack_set(&mut self, dst_tys: &[ValType], src_ty: ValType) {
4028        assert_eq!(dst_tys.len(), 1);
4029        let dst_ty = dst_tys[0];
4030        match (src_ty, dst_ty) {
4031            (ValType::I32, ValType::I32)
4032            | (ValType::I64, ValType::I64)
4033            | (ValType::F32, ValType::F32)
4034            | (ValType::F64, ValType::F64) => {}
4035
4036            (ValType::F32, ValType::I32) => self.instruction(I32ReinterpretF32),
4037            (ValType::I32, ValType::I64) => self.instruction(I64ExtendI32U),
4038            (ValType::F64, ValType::I64) => self.instruction(I64ReinterpretF64),
4039            (ValType::F32, ValType::I64) => {
4040                self.instruction(I32ReinterpretF32);
4041                self.instruction(I64ExtendI32U);
4042            }
4043
4044            // should not be possible given the `join` function for variants
4045            (ValType::I64, ValType::I32)
4046            | (ValType::F64, ValType::I32)
4047            | (ValType::I32, ValType::F32)
4048            | (ValType::I64, ValType::F32)
4049            | (ValType::F64, ValType::F32)
4050            | (ValType::I32, ValType::F64)
4051            | (ValType::I64, ValType::F64)
4052            | (ValType::F32, ValType::F64)
4053
4054            // not used in the component model
4055            | (ValType::Ref(_), _)
4056            | (_, ValType::Ref(_))
4057            | (ValType::V128, _)
4058            | (_, ValType::V128) => {
4059                panic!("cannot get {dst_ty:?} from {src_ty:?} local");
4060            }
4061        }
4062    }
4063
4064    fn i32_load8u(&mut self, mem: &Memory) {
4065        self.instruction(LocalGet(mem.addr.idx));
4066        self.instruction(I32Load8U(mem.memarg(0)));
4067    }
4068
4069    fn i32_load8s(&mut self, mem: &Memory) {
4070        self.instruction(LocalGet(mem.addr.idx));
4071        self.instruction(I32Load8S(mem.memarg(0)));
4072    }
4073
4074    fn i32_load16u(&mut self, mem: &Memory) {
4075        self.instruction(LocalGet(mem.addr.idx));
4076        self.instruction(I32Load16U(mem.memarg(1)));
4077    }
4078
4079    fn i32_load16s(&mut self, mem: &Memory) {
4080        self.instruction(LocalGet(mem.addr.idx));
4081        self.instruction(I32Load16S(mem.memarg(1)));
4082    }
4083
4084    fn i32_load(&mut self, mem: &Memory) {
4085        self.instruction(LocalGet(mem.addr.idx));
4086        self.instruction(I32Load(mem.memarg(2)));
4087    }
4088
4089    fn i64_load(&mut self, mem: &Memory) {
4090        self.instruction(LocalGet(mem.addr.idx));
4091        self.instruction(I64Load(mem.memarg(3)));
4092    }
4093
4094    fn ptr_load(&mut self, mem: &Memory) {
4095        if mem.mem_opts().memory64() {
4096            self.i64_load(mem);
4097        } else {
4098            self.i32_load(mem);
4099        }
4100    }
4101
4102    fn ptr_add(&mut self, opts: &LinearMemoryOptions) {
4103        if opts.memory64() {
4104            self.instruction(I64Add);
4105        } else {
4106            self.instruction(I32Add);
4107        }
4108    }
4109
4110    fn ptr_sub(&mut self, opts: &LinearMemoryOptions) {
4111        if opts.memory64() {
4112            self.instruction(I64Sub);
4113        } else {
4114            self.instruction(I32Sub);
4115        }
4116    }
4117
4118    fn ptr_mul(&mut self, opts: &LinearMemoryOptions) {
4119        if opts.memory64() {
4120            self.instruction(I64Mul);
4121        } else {
4122            self.instruction(I32Mul);
4123        }
4124    }
4125
4126    fn ptr_gt_u(&mut self, opts: &LinearMemoryOptions) {
4127        if opts.memory64() {
4128            self.instruction(I64GtU);
4129        } else {
4130            self.instruction(I32GtU);
4131        }
4132    }
4133
4134    fn ptr_lt_u(&mut self, opts: &LinearMemoryOptions) {
4135        if opts.memory64() {
4136            self.instruction(I64LtU);
4137        } else {
4138            self.instruction(I32LtU);
4139        }
4140    }
4141
4142    fn ptr_shl(&mut self, opts: &LinearMemoryOptions) {
4143        if opts.memory64() {
4144            self.instruction(I64Shl);
4145        } else {
4146            self.instruction(I32Shl);
4147        }
4148    }
4149
4150    fn ptr_eqz(&mut self, opts: &LinearMemoryOptions) {
4151        if opts.memory64() {
4152            self.instruction(I64Eqz);
4153        } else {
4154            self.instruction(I32Eqz);
4155        }
4156    }
4157
4158    fn ptr_uconst(&mut self, opts: &LinearMemoryOptions, val: u32) {
4159        if opts.memory64() {
4160            self.instruction(I64Const(val.into()));
4161        } else {
4162            self.instruction(I32Const(val.cast_signed()));
4163        }
4164    }
4165
4166    fn ptr_iconst(&mut self, opts: &LinearMemoryOptions, val: i32) {
4167        if opts.memory64() {
4168            self.instruction(I64Const(val.into()));
4169        } else {
4170            self.instruction(I32Const(val));
4171        }
4172    }
4173
4174    fn ptr_eq(&mut self, opts: &LinearMemoryOptions) {
4175        if opts.memory64() {
4176            self.instruction(I64Eq);
4177        } else {
4178            self.instruction(I32Eq);
4179        }
4180    }
4181
4182    fn ptr_ne(&mut self, opts: &LinearMemoryOptions) {
4183        if opts.memory64() {
4184            self.instruction(I64Ne);
4185        } else {
4186            self.instruction(I32Ne);
4187        }
4188    }
4189
4190    fn ptr_and(&mut self, opts: &LinearMemoryOptions) {
4191        if opts.memory64() {
4192            self.instruction(I64And);
4193        } else {
4194            self.instruction(I32And);
4195        }
4196    }
4197
4198    fn ptr_or(&mut self, opts: &LinearMemoryOptions) {
4199        if opts.memory64() {
4200            self.instruction(I64Or);
4201        } else {
4202            self.instruction(I32Or);
4203        }
4204    }
4205
4206    fn ptr_xor(&mut self, opts: &LinearMemoryOptions) {
4207        if opts.memory64() {
4208            self.instruction(I64Xor);
4209        } else {
4210            self.instruction(I32Xor);
4211        }
4212    }
4213
4214    fn ptr_if(&mut self, opts: &LinearMemoryOptions, ty: BlockType) {
4215        if opts.memory64() {
4216            self.instruction(I64Const(0));
4217            self.instruction(I64Ne);
4218        }
4219        self.instruction(If(ty));
4220    }
4221
4222    fn ptr_br_if(&mut self, opts: &LinearMemoryOptions, depth: u32) {
4223        if opts.memory64() {
4224            self.instruction(I64Const(0));
4225            self.instruction(I64Ne);
4226        }
4227        self.instruction(BrIf(depth));
4228    }
4229
4230    fn f32_load(&mut self, mem: &Memory) {
4231        self.instruction(LocalGet(mem.addr.idx));
4232        self.instruction(F32Load(mem.memarg(2)));
4233    }
4234
4235    fn f64_load(&mut self, mem: &Memory) {
4236        self.instruction(LocalGet(mem.addr.idx));
4237        self.instruction(F64Load(mem.memarg(3)));
4238    }
4239
4240    fn push_dst_addr(&mut self, dst: &Destination) {
4241        if let Destination::Memory(mem) = dst {
4242            self.instruction(LocalGet(mem.addr.idx));
4243        }
4244    }
4245
4246    fn i32_store8(&mut self, mem: &Memory) {
4247        self.instruction(I32Store8(mem.memarg(0)));
4248    }
4249
4250    fn i32_store16(&mut self, mem: &Memory) {
4251        self.instruction(I32Store16(mem.memarg(1)));
4252    }
4253
4254    fn i32_store(&mut self, mem: &Memory) {
4255        self.instruction(I32Store(mem.memarg(2)));
4256    }
4257
4258    fn i64_store(&mut self, mem: &Memory) {
4259        self.instruction(I64Store(mem.memarg(3)));
4260    }
4261
4262    fn ptr_store(&mut self, mem: &Memory) {
4263        if mem.mem_opts().memory64() {
4264            self.i64_store(mem);
4265        } else {
4266            self.i32_store(mem);
4267        }
4268    }
4269
4270    fn f32_store(&mut self, mem: &Memory) {
4271        self.instruction(F32Store(mem.memarg(2)));
4272    }
4273
4274    fn f64_store(&mut self, mem: &Memory) {
4275        self.instruction(F64Store(mem.memarg(3)));
4276    }
4277
4278    /// Push a pointer-typed value for `opts` on the wasm stack representing
4279    /// the `size` passed in.
4280    fn alloc_size(&mut self, opts: &LinearMemoryOptions, size: &AllocSize) {
4281        match size {
4282            AllocSize::Const(size) => self.ptr_uconst(opts, *size),
4283            AllocSize::Local(idx) => self.instruction(LocalGet(*idx)),
4284            AllocSize::DoubleLocal(idx) => {
4285                self.instruction(LocalGet(*idx));
4286                self.ptr_uconst(opts, 1);
4287                self.ptr_shl(opts);
4288            }
4289        }
4290    }
4291}
4292
4293impl<'a> Source<'a> {
4294    /// Given this `Source` returns an iterator over the `Source` for each of
4295    /// the component `fields` specified.
4296    ///
4297    /// This will automatically slice stack-based locals to the appropriate
4298    /// width for each component type and additionally calculate the appropriate
4299    /// offset for each memory-based type.
4300    fn record_field_srcs<'b>(
4301        &'b self,
4302        types: &'b ComponentTypesBuilder,
4303        fields: impl IntoIterator<Item = InterfaceType> + 'b,
4304    ) -> impl Iterator<Item = Source<'a>> + 'b
4305    where
4306        'a: 'b,
4307    {
4308        let mut offset = 0;
4309        fields.into_iter().map(move |ty| match self {
4310            Source::Memory(mem) => {
4311                let mem = next_field_offset(&mut offset, types, &ty, mem);
4312                Source::Memory(mem)
4313            }
4314            Source::Stack(stack) => {
4315                let cnt = types.flat_types(&ty).unwrap().len() as u32;
4316                offset += cnt;
4317                Source::Stack(stack.slice((offset - cnt) as usize..offset as usize))
4318            }
4319            Source::Struct(_) => todo!(),
4320            Source::Array(_) => todo!(),
4321        })
4322    }
4323
4324    /// Returns the corresponding discriminant source and payload source f
4325    fn payload_src(
4326        &self,
4327        types: &ComponentTypesBuilder,
4328        info: &VariantInfo,
4329        case: Option<&InterfaceType>,
4330    ) -> Source<'a> {
4331        match self {
4332            Source::Stack(s) => {
4333                let flat_len = match case {
4334                    Some(case) => types.flat_types(case).unwrap().len(),
4335                    None => 0,
4336                };
4337                Source::Stack(s.slice(1..s.locals.len()).slice(0..flat_len))
4338            }
4339            Source::Memory(mem) => {
4340                let mem = if mem.mem_opts().memory64() {
4341                    mem.bump(info.payload_offset64)
4342                } else {
4343                    mem.bump(info.payload_offset32)
4344                };
4345                Source::Memory(mem)
4346            }
4347            Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
4348        }
4349    }
4350
4351    fn opts(&self) -> &'a Options {
4352        match self {
4353            Source::Stack(s) => s.opts,
4354            Source::Memory(mem) => mem.opts,
4355            Source::Struct(s) => s.opts,
4356            Source::Array(a) => a.opts,
4357        }
4358    }
4359}
4360
4361impl<'a> Destination<'a> {
4362    /// Same as `Source::record_field_srcs` but for destinations.
4363    fn record_field_dsts<'b, I>(
4364        &'b self,
4365        types: &'b ComponentTypesBuilder,
4366        fields: I,
4367    ) -> impl Iterator<Item = Destination<'b>> + use<'b, I>
4368    where
4369        'a: 'b,
4370        I: IntoIterator<Item = InterfaceType> + 'b,
4371    {
4372        let mut offset = 0;
4373        fields.into_iter().map(move |ty| match self {
4374            Destination::Memory(mem) => {
4375                let mem = next_field_offset(&mut offset, types, &ty, mem);
4376                Destination::Memory(mem)
4377            }
4378            Destination::Stack(s, opts) => {
4379                let cnt = types.flat_types(&ty).unwrap().len() as u32;
4380                offset += cnt;
4381                Destination::Stack(&s[(offset - cnt) as usize..offset as usize], opts)
4382            }
4383            Destination::Struct(_) => todo!(),
4384            Destination::Array(_) => todo!(),
4385        })
4386    }
4387
4388    /// Returns the corresponding discriminant source and payload source f
4389    fn payload_dst(
4390        &self,
4391        types: &ComponentTypesBuilder,
4392        info: &VariantInfo,
4393        case: Option<&InterfaceType>,
4394    ) -> Destination<'_> {
4395        match self {
4396            Destination::Stack(s, opts) => {
4397                let flat_len = match case {
4398                    Some(case) => types.flat_types(case).unwrap().len(),
4399                    None => 0,
4400                };
4401                Destination::Stack(&s[1..][..flat_len], opts)
4402            }
4403            Destination::Memory(mem) => {
4404                let mem = if mem.mem_opts().memory64() {
4405                    mem.bump(info.payload_offset64)
4406                } else {
4407                    mem.bump(info.payload_offset32)
4408                };
4409                Destination::Memory(mem)
4410            }
4411            Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
4412        }
4413    }
4414
4415    fn opts(&self) -> &'a Options {
4416        match self {
4417            Destination::Stack(_, opts) => opts,
4418            Destination::Memory(mem) => mem.opts,
4419            Destination::Struct(s) => s.opts,
4420            Destination::Array(a) => a.opts,
4421        }
4422    }
4423}
4424
4425fn next_field_offset<'a>(
4426    offset: &mut u32,
4427    types: &ComponentTypesBuilder,
4428    field: &InterfaceType,
4429    mem: &Memory<'a>,
4430) -> Memory<'a> {
4431    let abi = types.canonical_abi(field);
4432    let offset = if mem.mem_opts().memory64() {
4433        abi.next_field64(offset)
4434    } else {
4435        abi.next_field32(offset)
4436    };
4437    mem.bump(offset)
4438}
4439
4440impl<'a> Memory<'a> {
4441    fn memarg(&self, align: u32) -> MemArg {
4442        MemArg {
4443            offset: u64::from(self.offset),
4444            align,
4445            memory_index: self.mem_opts().memory.unwrap().0.as_u32(),
4446        }
4447    }
4448
4449    fn bump(&self, offset: u32) -> Memory<'a> {
4450        Memory {
4451            opts: self.opts,
4452            addr: TempLocal::new(self.addr.idx, self.addr.ty),
4453            offset: self.offset + offset,
4454        }
4455    }
4456}
4457
4458impl<'a> Stack<'a> {
4459    fn slice(&self, range: Range<usize>) -> Stack<'a> {
4460        Stack {
4461            locals: &self.locals[range],
4462            opts: self.opts,
4463        }
4464    }
4465}
4466
4467struct VariantCase<'a> {
4468    src_i: u32,
4469    src_ty: Option<&'a InterfaceType>,
4470    dst_i: u32,
4471    dst_ty: Option<&'a InterfaceType>,
4472}
4473
4474fn variant_info<'a, I>(types: &ComponentTypesBuilder, cases: I) -> VariantInfo
4475where
4476    I: IntoIterator<Item = Option<&'a InterfaceType>>,
4477    I::IntoIter: ExactSizeIterator,
4478{
4479    VariantInfo::new(
4480        cases
4481            .into_iter()
4482            .map(|ty| ty.map(|ty| types.canonical_abi(ty))),
4483    )
4484    .0
4485}
4486
4487/// State for the iteration loop inside a sequence translation.
4488struct SequenceLoopState {
4489    remaining: TempLocal,
4490    cur_src_ptr: TempLocal,
4491    cur_dst_ptr: TempLocal,
4492}
4493
4494/// Holds all temporaries created by `begin_translate_sequence` so the
4495/// caller can emit a custom loop body before calling
4496/// `end_translate_sequence`.
4497struct SequenceTranslation<'a> {
4498    src_len: TempLocal,
4499    src_mem: Memory<'a>,
4500    dst_mem: Memory<'a>,
4501    src_opts: &'a Options,
4502    dst_opts: &'a Options,
4503    src_mem_opts: &'a LinearMemoryOptions,
4504    dst_mem_opts: &'a LinearMemoryOptions,
4505    loop_state: Option<SequenceLoopState>,
4506}
4507
4508enum AllocSize {
4509    Const(u32),
4510    Local(u32),
4511    DoubleLocal(u32),
4512}
4513
4514struct WasmString<'a> {
4515    ptr: TempLocal,
4516    len: TempLocal,
4517    opts: &'a Options,
4518}
4519
4520struct TempLocal {
4521    idx: u32,
4522    ty: ValType,
4523    needs_free: bool,
4524}
4525
4526impl TempLocal {
4527    fn new(idx: u32, ty: ValType) -> TempLocal {
4528        TempLocal {
4529            idx,
4530            ty,
4531            needs_free: false,
4532        }
4533    }
4534}
4535
4536impl std::ops::Drop for TempLocal {
4537    fn drop(&mut self) {
4538        if self.needs_free {
4539            panic!("temporary local not free'd");
4540        }
4541    }
4542}
4543
4544impl From<FlatType> for ValType {
4545    fn from(ty: FlatType) -> ValType {
4546        match ty {
4547            FlatType::I32 => ValType::I32,
4548            FlatType::I64 => ValType::I64,
4549            FlatType::F32 => ValType::F32,
4550            FlatType::F64 => ValType::F64,
4551        }
4552    }
4553}