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