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