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