wasmtime_environ/fact/
trampoline.rs

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