wit_bindgen_core/abi.rs
1use std::fmt;
2use std::iter;
3
4use wit_parser::Param;
5pub use wit_parser::abi::{AbiVariant, FlatTypes, WasmSignature, WasmType};
6use wit_parser::{
7 Alignment, ArchitectureSize, ElementInfo, Enum, Flags, FlagsRepr, Function, Handle, Int,
8 Record, Resolve, Result_, SizeAlign, Tuple, Type, TypeDefKind, TypeId, Variant, align_to_arch,
9};
10
11// Helper macro for defining instructions without having to have tons of
12// exhaustive `match` statements to update
13macro_rules! def_instruction {
14 (
15 $( #[$enum_attr:meta] )*
16 pub enum $name:ident<'a> {
17 $(
18 $( #[$attr:meta] )*
19 $variant:ident $( {
20 $($field:ident : $field_ty:ty $(,)* )*
21 } )?
22 :
23 [$num_popped:expr] => [$num_pushed:expr],
24 )*
25 }
26 ) => {
27 $( #[$enum_attr] )*
28 pub enum $name<'a> {
29 $(
30 $( #[$attr] )*
31 $variant $( {
32 $(
33 $field : $field_ty,
34 )*
35 } )? ,
36 )*
37 }
38
39 impl $name<'_> {
40 /// How many operands does this instruction pop from the stack?
41 #[allow(unused_variables, reason = "match arms bind fields for exhaustiveness, not usage")]
42 pub fn operands_len(&self) -> usize {
43 match self {
44 $(
45 Self::$variant $( {
46 $(
47 $field,
48 )*
49 } )? => $num_popped,
50 )*
51 }
52 }
53
54 /// How many results does this instruction push onto the stack?
55 #[allow(unused_variables, reason = "match arms bind fields for exhaustiveness, not usage")]
56 pub fn results_len(&self) -> usize {
57 match self {
58 $(
59 Self::$variant $( {
60 $(
61 $field,
62 )*
63 } )? => $num_pushed,
64 )*
65 }
66 }
67 }
68 };
69}
70
71def_instruction! {
72 #[derive(Debug)]
73 pub enum Instruction<'a> {
74 /// Acquires the specified parameter and places it on the stack.
75 /// Depending on the context this may refer to wasm parameters or
76 /// interface types parameters.
77 GetArg { nth: usize } : [0] => [1],
78
79 // Integer const/manipulation instructions
80
81 /// Pushes the constant `val` onto the stack.
82 I32Const { val: i32 } : [0] => [1],
83 /// Casts the top N items on the stack using the `Bitcast` enum
84 /// provided. Consumes the same number of operands that this produces.
85 Bitcasts { casts: &'a [Bitcast] } : [casts.len()] => [casts.len()],
86 /// Pushes a number of constant zeros for each wasm type on the stack.
87 ConstZero { tys: &'a [WasmType] } : [0] => [tys.len()],
88
89 // Memory load/store instructions
90
91 /// Pops a pointer from the stack and loads a little-endian `i32` from
92 /// it, using the specified constant offset.
93 I32Load { offset: ArchitectureSize } : [1] => [1],
94 /// Pops a pointer from the stack and loads a little-endian `i8` from
95 /// it, using the specified constant offset. The value loaded is the
96 /// zero-extended to 32-bits
97 I32Load8U { offset: ArchitectureSize } : [1] => [1],
98 /// Pops a pointer from the stack and loads a little-endian `i8` from
99 /// it, using the specified constant offset. The value loaded is the
100 /// sign-extended to 32-bits
101 I32Load8S { offset: ArchitectureSize } : [1] => [1],
102 /// Pops a pointer from the stack and loads a little-endian `i16` from
103 /// it, using the specified constant offset. The value loaded is the
104 /// zero-extended to 32-bits
105 I32Load16U { offset: ArchitectureSize } : [1] => [1],
106 /// Pops a pointer from the stack and loads a little-endian `i16` from
107 /// it, using the specified constant offset. The value loaded is the
108 /// sign-extended to 32-bits
109 I32Load16S { offset: ArchitectureSize } : [1] => [1],
110 /// Pops a pointer from the stack and loads a little-endian `i64` from
111 /// it, using the specified constant offset.
112 I64Load { offset: ArchitectureSize } : [1] => [1],
113 /// Pops a pointer from the stack and loads a little-endian `f32` from
114 /// it, using the specified constant offset.
115 F32Load { offset: ArchitectureSize } : [1] => [1],
116 /// Pops a pointer from the stack and loads a little-endian `f64` from
117 /// it, using the specified constant offset.
118 F64Load { offset: ArchitectureSize } : [1] => [1],
119
120 /// Like `I32Load` or `I64Load`, but for loading pointer values.
121 PointerLoad { offset: ArchitectureSize } : [1] => [1],
122 /// Like `I32Load` or `I64Load`, but for loading array length values.
123 LengthLoad { offset: ArchitectureSize } : [1] => [1],
124
125 /// Pops a pointer from the stack and then an `i32` value.
126 /// Stores the value in little-endian at the pointer specified plus the
127 /// constant `offset`.
128 I32Store { offset: ArchitectureSize } : [2] => [0],
129 /// Pops a pointer from the stack and then an `i32` value.
130 /// Stores the low 8 bits of the value in little-endian at the pointer
131 /// specified plus the constant `offset`.
132 I32Store8 { offset: ArchitectureSize } : [2] => [0],
133 /// Pops a pointer from the stack and then an `i32` value.
134 /// Stores the low 16 bits of the value in little-endian at the pointer
135 /// specified plus the constant `offset`.
136 I32Store16 { offset: ArchitectureSize } : [2] => [0],
137 /// Pops a pointer from the stack and then an `i64` value.
138 /// Stores the value in little-endian at the pointer specified plus the
139 /// constant `offset`.
140 I64Store { offset: ArchitectureSize } : [2] => [0],
141 /// Pops a pointer from the stack and then an `f32` value.
142 /// Stores the value in little-endian at the pointer specified plus the
143 /// constant `offset`.
144 F32Store { offset: ArchitectureSize } : [2] => [0],
145 /// Pops a pointer from the stack and then an `f64` value.
146 /// Stores the value in little-endian at the pointer specified plus the
147 /// constant `offset`.
148 F64Store { offset: ArchitectureSize } : [2] => [0],
149
150 /// Like `I32Store` or `I64Store`, but for storing pointer values.
151 PointerStore { offset: ArchitectureSize } : [2] => [0],
152 /// Like `I32Store` or `I64Store`, but for storing array length values.
153 LengthStore { offset: ArchitectureSize } : [2] => [0],
154
155 // Scalar lifting/lowering
156
157 /// Converts an interface type `char` value to a 32-bit integer
158 /// representing the unicode scalar value.
159 I32FromChar : [1] => [1],
160 /// Converts an interface type `u64` value to a wasm `i64`.
161 I64FromU64 : [1] => [1],
162 /// Converts an interface type `s64` value to a wasm `i64`.
163 I64FromS64 : [1] => [1],
164 /// Converts an interface type `u32` value to a wasm `i32`.
165 I32FromU32 : [1] => [1],
166 /// Converts an interface type `s32` value to a wasm `i32`.
167 I32FromS32 : [1] => [1],
168 /// Converts an interface type `u16` value to a wasm `i32`.
169 I32FromU16 : [1] => [1],
170 /// Converts an interface type `s16` value to a wasm `i32`.
171 I32FromS16 : [1] => [1],
172 /// Converts an interface type `u8` value to a wasm `i32`.
173 I32FromU8 : [1] => [1],
174 /// Converts an interface type `s8` value to a wasm `i32`.
175 I32FromS8 : [1] => [1],
176 /// Conversion an interface type `f32` value to a wasm `f32`.
177 ///
178 /// This may be a noop for some implementations, but it's here in case the
179 /// native language representation of `f32` is different than the wasm
180 /// representation of `f32`.
181 CoreF32FromF32 : [1] => [1],
182 /// Conversion an interface type `f64` value to a wasm `f64`.
183 ///
184 /// This may be a noop for some implementations, but it's here in case the
185 /// native language representation of `f64` is different than the wasm
186 /// representation of `f64`.
187 CoreF64FromF64 : [1] => [1],
188
189 /// Converts a native wasm `i32` to an interface type `s8`.
190 ///
191 /// This will truncate the upper bits of the `i32`.
192 S8FromI32 : [1] => [1],
193 /// Converts a native wasm `i32` to an interface type `u8`.
194 ///
195 /// This will truncate the upper bits of the `i32`.
196 U8FromI32 : [1] => [1],
197 /// Converts a native wasm `i32` to an interface type `s16`.
198 ///
199 /// This will truncate the upper bits of the `i32`.
200 S16FromI32 : [1] => [1],
201 /// Converts a native wasm `i32` to an interface type `u16`.
202 ///
203 /// This will truncate the upper bits of the `i32`.
204 U16FromI32 : [1] => [1],
205 /// Converts a native wasm `i32` to an interface type `s32`.
206 S32FromI32 : [1] => [1],
207 /// Converts a native wasm `i32` to an interface type `u32`.
208 U32FromI32 : [1] => [1],
209 /// Converts a native wasm `i64` to an interface type `s64`.
210 S64FromI64 : [1] => [1],
211 /// Converts a native wasm `i64` to an interface type `u64`.
212 U64FromI64 : [1] => [1],
213 /// Converts a native wasm `i32` to an interface type `char`.
214 ///
215 /// It's safe to assume that the `i32` is indeed a valid unicode code point.
216 CharFromI32 : [1] => [1],
217 /// Converts a native wasm `f32` to an interface type `f32`.
218 F32FromCoreF32 : [1] => [1],
219 /// Converts a native wasm `f64` to an interface type `f64`.
220 F64FromCoreF64 : [1] => [1],
221
222 /// Creates a `bool` from an `i32` input, trapping if the `i32` isn't
223 /// zero or one.
224 BoolFromI32 : [1] => [1],
225 /// Creates an `i32` from a `bool` input, must return 0 or 1.
226 I32FromBool : [1] => [1],
227
228 // lists
229
230 /// Lowers a list where the element's layout in the native language is
231 /// expected to match the canonical ABI definition of interface types.
232 ///
233 /// Pops a list value from the stack and pushes the pointer/length onto
234 /// the stack. If `realloc` is set to `Some` then this is expected to
235 /// *consume* the list which means that the data needs to be copied. An
236 /// allocation/copy is expected when:
237 ///
238 /// * A host is calling a wasm export with a list (it needs to copy the
239 /// list in to the callee's module, allocating space with `realloc`)
240 /// * A wasm export is returning a list (it's expected to use `realloc`
241 /// to give ownership of the list to the caller.
242 /// * A host is returning a list in a import definition, meaning that
243 /// space needs to be allocated in the caller with `realloc`).
244 ///
245 /// A copy does not happen (e.g. `realloc` is `None`) when:
246 ///
247 /// * A wasm module calls an import with the list. In this situation
248 /// it's expected the caller will know how to access this module's
249 /// memory (e.g. the host has raw access or wasm-to-wasm communication
250 /// would copy the list).
251 ///
252 /// If `realloc` is `Some` then the adapter is not responsible for
253 /// cleaning up this list because the other end is receiving the
254 /// allocation. If `realloc` is `None` then the adapter is responsible
255 /// for cleaning up any temporary allocation it created, if any.
256 ListCanonLower {
257 element: &'a Type,
258 realloc: Option<&'a str>,
259 } : [1] => [2],
260
261 /// Same as `ListCanonLower`, but used for strings
262 StringLower {
263 realloc: Option<&'a str>,
264 } : [1] => [2],
265
266 /// Lowers a list where the element's layout in the native language is
267 /// not expected to match the canonical ABI definition of interface
268 /// types.
269 ///
270 /// Pops a list value from the stack and pushes the pointer/length onto
271 /// the stack. This operation also pops a block from the block stack
272 /// which is used as the iteration body of writing each element of the
273 /// list consumed.
274 ///
275 /// The `realloc` field here behaves the same way as `ListCanonLower`.
276 /// It's only set to `None` when a wasm module calls a declared import.
277 /// Otherwise lowering in other contexts requires allocating memory for
278 /// the receiver to own.
279 ListLower {
280 element: &'a Type,
281 realloc: Option<&'a str>,
282 } : [1] => [2],
283
284 /// Lifts a list which has a canonical representation into an interface
285 /// types value.
286 ///
287 /// The term "canonical" representation here means that the
288 /// representation of the interface types value in the native language
289 /// exactly matches the canonical ABI definition of the type.
290 ///
291 /// This will consume two `i32` values from the stack, a pointer and a
292 /// length, and then produces an interface value list.
293 ListCanonLift {
294 element: &'a Type,
295 ty: TypeId,
296 } : [2] => [1],
297
298 /// Same as `ListCanonLift`, but used for strings
299 StringLift : [2] => [1],
300
301 /// Lifts a list which into an interface types value.
302 ///
303 /// This will consume two `i32` values from the stack, a pointer and a
304 /// length, and then produces an interface value list.
305 ///
306 /// This will also pop a block from the block stack which is how to
307 /// read each individual element from the list.
308 ListLift {
309 element: &'a Type,
310 ty: TypeId,
311 } : [2] => [1],
312
313 /// Lowers a map into a canonical pointer/length pair.
314 ///
315 /// This operation pops a map value from the stack and pushes pointer
316 /// and length. A block is popped from the block stack to lower one
317 /// key/value entry into linear memory.
318 MapLower {
319 key: &'a Type,
320 value: &'a Type,
321 realloc: Option<&'a str>,
322 } : [1] => [2],
323
324 /// Lifts a canonical pointer/length pair into a map.
325 ///
326 /// This operation consumes pointer and length from the stack. A block
327 /// is popped from the block stack and must produce key/value for one
328 /// map entry.
329 MapLift {
330 key: &'a Type,
331 value: &'a Type,
332 ty: TypeId,
333 } : [2] => [1],
334
335 /// Pops all fields for a fixed list off the stack and then composes them
336 /// into an array.
337 FixedLengthListLift {
338 element: &'a Type,
339 size: u32,
340 id: TypeId,
341 } : [*size as usize] => [1],
342
343 /// Pops an array off the stack, decomposes the elements and then pushes them onto the stack.
344 FixedLengthListLower {
345 element: &'a Type,
346 size: u32,
347 id: TypeId,
348 } : [1] => [*size as usize],
349
350 /// Pops an array and an address off the stack, passes each element to a block storing it
351 FixedLengthListLowerToMemory {
352 element: &'a Type,
353 size: u32,
354 id: TypeId,
355 } : [2] => [0],
356
357 /// Pops base address, pushes an array
358 ///
359 /// This will also pop a block from the block stack which is how to
360 /// read each individual element from the list.
361 FixedLengthListLiftFromMemory {
362 element: &'a Type,
363 size: u32,
364 id: TypeId,
365 } : [1] => [1],
366
367
368 /// Pushes an operand onto the stack representing the list item from
369 /// each iteration of the list.
370 ///
371 /// This is only used inside of blocks related to lowering lists.
372 IterElem { element: &'a Type } : [0] => [1],
373
374 /// Pushes an operand onto the stack representing the current map key
375 /// for each map iteration.
376 IterMapKey { key: &'a Type } : [0] => [1],
377
378 /// Pushes an operand onto the stack representing the current map value
379 /// for each map iteration.
380 IterMapValue { value: &'a Type } : [0] => [1],
381
382 /// Pushes an operand onto the stack representing the base pointer of
383 /// the next element in a list.
384 ///
385 /// This is used for both lifting and lowering lists.
386 IterBasePointer : [0] => [1],
387
388 // records and tuples
389
390 /// Pops a record value off the stack, decomposes the record to all of
391 /// its fields, and then pushes the fields onto the stack.
392 RecordLower {
393 record: &'a Record,
394 name: &'a str,
395 ty: TypeId,
396 } : [1] => [record.fields.len()],
397
398 /// Pops all fields for a record off the stack and then composes them
399 /// into a record.
400 RecordLift {
401 record: &'a Record,
402 name: &'a str,
403 ty: TypeId,
404 } : [record.fields.len()] => [1],
405
406 /// Create an `i32` from a handle.
407 HandleLower {
408 handle: &'a Handle,
409 name: &'a str,
410 ty: TypeId,
411 } : [1] => [1],
412
413 /// Create a handle from an `i32`.
414 HandleLift {
415 handle: &'a Handle,
416 name: &'a str,
417 ty: TypeId,
418 } : [1] => [1],
419
420 /// Create an `i32` from a future.
421 FutureLower {
422 payload: &'a Option<Type>,
423 ty: TypeId,
424 } : [1] => [1],
425
426 /// Create a future from an `i32`.
427 FutureLift {
428 payload: &'a Option<Type>,
429 ty: TypeId,
430 } : [1] => [1],
431
432 /// Create an `i32` from a stream.
433 StreamLower {
434 payload: &'a Option<Type>,
435 ty: TypeId,
436 } : [1] => [1],
437
438 /// Create a stream from an `i32`.
439 StreamLift {
440 payload: &'a Option<Type>,
441 ty: TypeId,
442 } : [1] => [1],
443
444 /// Create an `i32` from an error-context.
445 ErrorContextLower : [1] => [1],
446
447 /// Create a error-context from an `i32`.
448 ErrorContextLift : [1] => [1],
449
450 /// Pops a tuple value off the stack, decomposes the tuple to all of
451 /// its fields, and then pushes the fields onto the stack.
452 TupleLower {
453 tuple: &'a Tuple,
454 ty: TypeId,
455 } : [1] => [tuple.types.len()],
456
457 /// Pops all fields for a tuple off the stack and then composes them
458 /// into a tuple.
459 TupleLift {
460 tuple: &'a Tuple,
461 ty: TypeId,
462 } : [tuple.types.len()] => [1],
463
464 /// Converts a language-specific record-of-bools to a list of `i32`.
465 FlagsLower {
466 flags: &'a Flags,
467 name: &'a str,
468 ty: TypeId,
469 } : [1] => [flags.repr().count()],
470 /// Converts a list of native wasm `i32` to a language-specific
471 /// record-of-bools.
472 FlagsLift {
473 flags: &'a Flags,
474 name: &'a str,
475 ty: TypeId,
476 } : [flags.repr().count()] => [1],
477
478 // variants
479
480 /// This is a special instruction used for `VariantLower`
481 /// instruction to determine the name of the payload, if present, to use
482 /// within each block.
483 ///
484 /// Each sub-block will have this be the first instruction, and if it
485 /// lowers a payload it will expect something bound to this name.
486 VariantPayloadName : [0] => [1],
487
488 /// Pops a variant off the stack as well as `ty.cases.len()` blocks
489 /// from the code generator. Uses each of those blocks and the value
490 /// from the stack to produce `nresults` of items.
491 VariantLower {
492 variant: &'a Variant,
493 name: &'a str,
494 ty: TypeId,
495 results: &'a [WasmType],
496 } : [1] => [results.len()],
497
498 /// Pops an `i32` off the stack as well as `ty.cases.len()` blocks
499 /// from the code generator. Uses each of those blocks and the value
500 /// from the stack to produce a final variant.
501 VariantLift {
502 variant: &'a Variant,
503 name: &'a str,
504 ty: TypeId,
505 } : [1] => [1],
506
507 /// Pops an enum off the stack and pushes the `i32` representation.
508 EnumLower {
509 enum_: &'a Enum,
510 name: &'a str,
511 ty: TypeId,
512 } : [1] => [1],
513
514 /// Pops an `i32` off the stack and lifts it into the `enum` specified.
515 EnumLift {
516 enum_: &'a Enum,
517 name: &'a str,
518 ty: TypeId,
519 } : [1] => [1],
520
521 /// Specialization of `VariantLower` for specifically `option<T>` types,
522 /// otherwise behaves the same as `VariantLower` (e.g. two blocks for
523 /// the two cases.
524 OptionLower {
525 payload: &'a Type,
526 ty: TypeId,
527 results: &'a [WasmType],
528 } : [1] => [results.len()],
529
530 /// Specialization of `VariantLift` for specifically the `option<T>`
531 /// type. Otherwise behaves the same as the `VariantLift` instruction
532 /// with two blocks for the lift.
533 OptionLift {
534 payload: &'a Type,
535 ty: TypeId,
536 } : [1] => [1],
537
538 /// Specialization of `VariantLower` for specifically `result<T, E>`
539 /// types, otherwise behaves the same as `VariantLower` (e.g. two blocks
540 /// for the two cases.
541 ResultLower {
542 result: &'a Result_
543 ty: TypeId,
544 results: &'a [WasmType],
545 } : [1] => [results.len()],
546
547 /// Specialization of `VariantLift` for specifically the `result<T,
548 /// E>` type. Otherwise behaves the same as the `VariantLift`
549 /// instruction with two blocks for the lift.
550 ResultLift {
551 result: &'a Result_,
552 ty: TypeId,
553 } : [1] => [1],
554
555 // calling/control flow
556
557 /// Represents a call to a raw WebAssembly API. The module/name are
558 /// provided inline as well as the types if necessary.
559 CallWasm {
560 name: &'a str,
561 sig: &'a WasmSignature,
562 } : [sig.params.len()] => [sig.results.len()],
563
564 /// Same as `CallWasm`, except the dual where an interface is being
565 /// called rather than a raw wasm function.
566 ///
567 /// Note that this will be used for async functions, and `async_`
568 /// indicates whether the function should be invoked in an async
569 /// fashion.
570 CallInterface {
571 func: &'a Function,
572 async_: bool,
573 } : [func.params.len()] => [usize::from(func.result.is_some())],
574
575 /// Returns `amt` values on the stack. This is always the last
576 /// instruction.
577 Return { amt: usize, func: &'a Function } : [*amt] => [0],
578
579 /// Calls the `realloc` function specified in a malloc-like fashion
580 /// allocating `size` bytes with alignment `align`.
581 ///
582 /// Pushes the returned pointer onto the stack.
583 Malloc {
584 realloc: &'static str,
585 size: ArchitectureSize,
586 align: Alignment,
587 } : [0] => [1],
588
589 /// Used exclusively for guest-code generation this indicates that
590 /// the standard memory deallocation function needs to be invoked with
591 /// the specified parameters.
592 ///
593 /// This will pop a pointer from the stack and push nothing.
594 GuestDeallocate {
595 size: ArchitectureSize,
596 align: Alignment,
597 } : [1] => [0],
598
599 /// Used exclusively for guest-code generation this indicates that
600 /// a string is being deallocated. The ptr/length are on the stack and
601 /// are poppped off and used to deallocate the string.
602 GuestDeallocateString : [2] => [0],
603
604 /// Used exclusively for guest-code generation this indicates that
605 /// a list is being deallocated. The ptr/length are on the stack and
606 /// are poppped off and used to deallocate the list.
607 ///
608 /// This variant also pops a block off the block stack to be used as the
609 /// body of the deallocation loop.
610 GuestDeallocateList {
611 element: &'a Type,
612 } : [2] => [0],
613
614 /// Used exclusively for guest-code generation this indicates that a
615 /// map is being deallocated. The ptr/length are on the stack and are
616 /// popped off and used to deallocate the map entry buffer.
617 ///
618 /// This variant also pops a block off the block stack to be used as
619 /// the body of the deallocation loop over map entries.
620 GuestDeallocateMap {
621 key: &'a Type,
622 value: &'a Type,
623 } : [2] => [0],
624
625 /// Used exclusively for guest-code generation this indicates that
626 /// a variant is being deallocated. The integer discriminant is popped
627 /// off the stack as well as `blocks` number of blocks popped from the
628 /// blocks stack. The variant is used to select, at runtime, which of
629 /// the blocks is executed to deallocate the variant.
630 GuestDeallocateVariant {
631 blocks: usize,
632 } : [1] => [0],
633
634 /// Deallocates the language-specific handle representation on the top
635 /// of the stack. Used for async imports.
636 DropHandle { ty: &'a Type } : [1] => [0],
637
638 /// Call `task.return` for an async-lifted export.
639 ///
640 /// This will call core wasm import `name` which will be mapped to
641 /// `task.return` later on. The function given has `params` as its
642 /// parameters and it will return no results. This is used to pass the
643 /// lowered representation of a function's results to `task.return`.
644 AsyncTaskReturn { name: &'a str, params: &'a [WasmType] } : [params.len()] => [0],
645
646 /// Force the evaluation of the specified number of expressions and push
647 /// the results to the stack.
648 ///
649 /// This is useful prior to disposing of temporary variables and/or
650 /// allocations which are referenced by one or more not-yet-evaluated
651 /// expressions.
652 Flush { amt: usize } : [*amt] => [*amt],
653 }
654}
655
656#[derive(Debug, PartialEq)]
657pub enum Bitcast {
658 // Upcasts
659 F32ToI32,
660 F64ToI64,
661 I32ToI64,
662 F32ToI64,
663
664 // Downcasts
665 I32ToF32,
666 I64ToF64,
667 I64ToI32,
668 I64ToF32,
669
670 // PointerOrI64 conversions. These preserve provenance when the source
671 // or destination is a pointer value.
672 //
673 // These are used when pointer values are being stored in
674 // (ToP64) and loaded out of (P64To) PointerOrI64 values, so they
675 // always have to preserve provenance when the value being loaded or
676 // stored is a pointer.
677 P64ToI64,
678 I64ToP64,
679 P64ToP,
680 PToP64,
681
682 // Pointer<->number conversions. These do not preserve provenance.
683 //
684 // These are used when integer or floating-point values are being stored in
685 // (I32ToP/etc.) and loaded out of (PToI32/etc.) pointer values, so they
686 // never have any provenance to preserve.
687 I32ToP,
688 PToI32,
689 PToL,
690 LToP,
691
692 // Number<->Number conversions.
693 I32ToL,
694 LToI32,
695 I64ToL,
696 LToI64,
697
698 // Multiple conversions in sequence.
699 Sequence(Box<[Bitcast; 2]>),
700
701 None,
702}
703
704/// Whether the glue code surrounding a call is lifting arguments and lowering
705/// results or vice versa.
706#[derive(Clone, Copy, PartialEq, Eq)]
707pub enum LiftLower {
708 /// When the glue code lifts arguments and lowers results.
709 ///
710 /// ```text
711 /// Wasm --lift-args--> SourceLanguage; call; SourceLanguage --lower-results--> Wasm
712 /// ```
713 LiftArgsLowerResults,
714 /// When the glue code lowers arguments and lifts results.
715 ///
716 /// ```text
717 /// SourceLanguage --lower-args--> Wasm; call; Wasm --lift-results--> SourceLanguage
718 /// ```
719 LowerArgsLiftResults,
720}
721
722/// Trait for language implementors to use to generate glue code between native
723/// WebAssembly signatures and interface types signatures.
724///
725/// This is used as an implementation detail in interpreting the ABI between
726/// interface types and wasm types. Eventually this will be driven by interface
727/// types adapters themselves, but for now the ABI of a function dictates what
728/// instructions are fed in.
729///
730/// Types implementing `Bindgen` are incrementally fed `Instruction` values to
731/// generate code for. Instructions operate like a stack machine where each
732/// instruction has a list of inputs and a list of outputs (provided by the
733/// `emit` function).
734pub trait Bindgen {
735 /// The intermediate type for fragments of code for this type.
736 ///
737 /// For most languages `String` is a suitable intermediate type.
738 type Operand: Clone + fmt::Debug;
739
740 /// Emit code to implement the given instruction.
741 ///
742 /// Each operand is given in `operands` and can be popped off if ownership
743 /// is required. It's guaranteed that `operands` has the appropriate length
744 /// for the `inst` given, as specified with [`Instruction`].
745 ///
746 /// Each result variable should be pushed onto `results`. This function must
747 /// push the appropriate number of results or binding generation will panic.
748 fn emit(
749 &mut self,
750 resolve: &Resolve,
751 inst: &Instruction<'_>,
752 operands: &mut Vec<Self::Operand>,
753 results: &mut Vec<Self::Operand>,
754 );
755
756 /// Gets a operand reference to the return pointer area.
757 ///
758 /// The provided size and alignment is for the function's return type.
759 fn return_pointer(&mut self, size: ArchitectureSize, align: Alignment) -> Self::Operand;
760
761 /// Enters a new block of code to generate code for.
762 ///
763 /// This is currently exclusively used for constructing variants. When a
764 /// variant is constructed a block here will be pushed for each case of a
765 /// variant, generating the code necessary to translate a variant case.
766 ///
767 /// Blocks are completed with `finish_block` below. It's expected that `emit`
768 /// will always push code (if necessary) into the "current block", which is
769 /// updated by calling this method and `finish_block` below.
770 fn push_block(&mut self);
771
772 /// Indicates to the code generator that a block is completed, and the
773 /// `operand` specified was the resulting value of the block.
774 ///
775 /// This method will be used to compute the value of each arm of lifting a
776 /// variant. The `operand` will be `None` if the variant case didn't
777 /// actually have any type associated with it. Otherwise it will be `Some`
778 /// as the last value remaining on the stack representing the value
779 /// associated with a variant's `case`.
780 ///
781 /// It's expected that this will resume code generation in the previous
782 /// block before `push_block` was called. This must also save the results
783 /// of the current block internally for instructions like `ResultLift` to
784 /// use later.
785 fn finish_block(&mut self, operand: &mut Vec<Self::Operand>);
786
787 /// Returns size information that was previously calculated for all types.
788 fn sizes(&self) -> &SizeAlign;
789
790 /// Returns whether or not the specified element type is represented in a
791 /// "canonical" form for lists. This dictates whether the `ListCanonLower`
792 /// and `ListCanonLift` instructions are used or not.
793 fn is_list_canonical(&self, resolve: &Resolve, element: &Type) -> bool;
794}
795
796/// Generates an abstract sequence of instructions which represents this
797/// function being adapted as an imported function.
798///
799/// The instructions here, when executed, will emulate a language with
800/// interface types calling the concrete wasm implementation. The parameters
801/// for the returned instruction sequence are the language's own
802/// interface-types parameters. One instruction in the instruction stream
803/// will be a `Call` which represents calling the actual raw wasm function
804/// signature.
805///
806/// This function is useful, for example, if you're building a language
807/// generator for WASI bindings. This will document how to translate
808/// language-specific values into the wasm types to call a WASI function,
809/// and it will also automatically convert the results of the WASI function
810/// back to a language-specific value.
811pub fn call(
812 resolve: &Resolve,
813 variant: AbiVariant,
814 lift_lower: LiftLower,
815 func: &Function,
816 bindgen: &mut impl Bindgen,
817 async_: bool,
818) {
819 Generator::new(resolve, bindgen).call(func, variant, lift_lower, async_);
820}
821
822pub fn lower_to_memory<B: Bindgen>(
823 resolve: &Resolve,
824 bindgen: &mut B,
825 address: B::Operand,
826 value: B::Operand,
827 ty: &Type,
828) {
829 let mut generator = Generator::new(resolve, bindgen);
830 // TODO: make this configurable? Right now this function is only called for
831 // future/stream callbacks so it's appropriate to skip realloc here as it's
832 // all "lower for wasm import", but this might get reused for something else
833 // in the future.
834 generator.realloc = Some(Realloc::Export("cabi_realloc"));
835 generator.stack.push(value);
836 generator.write_to_memory(ty, address, Default::default());
837}
838
839pub fn lower_flat<B: Bindgen>(
840 resolve: &Resolve,
841 bindgen: &mut B,
842 value: B::Operand,
843 ty: &Type,
844) -> Vec<B::Operand> {
845 let mut generator = Generator::new(resolve, bindgen);
846 generator.stack.push(value);
847 generator.realloc = Some(Realloc::Export("cabi_realloc"));
848 generator.lower(ty);
849 generator.stack
850}
851
852pub fn lift_from_memory<B: Bindgen>(
853 resolve: &Resolve,
854 bindgen: &mut B,
855 address: B::Operand,
856 ty: &Type,
857) -> B::Operand {
858 let mut generator = Generator::new(resolve, bindgen);
859 generator.read_from_memory(ty, address, Default::default());
860 generator.stack.pop().unwrap()
861}
862
863/// Used in a similar manner as the `Interface::call` function except is
864/// used to generate the `post-return` callback for `func`.
865///
866/// This is only intended to be used in guest generators for exported
867/// functions and will primarily generate `GuestDeallocate*` instructions,
868/// plus others used as input to those instructions.
869pub fn post_return(resolve: &Resolve, func: &Function, bindgen: &mut impl Bindgen) {
870 Generator::new(resolve, bindgen).post_return(func);
871}
872
873/// Returns whether the `Function` specified needs a post-return function to
874/// be generated in guest code.
875///
876/// This is used when the return value contains a memory allocation such as
877/// a list or a string primarily.
878pub fn guest_export_needs_post_return(resolve: &Resolve, func: &Function) -> bool {
879 func.result
880 .map(|t| needs_deallocate(resolve, &t, Deallocate::Lists))
881 .unwrap_or(false)
882}
883
884pub fn guest_export_params_have_allocations(resolve: &Resolve, func: &Function) -> bool {
885 func.params
886 .iter()
887 .any(|param| needs_deallocate(resolve, ¶m.ty, Deallocate::Lists))
888}
889
890fn needs_deallocate(resolve: &Resolve, ty: &Type, what: Deallocate) -> bool {
891 match ty {
892 Type::String => true,
893 Type::ErrorContext => true,
894 Type::Id(id) => match &resolve.types[*id].kind {
895 TypeDefKind::List(_) => true,
896 TypeDefKind::Type(t) => needs_deallocate(resolve, t, what),
897 TypeDefKind::Handle(Handle::Own(_)) => what.handles(),
898 TypeDefKind::Handle(Handle::Borrow(_)) => false,
899 TypeDefKind::Resource => false,
900 TypeDefKind::Record(r) => r
901 .fields
902 .iter()
903 .any(|f| needs_deallocate(resolve, &f.ty, what)),
904 TypeDefKind::Tuple(t) => t.types.iter().any(|t| needs_deallocate(resolve, t, what)),
905 TypeDefKind::Variant(t) => t
906 .cases
907 .iter()
908 .filter_map(|t| t.ty.as_ref())
909 .any(|t| needs_deallocate(resolve, t, what)),
910 TypeDefKind::Option(t) => needs_deallocate(resolve, t, what),
911 TypeDefKind::Result(t) => [&t.ok, &t.err]
912 .iter()
913 .filter_map(|t| t.as_ref())
914 .any(|t| needs_deallocate(resolve, t, what)),
915 TypeDefKind::Flags(_) | TypeDefKind::Enum(_) => false,
916 TypeDefKind::Future(_) | TypeDefKind::Stream(_) => what.handles(),
917 TypeDefKind::Unknown => unreachable!(),
918 TypeDefKind::FixedLengthList(t, _) => needs_deallocate(resolve, t, what),
919 TypeDefKind::Map(_, _) => true,
920 },
921
922 Type::Bool
923 | Type::U8
924 | Type::S8
925 | Type::U16
926 | Type::S16
927 | Type::U32
928 | Type::S32
929 | Type::U64
930 | Type::S64
931 | Type::F32
932 | Type::F64
933 | Type::Char => false,
934 }
935}
936
937/// Generate instructions in `bindgen` to deallocate all lists in `ptr` where
938/// that's a pointer to a sequence of `types` stored in linear memory.
939pub fn deallocate_lists_in_types<B: Bindgen>(
940 resolve: &Resolve,
941 types: &[Type],
942 operands: &[B::Operand],
943 indirect: bool,
944 bindgen: &mut B,
945) {
946 Generator::new(resolve, bindgen).deallocate_in_types(
947 types,
948 operands,
949 indirect,
950 Deallocate::Lists,
951 );
952}
953
954/// Generate instructions in `bindgen` to deallocate all lists in `ptr` where
955/// that's a pointer to a sequence of `types` stored in linear memory.
956pub fn deallocate_lists_and_own_in_types<B: Bindgen>(
957 resolve: &Resolve,
958 types: &[Type],
959 operands: &[B::Operand],
960 indirect: bool,
961 bindgen: &mut B,
962) {
963 Generator::new(resolve, bindgen).deallocate_in_types(
964 types,
965 operands,
966 indirect,
967 Deallocate::ListsAndOwn,
968 );
969}
970
971#[derive(Copy, Clone)]
972pub enum Realloc {
973 None,
974 Export(&'static str),
975}
976
977/// What to deallocate in various `deallocate_*` methods.
978#[derive(Copy, Clone)]
979enum Deallocate {
980 /// Only deallocate lists.
981 Lists,
982 /// Deallocate lists and owned resources such as `own<T>` and
983 /// futures/streams.
984 ListsAndOwn,
985}
986
987impl Deallocate {
988 fn handles(&self) -> bool {
989 match self {
990 Deallocate::Lists => false,
991 Deallocate::ListsAndOwn => true,
992 }
993 }
994}
995
996struct Generator<'a, B: Bindgen> {
997 bindgen: &'a mut B,
998 resolve: &'a Resolve,
999 operands: Vec<B::Operand>,
1000 results: Vec<B::Operand>,
1001 stack: Vec<B::Operand>,
1002 return_pointer: Option<B::Operand>,
1003 realloc: Option<Realloc>,
1004}
1005
1006const MAX_FLAT_PARAMS: usize = 16;
1007const MAX_FLAT_ASYNC_PARAMS: usize = 4;
1008
1009impl<'a, B: Bindgen> Generator<'a, B> {
1010 fn new(resolve: &'a Resolve, bindgen: &'a mut B) -> Generator<'a, B> {
1011 Generator {
1012 resolve,
1013 bindgen,
1014 operands: Vec::new(),
1015 results: Vec::new(),
1016 stack: Vec::new(),
1017 return_pointer: None,
1018 realloc: None,
1019 }
1020 }
1021
1022 fn call(&mut self, func: &Function, variant: AbiVariant, lift_lower: LiftLower, async_: bool) {
1023 let sig = self.resolve.wasm_signature(variant, func);
1024
1025 // Lowering parameters calling a wasm import _or_ returning a result
1026 // from an async-lifted wasm export means we don't need to pass
1027 // ownership, but we pass ownership in all other cases.
1028 let realloc = match (variant, lift_lower, async_) {
1029 (AbiVariant::GuestImport, LiftLower::LowerArgsLiftResults, _)
1030 | (
1031 AbiVariant::GuestExport
1032 | AbiVariant::GuestExportAsync
1033 | AbiVariant::GuestExportAsyncStackful,
1034 LiftLower::LiftArgsLowerResults,
1035 true,
1036 ) => Realloc::None,
1037 _ => Realloc::Export("cabi_realloc"),
1038 };
1039 assert!(self.realloc.is_none());
1040
1041 match lift_lower {
1042 LiftLower::LowerArgsLiftResults => {
1043 self.realloc = Some(realloc);
1044
1045 // Create a function that performs individual lowering of operands
1046 let lower_to_memory = |self_: &mut Self, ptr: B::Operand| {
1047 let mut offset = ArchitectureSize::default();
1048 for (nth, Param { ty, .. }) in func.params.iter().enumerate() {
1049 self_.emit(&Instruction::GetArg { nth });
1050 offset = align_to_arch(offset, self_.bindgen.sizes().align(ty));
1051 self_.write_to_memory(ty, ptr.clone(), offset);
1052 offset += self_.bindgen.sizes().size(ty);
1053 }
1054
1055 self_.stack.push(ptr);
1056 };
1057
1058 // Lower parameters
1059 if sig.indirect_params {
1060 // If parameters are indirect space is
1061 // allocated for them and each argument is lowered
1062 // individually into memory.
1063 let ElementInfo { size, align } = self
1064 .bindgen
1065 .sizes()
1066 .record(func.params.iter().map(|param| ¶m.ty));
1067
1068 // Resolve the pointer to the indirectly stored parameters
1069 let ptr = match variant {
1070 // When a wasm module calls an import it will provide
1071 // space that isn't explicitly deallocated.
1072 AbiVariant::GuestImport => self.bindgen.return_pointer(size, align),
1073
1074 AbiVariant::GuestImportAsync => {
1075 todo!("direct param lowering for async guest import not implemented")
1076 }
1077
1078 // When calling a wasm module from the outside, though,
1079 // malloc needs to be called.
1080 AbiVariant::GuestExport => {
1081 self.emit(&Instruction::Malloc {
1082 realloc: "cabi_realloc",
1083 size,
1084 align,
1085 });
1086 self.stack.pop().unwrap()
1087 }
1088
1089 AbiVariant::GuestExportAsync | AbiVariant::GuestExportAsyncStackful => {
1090 todo!("direct param lowering for async not implemented")
1091 }
1092 };
1093
1094 // Lower the parameters to memory
1095 lower_to_memory(self, ptr);
1096 } else {
1097 // ... otherwise arguments are direct,
1098 // (there aren't too many) then we simply do a normal lower
1099 // operation for them all.
1100 for (nth, Param { ty, .. }) in func.params.iter().enumerate() {
1101 self.emit(&Instruction::GetArg { nth });
1102 self.lower(ty);
1103 }
1104 }
1105 self.realloc = None;
1106
1107 // If necessary we may need to prepare a return pointer for this ABI.
1108 if variant == AbiVariant::GuestImport && sig.retptr {
1109 let info = self.bindgen.sizes().params(&func.result);
1110 let ptr = self.bindgen.return_pointer(info.size, info.align);
1111 self.return_pointer = Some(ptr.clone());
1112 self.stack.push(ptr);
1113 }
1114
1115 // Call the Wasm function
1116 assert_eq!(self.stack.len(), sig.params.len());
1117 self.emit(&Instruction::CallWasm {
1118 name: &func.name,
1119 sig: &sig,
1120 });
1121
1122 // Handle the result
1123 if sig.retptr {
1124 // If there is a return pointer, we must get the pointer to where results
1125 // should be stored, and store the results there?
1126
1127 let ptr = match variant {
1128 // imports into guests means it's a wasm module
1129 // calling an imported function. We supplied the
1130 // return pointer as the last argument (saved in
1131 // `self.return_pointer`) so we use that to read
1132 // the result of the function from memory.
1133 AbiVariant::GuestImport => {
1134 assert!(sig.results.is_empty());
1135 self.return_pointer.take().unwrap()
1136 }
1137
1138 // guest exports means that this is a host
1139 // calling wasm so wasm returned a pointer to where
1140 // the result is stored
1141 AbiVariant::GuestExport => self.stack.pop().unwrap(),
1142
1143 AbiVariant::GuestImportAsync
1144 | AbiVariant::GuestExportAsync
1145 | AbiVariant::GuestExportAsyncStackful => {
1146 unreachable!()
1147 }
1148 };
1149
1150 if let (AbiVariant::GuestExport, true) = (variant, async_) {
1151 // If we're dealing with an async function, the result should not be read from memory
1152 // immediately, as it's the async call result
1153 //
1154 // We can leave the result of the call (the indication of what to do as an async call)
1155 // on the stack as a return
1156 self.stack.push(ptr);
1157 } else {
1158 // If we're not dealing with an async call, the result must be in memory at this point and can be read out
1159 self.read_results_from_memory(
1160 &func.result,
1161 ptr.clone(),
1162 ArchitectureSize::default(),
1163 );
1164 self.emit(&Instruction::Flush {
1165 amt: usize::from(func.result.is_some()),
1166 });
1167 }
1168 } else if !async_ {
1169 // With no return pointer in use for a synchronous call, we can simply lift the
1170 // result(s) of the function from the result of the core wasm function. For async
1171 // calls, the component result is delivered via `task.return` and core result is
1172 // async progress.
1173 if let Some(ty) = &func.result {
1174 self.lift(ty)
1175 }
1176 }
1177
1178 // Emit the function return
1179 if async_ {
1180 self.emit(&Instruction::AsyncTaskReturn {
1181 name: &func.name,
1182 params: &sig.results,
1183 });
1184 } else {
1185 self.emit(&Instruction::Return {
1186 func,
1187 amt: usize::from(func.result.is_some()),
1188 });
1189 }
1190 }
1191
1192 LiftLower::LiftArgsLowerResults => {
1193 let max_flat_params = match (variant, async_) {
1194 (AbiVariant::GuestImportAsync, _is_async @ true) => MAX_FLAT_ASYNC_PARAMS,
1195 _ => MAX_FLAT_PARAMS,
1196 };
1197
1198 // Read parameters from memory
1199 let read_from_memory = |self_: &mut Self| {
1200 let mut offset = ArchitectureSize::default();
1201 let ptr = self_
1202 .stack
1203 .pop()
1204 .expect("empty stack during read param from memory");
1205 for Param { ty, .. } in func.params.iter() {
1206 offset = align_to_arch(offset, self_.bindgen.sizes().align(ty));
1207 self_.read_from_memory(ty, ptr.clone(), offset);
1208 offset += self_.bindgen.sizes().size(ty);
1209 }
1210 };
1211
1212 // Resolve parameters
1213 if sig.indirect_params {
1214 // If parameters were passed indirectly, arguments must be
1215 // read in succession from memory, with the pointer to the arguments
1216 // being the first argument to the function.
1217 self.emit(&Instruction::GetArg { nth: 0 });
1218 read_from_memory(self);
1219 } else {
1220 // ... otherwise, if parameters were passed directly then we lift each
1221 // argument in succession from the component wasm types that
1222 // make-up the type.
1223 let mut offset = 0;
1224 for Param {
1225 name: param_name,
1226 ty,
1227 ..
1228 } in func.params.iter()
1229 {
1230 let Some(types) = flat_types(self.resolve, ty, Some(max_flat_params))
1231 else {
1232 panic!(
1233 "failed to flatten types during direct parameter lifting ('{param_name}' in func '{}')",
1234 func.name
1235 );
1236 };
1237 for _ in 0..types.len() {
1238 self.emit(&Instruction::GetArg { nth: offset });
1239 offset += 1;
1240 }
1241 self.lift(ty);
1242 }
1243 }
1244
1245 // ... and that allows us to call the interface types function
1246 self.emit(&Instruction::CallInterface { func, async_ });
1247
1248 // The return value of an async function is *not* the result of the function
1249 // itself or a pointer but rather a status code.
1250 //
1251 // Asynchronous functions will call `task.return` after the
1252 // interface function completes, so lowering is conditional
1253 // based on slightly different logic for the `task.return`
1254 // intrinsic.
1255 //
1256 // Note that in the async import case the code below deals with the CM function being lowered,
1257 // not the core function that is underneath that (i.e. func.result may be empty,
1258 // where the associated core function underneath must have a i32 status code result)
1259 let (lower_to_memory, async_flat_results) = match (async_, &func.result) {
1260 // All async cases pass along the function results and flatten where necesary
1261 (_is_async @ true, func_result) => {
1262 let results = match &func_result {
1263 Some(ty) => flat_types(self.resolve, ty, Some(max_flat_params)),
1264 None => Some(Vec::new()),
1265 };
1266 (results.is_none(), Some(results))
1267 }
1268 // All other non-async cases
1269 (_is_async @ false, _) => (sig.retptr, None),
1270 };
1271
1272 // This was dynamically allocated by the caller (or async start
1273 // function) so after it's been read by the guest we need to
1274 // deallocate it.
1275 if let AbiVariant::GuestExport
1276 | AbiVariant::GuestExportAsync
1277 | AbiVariant::GuestExportAsyncStackful = variant
1278 {
1279 if sig.indirect_params && !async_ {
1280 let ElementInfo { size, align } = self
1281 .bindgen
1282 .sizes()
1283 .record(func.params.iter().map(|param| ¶m.ty));
1284 self.emit(&Instruction::GetArg { nth: 0 });
1285 self.emit(&Instruction::GuestDeallocate { size, align });
1286 }
1287 }
1288
1289 self.realloc = Some(realloc);
1290
1291 // Perform memory lowing of relevant results, including out pointers as well as traditional results
1292 match (lower_to_memory, sig.retptr, variant) {
1293 // For sync calls, if no lowering to memory is required and there *is* a return pointer in use
1294 // then we need to lower then simply lower the result(s) and return that directly from the function.
1295 (_lower_to_memory @ false, _, _) => {
1296 if let Some(ty) = &func.result {
1297 self.lower(ty);
1298 }
1299 }
1300
1301 // Lowering to memory for a guest import
1302 //
1303 // When a function is imported to a guest this means
1304 // it's a host providing the implementation of the
1305 // import. The result is stored in the pointer
1306 // specified in the last argument, so we get the
1307 // pointer here and then write the return value into
1308 // it.
1309 (
1310 _lower_to_memory @ true,
1311 _has_ret_ptr @ true,
1312 AbiVariant::GuestImport | AbiVariant::GuestImportAsync,
1313 ) => {
1314 self.emit(&Instruction::GetArg {
1315 nth: sig.params.len() - 1,
1316 });
1317 let ptr = self
1318 .stack
1319 .pop()
1320 .expect("empty stack during result lower to memory");
1321 self.write_params_to_memory(&func.result, ptr, Default::default());
1322 }
1323
1324 // Lowering to memory for a guest export
1325 //
1326 // For a guest import this is a function defined in
1327 // wasm, so we're returning a pointer where the
1328 // value was stored at. Allocate some space here
1329 // (statically) and then write the result into that
1330 // memory, returning the pointer at the end.
1331 (_lower_to_memory @ true, _, variant) => match variant {
1332 AbiVariant::GuestExport | AbiVariant::GuestExportAsync => {
1333 let ElementInfo { size, align } =
1334 self.bindgen.sizes().params(&func.result);
1335 let ptr = self.bindgen.return_pointer(size, align);
1336 self.write_params_to_memory(
1337 &func.result,
1338 ptr.clone(),
1339 Default::default(),
1340 );
1341 self.stack.push(ptr);
1342 }
1343 AbiVariant::GuestImport | AbiVariant::GuestImportAsync => {
1344 unreachable!(
1345 "lowering to memory cannot be performed without a return pointer ({async_note} func [{func_name}], variant {variant:#?})",
1346 async_note = async_.then_some("async").unwrap_or("sync"),
1347 func_name = func.name,
1348 )
1349 }
1350 AbiVariant::GuestExportAsyncStackful => {
1351 todo!("stackful exports are not yet supported")
1352 }
1353 },
1354 }
1355
1356 // Build and emit the appropriate return
1357 match (variant, async_flat_results) {
1358 // Async guest imports always return a i32 status code
1359 (AbiVariant::GuestImport | AbiVariant::GuestImportAsync, None) if async_ => {
1360 unreachable!("async guest imports must have a return")
1361 }
1362
1363 // Async guest imports with results return the status code, not a pointer to any results
1364 (AbiVariant::GuestImport | AbiVariant::GuestImportAsync, Some(results))
1365 if async_ =>
1366 {
1367 let name = &format!("[task-return]{}", func.name);
1368 let params = results.as_deref().unwrap_or_default();
1369 self.emit(&Instruction::AsyncTaskReturn { name, params });
1370 }
1371
1372 // All async/non-async cases with results that need to be returned
1373 //
1374 // In practice, async imports should not end up here, as the returned result of an
1375 // async import is *not* a pointer but instead a status code.
1376 (_, Some(results)) => {
1377 let name = &format!("[task-return]{}", func.name);
1378 let params = results.as_deref().unwrap_or(&[WasmType::Pointer]);
1379 self.emit(&Instruction::AsyncTaskReturn { name, params });
1380 }
1381
1382 // All async/non-async cases with no results
1383 (_, None) => {
1384 if async_ {
1385 let name = &format!("[task-return]{}", func.name);
1386 self.emit(&Instruction::AsyncTaskReturn {
1387 name: name,
1388 params: if sig.results.len() > MAX_FLAT_ASYNC_PARAMS {
1389 &[WasmType::Pointer]
1390 } else {
1391 &sig.results
1392 },
1393 });
1394 } else {
1395 self.emit(&Instruction::Return {
1396 func,
1397 amt: sig.results.len(),
1398 });
1399 }
1400 }
1401 }
1402
1403 self.realloc = None;
1404 }
1405 }
1406
1407 assert!(self.realloc.is_none());
1408
1409 assert!(
1410 self.stack.is_empty(),
1411 "stack has {} items remaining: {:?}",
1412 self.stack.len(),
1413 self.stack,
1414 );
1415 }
1416
1417 fn post_return(&mut self, func: &Function) {
1418 let sig = self.resolve.wasm_signature(AbiVariant::GuestExport, func);
1419
1420 // Currently post-return is only used for lists and lists are always
1421 // returned indirectly through memory due to their flat representation
1422 // having more than one type. Assert that a return pointer is used,
1423 // though, in case this ever changes.
1424 assert!(sig.retptr);
1425
1426 self.emit(&Instruction::GetArg { nth: 0 });
1427 let addr = self.stack.pop().unwrap();
1428
1429 let mut types = Vec::new();
1430 types.extend(func.result);
1431 self.deallocate_in_types(&types, &[addr], true, Deallocate::Lists);
1432
1433 self.emit(&Instruction::Return { func, amt: 0 });
1434 }
1435
1436 fn deallocate_in_types(
1437 &mut self,
1438 types: &[Type],
1439 operands: &[B::Operand],
1440 indirect: bool,
1441 what: Deallocate,
1442 ) {
1443 if indirect {
1444 assert_eq!(operands.len(), 1);
1445 for (offset, ty) in self.bindgen.sizes().field_offsets(types) {
1446 self.deallocate_indirect(ty, operands[0].clone(), offset, what);
1447 }
1448 assert!(
1449 self.stack.is_empty(),
1450 "stack has {} items remaining",
1451 self.stack.len()
1452 );
1453 } else {
1454 let mut operands = operands;
1455 let mut operands_for_ty;
1456 for ty in types {
1457 let types = flat_types(self.resolve, ty, None).unwrap();
1458 (operands_for_ty, operands) = operands.split_at(types.len());
1459 self.stack.extend_from_slice(operands_for_ty);
1460 self.deallocate(ty, what);
1461 assert!(
1462 self.stack.is_empty(),
1463 "stack has {} items remaining",
1464 self.stack.len()
1465 );
1466 }
1467 assert!(operands.is_empty());
1468 }
1469 }
1470
1471 fn emit(&mut self, inst: &Instruction<'_>) {
1472 self.operands.clear();
1473 self.results.clear();
1474
1475 let operands_len = inst.operands_len();
1476 assert!(
1477 self.stack.len() >= operands_len,
1478 "not enough operands on stack for {:?}: have {} need {operands_len}",
1479 inst,
1480 self.stack.len(),
1481 );
1482 self.operands
1483 .extend(self.stack.drain((self.stack.len() - operands_len)..));
1484 self.results.reserve(inst.results_len());
1485
1486 self.bindgen
1487 .emit(self.resolve, inst, &mut self.operands, &mut self.results);
1488
1489 assert_eq!(
1490 self.results.len(),
1491 inst.results_len(),
1492 "{:?} expected {} results, got {}",
1493 inst,
1494 inst.results_len(),
1495 self.results.len()
1496 );
1497 self.stack.append(&mut self.results);
1498 }
1499
1500 fn push_block(&mut self) {
1501 self.bindgen.push_block();
1502 }
1503
1504 fn finish_block(&mut self, size: usize) {
1505 self.operands.clear();
1506 assert!(
1507 size <= self.stack.len(),
1508 "not enough operands on stack for finishing block",
1509 );
1510 self.operands
1511 .extend(self.stack.drain((self.stack.len() - size)..));
1512 self.bindgen.finish_block(&mut self.operands);
1513 }
1514
1515 fn lower(&mut self, ty: &Type) {
1516 use Instruction::*;
1517
1518 match *ty {
1519 Type::Bool => self.emit(&I32FromBool),
1520 Type::S8 => self.emit(&I32FromS8),
1521 Type::U8 => self.emit(&I32FromU8),
1522 Type::S16 => self.emit(&I32FromS16),
1523 Type::U16 => self.emit(&I32FromU16),
1524 Type::S32 => self.emit(&I32FromS32),
1525 Type::U32 => self.emit(&I32FromU32),
1526 Type::S64 => self.emit(&I64FromS64),
1527 Type::U64 => self.emit(&I64FromU64),
1528 Type::Char => self.emit(&I32FromChar),
1529 Type::F32 => self.emit(&CoreF32FromF32),
1530 Type::F64 => self.emit(&CoreF64FromF64),
1531 Type::String => {
1532 let realloc = self.list_realloc();
1533 self.emit(&StringLower { realloc });
1534 }
1535 Type::ErrorContext => self.emit(&ErrorContextLower),
1536 Type::Id(id) => match &self.resolve.types[id].kind {
1537 TypeDefKind::Type(t) => self.lower(t),
1538 TypeDefKind::List(element) => {
1539 let realloc = self.list_realloc();
1540 if self.bindgen.is_list_canonical(self.resolve, element) {
1541 self.emit(&ListCanonLower { element, realloc });
1542 } else {
1543 self.push_block();
1544 self.emit(&IterElem { element });
1545 self.emit(&IterBasePointer);
1546 let addr = self.stack.pop().unwrap();
1547 self.write_to_memory(element, addr, Default::default());
1548 self.finish_block(0);
1549 self.emit(&ListLower { element, realloc });
1550 }
1551 }
1552 TypeDefKind::Handle(handle) => {
1553 let (Handle::Own(ty) | Handle::Borrow(ty)) = handle;
1554 self.emit(&HandleLower {
1555 handle,
1556 ty: id,
1557 name: self.resolve.types[*ty].name.as_deref().unwrap(),
1558 });
1559 }
1560 TypeDefKind::Resource => {
1561 todo!();
1562 }
1563 TypeDefKind::Record(record) => {
1564 self.emit(&RecordLower {
1565 record,
1566 ty: id,
1567 name: self.resolve.types[id].name.as_deref().unwrap(),
1568 });
1569 let values = self
1570 .stack
1571 .drain(self.stack.len() - record.fields.len()..)
1572 .collect::<Vec<_>>();
1573 for (field, value) in record.fields.iter().zip(values) {
1574 self.stack.push(value);
1575 self.lower(&field.ty);
1576 }
1577 }
1578 TypeDefKind::Tuple(tuple) => {
1579 self.emit(&TupleLower { tuple, ty: id });
1580 let values = self
1581 .stack
1582 .drain(self.stack.len() - tuple.types.len()..)
1583 .collect::<Vec<_>>();
1584 for (ty, value) in tuple.types.iter().zip(values) {
1585 self.stack.push(value);
1586 self.lower(ty);
1587 }
1588 }
1589
1590 TypeDefKind::Flags(flags) => {
1591 self.emit(&FlagsLower {
1592 flags,
1593 ty: id,
1594 name: self.resolve.types[id].name.as_ref().unwrap(),
1595 });
1596 }
1597
1598 TypeDefKind::Variant(v) => {
1599 let results =
1600 self.lower_variant_arms(ty, v.cases.iter().map(|c| c.ty.as_ref()));
1601 self.emit(&VariantLower {
1602 variant: v,
1603 ty: id,
1604 results: &results,
1605 name: self.resolve.types[id].name.as_deref().unwrap(),
1606 });
1607 }
1608 TypeDefKind::Enum(enum_) => {
1609 self.emit(&EnumLower {
1610 enum_,
1611 ty: id,
1612 name: self.resolve.types[id].name.as_deref().unwrap(),
1613 });
1614 }
1615 TypeDefKind::Option(t) => {
1616 let results = self.lower_variant_arms(ty, [None, Some(t)]);
1617 self.emit(&OptionLower {
1618 payload: t,
1619 ty: id,
1620 results: &results,
1621 });
1622 }
1623 TypeDefKind::Result(r) => {
1624 let results = self.lower_variant_arms(ty, [r.ok.as_ref(), r.err.as_ref()]);
1625 self.emit(&ResultLower {
1626 result: r,
1627 ty: id,
1628 results: &results,
1629 });
1630 }
1631 TypeDefKind::Future(ty) => {
1632 self.emit(&FutureLower {
1633 payload: ty,
1634 ty: id,
1635 });
1636 }
1637 TypeDefKind::Stream(ty) => {
1638 self.emit(&StreamLower {
1639 payload: ty,
1640 ty: id,
1641 });
1642 }
1643 TypeDefKind::Unknown => unreachable!(),
1644 TypeDefKind::FixedLengthList(ty, size) => {
1645 self.emit(&FixedLengthListLower {
1646 element: ty,
1647 size: *size,
1648 id,
1649 });
1650 let mut values = self
1651 .stack
1652 .drain(self.stack.len() - (*size as usize)..)
1653 .collect::<Vec<_>>();
1654 for value in values.drain(..) {
1655 self.stack.push(value);
1656 self.lower(ty);
1657 }
1658 }
1659 TypeDefKind::Map(key, value) => {
1660 let realloc = self.list_realloc();
1661 let value_offset = self.bindgen.sizes().field_offsets([key, value])[1].0;
1662 self.push_block();
1663 self.emit(&IterMapKey { key });
1664 self.emit(&IterBasePointer);
1665 let key_addr = self.stack.pop().unwrap();
1666 self.write_to_memory(key, key_addr, Default::default());
1667 self.emit(&IterMapValue { value });
1668 self.emit(&IterBasePointer);
1669 let value_addr = self.stack.pop().unwrap();
1670 self.write_to_memory(value, value_addr, value_offset);
1671 self.finish_block(0);
1672 self.emit(&MapLower {
1673 key,
1674 value,
1675 realloc,
1676 });
1677 }
1678 },
1679 }
1680 }
1681
1682 fn lower_variant_arms<'b>(
1683 &mut self,
1684 ty: &Type,
1685 cases: impl IntoIterator<Item = Option<&'b Type>>,
1686 ) -> Vec<WasmType> {
1687 use Instruction::*;
1688 let results = flat_types(self.resolve, ty, None).unwrap();
1689 let mut casts = Vec::new();
1690 for (i, ty) in cases.into_iter().enumerate() {
1691 self.push_block();
1692 self.emit(&VariantPayloadName);
1693 let payload_name = self.stack.pop().unwrap();
1694 self.emit(&I32Const { val: i as i32 });
1695 let mut pushed = 1;
1696 if let Some(ty) = ty {
1697 // Using the payload of this block we lower the type to
1698 // raw wasm values.
1699 self.stack.push(payload_name);
1700 self.lower(ty);
1701
1702 // Determine the types of all the wasm values we just
1703 // pushed, and record how many. If we pushed too few
1704 // then we'll need to push some zeros after this.
1705 let temp = flat_types(self.resolve, ty, None).unwrap();
1706 pushed += temp.len();
1707
1708 // For all the types pushed we may need to insert some
1709 // bitcasts. This will go through and cast everything
1710 // to the right type to ensure all blocks produce the
1711 // same set of results.
1712 casts.truncate(0);
1713 for (actual, expected) in temp.iter().zip(&results[1..]) {
1714 casts.push(cast(*actual, *expected));
1715 }
1716 if casts.iter().any(|c| *c != Bitcast::None) {
1717 self.emit(&Bitcasts { casts: &casts });
1718 }
1719 }
1720
1721 // If we haven't pushed enough items in this block to match
1722 // what other variants are pushing then we need to push
1723 // some zeros.
1724 if pushed < results.len() {
1725 self.emit(&ConstZero {
1726 tys: &results[pushed..],
1727 });
1728 }
1729 self.finish_block(results.len());
1730 }
1731 results
1732 }
1733
1734 fn list_realloc(&self) -> Option<&'static str> {
1735 match self.realloc.expect("realloc should be configured") {
1736 Realloc::None => None,
1737 Realloc::Export(s) => Some(s),
1738 }
1739 }
1740
1741 /// Note that in general everything in this function is the opposite of the
1742 /// `lower` function above. This is intentional and should be kept this way!
1743 fn lift(&mut self, ty: &Type) {
1744 use Instruction::*;
1745
1746 match *ty {
1747 Type::Bool => self.emit(&BoolFromI32),
1748 Type::S8 => self.emit(&S8FromI32),
1749 Type::U8 => self.emit(&U8FromI32),
1750 Type::S16 => self.emit(&S16FromI32),
1751 Type::U16 => self.emit(&U16FromI32),
1752 Type::S32 => self.emit(&S32FromI32),
1753 Type::U32 => self.emit(&U32FromI32),
1754 Type::S64 => self.emit(&S64FromI64),
1755 Type::U64 => self.emit(&U64FromI64),
1756 Type::Char => self.emit(&CharFromI32),
1757 Type::F32 => self.emit(&F32FromCoreF32),
1758 Type::F64 => self.emit(&F64FromCoreF64),
1759 Type::String => self.emit(&StringLift),
1760 Type::ErrorContext => self.emit(&ErrorContextLift),
1761 Type::Id(id) => match &self.resolve.types[id].kind {
1762 TypeDefKind::Type(t) => self.lift(t),
1763 TypeDefKind::List(element) => {
1764 if self.bindgen.is_list_canonical(self.resolve, element) {
1765 self.emit(&ListCanonLift { element, ty: id });
1766 } else {
1767 self.push_block();
1768 self.emit(&IterBasePointer);
1769 let addr = self.stack.pop().unwrap();
1770 self.read_from_memory(element, addr, Default::default());
1771 self.finish_block(1);
1772 self.emit(&ListLift { element, ty: id });
1773 }
1774 }
1775 TypeDefKind::Handle(handle) => {
1776 let (Handle::Own(ty) | Handle::Borrow(ty)) = handle;
1777 self.emit(&HandleLift {
1778 handle,
1779 ty: id,
1780 name: self.resolve.types[*ty].name.as_deref().unwrap(),
1781 });
1782 }
1783 TypeDefKind::Resource => {
1784 todo!();
1785 }
1786 TypeDefKind::Record(record) => {
1787 self.flat_for_each_record_type(
1788 ty,
1789 record.fields.iter().map(|f| &f.ty),
1790 Self::lift,
1791 );
1792 self.emit(&RecordLift {
1793 record,
1794 ty: id,
1795 name: self.resolve.types[id].name.as_deref().unwrap(),
1796 });
1797 }
1798 TypeDefKind::Tuple(tuple) => {
1799 self.flat_for_each_record_type(ty, tuple.types.iter(), Self::lift);
1800 self.emit(&TupleLift { tuple, ty: id });
1801 }
1802 TypeDefKind::Flags(flags) => {
1803 self.emit(&FlagsLift {
1804 flags,
1805 ty: id,
1806 name: self.resolve.types[id].name.as_ref().unwrap(),
1807 });
1808 }
1809
1810 TypeDefKind::Variant(v) => {
1811 self.flat_for_each_variant_arm(
1812 ty,
1813 true,
1814 v.cases.iter().map(|c| c.ty.as_ref()),
1815 Self::lift,
1816 );
1817 self.emit(&VariantLift {
1818 variant: v,
1819 ty: id,
1820 name: self.resolve.types[id].name.as_deref().unwrap(),
1821 });
1822 }
1823
1824 TypeDefKind::Enum(enum_) => {
1825 self.emit(&EnumLift {
1826 enum_,
1827 ty: id,
1828 name: self.resolve.types[id].name.as_deref().unwrap(),
1829 });
1830 }
1831
1832 TypeDefKind::Option(t) => {
1833 self.flat_for_each_variant_arm(ty, true, [None, Some(t)], Self::lift);
1834 self.emit(&OptionLift { payload: t, ty: id });
1835 }
1836
1837 TypeDefKind::Result(r) => {
1838 self.flat_for_each_variant_arm(
1839 ty,
1840 true,
1841 [r.ok.as_ref(), r.err.as_ref()],
1842 Self::lift,
1843 );
1844 self.emit(&ResultLift { result: r, ty: id });
1845 }
1846
1847 TypeDefKind::Future(ty) => {
1848 self.emit(&FutureLift {
1849 payload: ty,
1850 ty: id,
1851 });
1852 }
1853 TypeDefKind::Stream(ty) => {
1854 self.emit(&StreamLift {
1855 payload: ty,
1856 ty: id,
1857 });
1858 }
1859 TypeDefKind::Unknown => unreachable!(),
1860 TypeDefKind::FixedLengthList(ty, size) => {
1861 let temp = flat_types(self.resolve, ty, None).unwrap();
1862 let flat_per_elem = temp.to_vec().len();
1863 let flatsize = flat_per_elem * (*size as usize);
1864 let mut lowered_args = self
1865 .stack
1866 .drain(self.stack.len() - flatsize..)
1867 .collect::<Vec<_>>();
1868 for _ in 0..*size {
1869 self.stack.extend(lowered_args.drain(..flat_per_elem));
1870 self.lift(ty);
1871 }
1872 self.emit(&FixedLengthListLift {
1873 element: ty,
1874 size: *size,
1875 id,
1876 });
1877 }
1878 TypeDefKind::Map(key, value) => {
1879 let value_offset = self.bindgen.sizes().field_offsets([key, value])[1].0;
1880 self.push_block();
1881 self.emit(&IterBasePointer);
1882 let entry_addr = self.stack.pop().unwrap();
1883 self.read_from_memory(key, entry_addr.clone(), Default::default());
1884 self.read_from_memory(value, entry_addr, value_offset);
1885 self.finish_block(2);
1886 self.emit(&MapLift { key, value, ty: id });
1887 }
1888 },
1889 }
1890 }
1891
1892 fn flat_for_each_record_type<'b>(
1893 &mut self,
1894 container: &Type,
1895 types: impl Iterator<Item = &'b Type>,
1896 mut iter: impl FnMut(&mut Self, &Type),
1897 ) {
1898 let temp = flat_types(self.resolve, container, None).unwrap();
1899 let mut args = self
1900 .stack
1901 .drain(self.stack.len() - temp.len()..)
1902 .collect::<Vec<_>>();
1903 for ty in types {
1904 let temp = flat_types(self.resolve, ty, None).unwrap();
1905 self.stack.extend(args.drain(..temp.len()));
1906 iter(self, ty);
1907 }
1908 }
1909
1910 fn flat_for_each_variant_arm<'b>(
1911 &mut self,
1912 ty: &Type,
1913 blocks_with_type_have_result: bool,
1914 cases: impl IntoIterator<Item = Option<&'b Type>>,
1915 mut iter: impl FnMut(&mut Self, &Type),
1916 ) {
1917 let params = flat_types(self.resolve, ty, None).unwrap();
1918 let mut casts = Vec::new();
1919 let block_inputs = self
1920 .stack
1921 .drain(self.stack.len() + 1 - params.len()..)
1922 .collect::<Vec<_>>();
1923 for ty in cases {
1924 self.push_block();
1925 if let Some(ty) = ty {
1926 // Push only the values we need for this variant onto
1927 // the stack.
1928 let temp = flat_types(self.resolve, ty, None).unwrap();
1929 self.stack
1930 .extend(block_inputs[..temp.len()].iter().cloned());
1931
1932 // Cast all the types we have on the stack to the actual
1933 // types needed for this variant, if necessary.
1934 casts.truncate(0);
1935 for (actual, expected) in temp.iter().zip(¶ms[1..]) {
1936 casts.push(cast(*expected, *actual));
1937 }
1938 if casts.iter().any(|c| *c != Bitcast::None) {
1939 self.emit(&Instruction::Bitcasts { casts: &casts });
1940 }
1941
1942 // Then recursively lift this variant's payload.
1943 iter(self, ty);
1944 }
1945 self.finish_block(if blocks_with_type_have_result {
1946 ty.is_some() as usize
1947 } else {
1948 0
1949 });
1950 }
1951 }
1952
1953 fn write_to_memory(&mut self, ty: &Type, addr: B::Operand, offset: ArchitectureSize) {
1954 use Instruction::*;
1955
1956 match *ty {
1957 // Builtin types need different flavors of storage instructions
1958 // depending on the size of the value written.
1959 Type::Bool | Type::U8 | Type::S8 => {
1960 self.lower_and_emit(ty, addr, &I32Store8 { offset })
1961 }
1962 Type::U16 | Type::S16 => self.lower_and_emit(ty, addr, &I32Store16 { offset }),
1963 Type::U32 | Type::S32 | Type::Char => {
1964 self.lower_and_emit(ty, addr, &I32Store { offset })
1965 }
1966 Type::U64 | Type::S64 => self.lower_and_emit(ty, addr, &I64Store { offset }),
1967 Type::F32 => self.lower_and_emit(ty, addr, &F32Store { offset }),
1968 Type::F64 => self.lower_and_emit(ty, addr, &F64Store { offset }),
1969 Type::String => self.write_list_to_memory(ty, addr, offset),
1970 Type::ErrorContext => self.lower_and_emit(ty, addr, &I32Store { offset }),
1971
1972 Type::Id(id) => match &self.resolve.types[id].kind {
1973 TypeDefKind::Type(t) => self.write_to_memory(t, addr, offset),
1974 TypeDefKind::List(_) => self.write_list_to_memory(ty, addr, offset),
1975 // Maps have the same linear memory layout as list<tuple<K, V>>.
1976 TypeDefKind::Map(_, _) => self.write_list_to_memory(ty, addr, offset),
1977
1978 TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::Handle(_) => {
1979 self.lower_and_emit(ty, addr, &I32Store { offset })
1980 }
1981
1982 // Decompose the record into its components and then write all
1983 // the components into memory one-by-one.
1984 TypeDefKind::Record(record) => {
1985 self.emit(&RecordLower {
1986 record,
1987 ty: id,
1988 name: self.resolve.types[id].name.as_deref().unwrap(),
1989 });
1990 self.write_fields_to_memory(record.fields.iter().map(|f| &f.ty), addr, offset);
1991 }
1992 TypeDefKind::Resource => {
1993 todo!()
1994 }
1995 TypeDefKind::Tuple(tuple) => {
1996 self.emit(&TupleLower { tuple, ty: id });
1997 self.write_fields_to_memory(tuple.types.iter(), addr, offset);
1998 }
1999
2000 TypeDefKind::Flags(f) => {
2001 self.lower(ty);
2002 match f.repr() {
2003 FlagsRepr::U8 => {
2004 self.stack.push(addr);
2005 self.store_intrepr(offset, Int::U8);
2006 }
2007 FlagsRepr::U16 => {
2008 self.stack.push(addr);
2009 self.store_intrepr(offset, Int::U16);
2010 }
2011 FlagsRepr::U32(n) => {
2012 for i in (0..n).rev() {
2013 self.stack.push(addr.clone());
2014 self.emit(&I32Store {
2015 offset: offset.add_bytes(i * 4),
2016 });
2017 }
2018 }
2019 }
2020 }
2021
2022 // Each case will get its own block, and the first item in each
2023 // case is writing the discriminant. After that if we have a
2024 // payload we write the payload after the discriminant, aligned up
2025 // to the type's alignment.
2026 TypeDefKind::Variant(v) => {
2027 self.write_variant_arms_to_memory(
2028 offset,
2029 addr,
2030 v.tag(),
2031 v.cases.iter().map(|c| c.ty.as_ref()),
2032 );
2033 self.emit(&VariantLower {
2034 variant: v,
2035 ty: id,
2036 results: &[],
2037 name: self.resolve.types[id].name.as_deref().unwrap(),
2038 });
2039 }
2040
2041 TypeDefKind::Option(t) => {
2042 self.write_variant_arms_to_memory(offset, addr, Int::U8, [None, Some(t)]);
2043 self.emit(&OptionLower {
2044 payload: t,
2045 ty: id,
2046 results: &[],
2047 });
2048 }
2049
2050 TypeDefKind::Result(r) => {
2051 self.write_variant_arms_to_memory(
2052 offset,
2053 addr,
2054 Int::U8,
2055 [r.ok.as_ref(), r.err.as_ref()],
2056 );
2057 self.emit(&ResultLower {
2058 result: r,
2059 ty: id,
2060 results: &[],
2061 });
2062 }
2063
2064 TypeDefKind::Enum(e) => {
2065 self.lower(ty);
2066 self.stack.push(addr);
2067 self.store_intrepr(offset, e.tag());
2068 }
2069
2070 TypeDefKind::Unknown => unreachable!(),
2071 TypeDefKind::FixedLengthList(element, size) => {
2072 // resembles write_list_to_memory
2073 self.push_block();
2074 self.emit(&IterElem { element });
2075 self.emit(&IterBasePointer);
2076 let elem_addr = self.stack.pop().unwrap();
2077 self.write_to_memory(element, elem_addr, offset);
2078 self.finish_block(0);
2079 self.stack.push(addr);
2080 self.emit(&FixedLengthListLowerToMemory {
2081 element,
2082 size: *size,
2083 id,
2084 });
2085 }
2086 },
2087 }
2088 }
2089
2090 fn write_params_to_memory<'b>(
2091 &mut self,
2092 params: impl IntoIterator<Item = &'b Type, IntoIter: ExactSizeIterator>,
2093 addr: B::Operand,
2094 offset: ArchitectureSize,
2095 ) {
2096 self.write_fields_to_memory(params, addr, offset);
2097 }
2098
2099 fn write_variant_arms_to_memory<'b>(
2100 &mut self,
2101 offset: ArchitectureSize,
2102 addr: B::Operand,
2103 tag: Int,
2104 cases: impl IntoIterator<Item = Option<&'b Type>> + Clone,
2105 ) {
2106 let payload_offset = offset + (self.bindgen.sizes().payload_offset(tag, cases.clone()));
2107 for (i, ty) in cases.into_iter().enumerate() {
2108 self.push_block();
2109 self.emit(&Instruction::VariantPayloadName);
2110 let payload_name = self.stack.pop().unwrap();
2111 self.emit(&Instruction::I32Const { val: i as i32 });
2112 self.stack.push(addr.clone());
2113 self.store_intrepr(offset, tag);
2114 if let Some(ty) = ty {
2115 self.stack.push(payload_name.clone());
2116 self.write_to_memory(ty, addr.clone(), payload_offset);
2117 }
2118 self.finish_block(0);
2119 }
2120 }
2121
2122 fn write_list_to_memory(&mut self, ty: &Type, addr: B::Operand, offset: ArchitectureSize) {
2123 // After lowering the list there's two i32 values on the stack
2124 // which we write into memory, writing the pointer into the low address
2125 // and the length into the high address.
2126 self.lower(ty);
2127 self.stack.push(addr.clone());
2128 self.emit(&Instruction::LengthStore {
2129 offset: offset + self.bindgen.sizes().align(ty).into(),
2130 });
2131 self.stack.push(addr);
2132 self.emit(&Instruction::PointerStore { offset });
2133 }
2134
2135 fn write_fields_to_memory<'b>(
2136 &mut self,
2137 tys: impl IntoIterator<Item = &'b Type, IntoIter: ExactSizeIterator>,
2138 addr: B::Operand,
2139 offset: ArchitectureSize,
2140 ) {
2141 let tys = tys.into_iter();
2142 let fields = self
2143 .stack
2144 .drain(self.stack.len() - tys.len()..)
2145 .collect::<Vec<_>>();
2146 for ((field_offset, ty), op) in self
2147 .bindgen
2148 .sizes()
2149 .field_offsets(tys)
2150 .into_iter()
2151 .zip(fields)
2152 {
2153 self.stack.push(op);
2154 self.write_to_memory(ty, addr.clone(), offset + (field_offset));
2155 }
2156 }
2157
2158 fn lower_and_emit(&mut self, ty: &Type, addr: B::Operand, instr: &Instruction) {
2159 self.lower(ty);
2160 self.stack.push(addr);
2161 self.emit(instr);
2162 }
2163
2164 fn read_from_memory(&mut self, ty: &Type, addr: B::Operand, offset: ArchitectureSize) {
2165 use Instruction::*;
2166
2167 match *ty {
2168 Type::Bool => self.emit_and_lift(ty, addr, &I32Load8U { offset }),
2169 Type::U8 => self.emit_and_lift(ty, addr, &I32Load8U { offset }),
2170 Type::S8 => self.emit_and_lift(ty, addr, &I32Load8S { offset }),
2171 Type::U16 => self.emit_and_lift(ty, addr, &I32Load16U { offset }),
2172 Type::S16 => self.emit_and_lift(ty, addr, &I32Load16S { offset }),
2173 Type::U32 | Type::S32 | Type::Char => self.emit_and_lift(ty, addr, &I32Load { offset }),
2174 Type::U64 | Type::S64 => self.emit_and_lift(ty, addr, &I64Load { offset }),
2175 Type::F32 => self.emit_and_lift(ty, addr, &F32Load { offset }),
2176 Type::F64 => self.emit_and_lift(ty, addr, &F64Load { offset }),
2177 Type::String => self.read_list_from_memory(ty, addr, offset),
2178 Type::ErrorContext => self.emit_and_lift(ty, addr, &I32Load { offset }),
2179
2180 Type::Id(id) => match &self.resolve.types[id].kind {
2181 TypeDefKind::Type(t) => self.read_from_memory(t, addr, offset),
2182
2183 TypeDefKind::List(_) => self.read_list_from_memory(ty, addr, offset),
2184 // Maps have the same linear memory layout as list<tuple<K, V>>.
2185 TypeDefKind::Map(_, _) => self.read_list_from_memory(ty, addr, offset),
2186
2187 TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::Handle(_) => {
2188 self.emit_and_lift(ty, addr, &I32Load { offset })
2189 }
2190
2191 TypeDefKind::Resource => {
2192 todo!();
2193 }
2194
2195 // Read and lift each field individually, adjusting the offset
2196 // as we go along, then aggregate all the fields into the
2197 // record.
2198 TypeDefKind::Record(record) => {
2199 self.read_fields_from_memory(record.fields.iter().map(|f| &f.ty), addr, offset);
2200 self.emit(&RecordLift {
2201 record,
2202 ty: id,
2203 name: self.resolve.types[id].name.as_deref().unwrap(),
2204 });
2205 }
2206
2207 TypeDefKind::Tuple(tuple) => {
2208 self.read_fields_from_memory(&tuple.types, addr, offset);
2209 self.emit(&TupleLift { tuple, ty: id });
2210 }
2211
2212 TypeDefKind::Flags(f) => {
2213 match f.repr() {
2214 FlagsRepr::U8 => {
2215 self.stack.push(addr);
2216 self.load_intrepr(offset, Int::U8);
2217 }
2218 FlagsRepr::U16 => {
2219 self.stack.push(addr);
2220 self.load_intrepr(offset, Int::U16);
2221 }
2222 FlagsRepr::U32(n) => {
2223 for i in 0..n {
2224 self.stack.push(addr.clone());
2225 self.emit(&I32Load {
2226 offset: offset.add_bytes(i * 4),
2227 });
2228 }
2229 }
2230 }
2231 self.lift(ty);
2232 }
2233
2234 // Each case will get its own block, and we'll dispatch to the
2235 // right block based on the `i32.load` we initially perform. Each
2236 // individual block is pretty simple and just reads the payload type
2237 // from the corresponding offset if one is available.
2238 TypeDefKind::Variant(variant) => {
2239 self.read_variant_arms_from_memory(
2240 offset,
2241 addr,
2242 variant.tag(),
2243 variant.cases.iter().map(|c| c.ty.as_ref()),
2244 );
2245 self.emit(&VariantLift {
2246 variant,
2247 ty: id,
2248 name: self.resolve.types[id].name.as_deref().unwrap(),
2249 });
2250 }
2251
2252 TypeDefKind::Option(t) => {
2253 self.read_variant_arms_from_memory(offset, addr, Int::U8, [None, Some(t)]);
2254 self.emit(&OptionLift { payload: t, ty: id });
2255 }
2256
2257 TypeDefKind::Result(r) => {
2258 self.read_variant_arms_from_memory(
2259 offset,
2260 addr,
2261 Int::U8,
2262 [r.ok.as_ref(), r.err.as_ref()],
2263 );
2264 self.emit(&ResultLift { result: r, ty: id });
2265 }
2266
2267 TypeDefKind::Enum(e) => {
2268 self.stack.push(addr.clone());
2269 self.load_intrepr(offset, e.tag());
2270 self.lift(ty);
2271 }
2272
2273 TypeDefKind::Unknown => unreachable!(),
2274 TypeDefKind::FixedLengthList(ty, size) => {
2275 self.push_block();
2276 self.emit(&IterBasePointer);
2277 let elemaddr = self.stack.pop().unwrap();
2278 self.read_from_memory(ty, elemaddr, offset);
2279 self.finish_block(1);
2280 self.stack.push(addr.clone());
2281 self.emit(&FixedLengthListLiftFromMemory {
2282 element: ty,
2283 size: *size,
2284 id,
2285 });
2286 }
2287 },
2288 }
2289 }
2290
2291 fn read_results_from_memory(
2292 &mut self,
2293 result: &Option<Type>,
2294 addr: B::Operand,
2295 offset: ArchitectureSize,
2296 ) {
2297 self.read_fields_from_memory(result, addr, offset)
2298 }
2299
2300 fn read_variant_arms_from_memory<'b>(
2301 &mut self,
2302 offset: ArchitectureSize,
2303 addr: B::Operand,
2304 tag: Int,
2305 cases: impl IntoIterator<Item = Option<&'b Type>> + Clone,
2306 ) {
2307 self.stack.push(addr.clone());
2308 self.load_intrepr(offset, tag);
2309 let payload_offset = offset + (self.bindgen.sizes().payload_offset(tag, cases.clone()));
2310 for ty in cases {
2311 self.push_block();
2312 if let Some(ty) = ty {
2313 self.read_from_memory(ty, addr.clone(), payload_offset);
2314 }
2315 self.finish_block(ty.is_some() as usize);
2316 }
2317 }
2318
2319 fn read_list_from_memory(&mut self, ty: &Type, addr: B::Operand, offset: ArchitectureSize) {
2320 // Read the pointer/len and then perform the standard lifting
2321 // proceses.
2322 self.stack.push(addr.clone());
2323 self.emit(&Instruction::PointerLoad { offset });
2324 self.stack.push(addr);
2325 self.emit(&Instruction::LengthLoad {
2326 offset: offset + self.bindgen.sizes().align(ty).into(),
2327 });
2328 self.lift(ty);
2329 }
2330
2331 fn read_fields_from_memory<'b>(
2332 &mut self,
2333 tys: impl IntoIterator<Item = &'b Type>,
2334 addr: B::Operand,
2335 offset: ArchitectureSize,
2336 ) {
2337 for (field_offset, ty) in self.bindgen.sizes().field_offsets(tys).iter() {
2338 self.read_from_memory(ty, addr.clone(), offset + (*field_offset));
2339 }
2340 }
2341
2342 fn emit_and_lift(&mut self, ty: &Type, addr: B::Operand, instr: &Instruction) {
2343 self.stack.push(addr);
2344 self.emit(instr);
2345 self.lift(ty);
2346 }
2347
2348 fn load_intrepr(&mut self, offset: ArchitectureSize, repr: Int) {
2349 self.emit(&match repr {
2350 Int::U64 => Instruction::I64Load { offset },
2351 Int::U32 => Instruction::I32Load { offset },
2352 Int::U16 => Instruction::I32Load16U { offset },
2353 Int::U8 => Instruction::I32Load8U { offset },
2354 });
2355 }
2356
2357 fn store_intrepr(&mut self, offset: ArchitectureSize, repr: Int) {
2358 self.emit(&match repr {
2359 Int::U64 => Instruction::I64Store { offset },
2360 Int::U32 => Instruction::I32Store { offset },
2361 Int::U16 => Instruction::I32Store16 { offset },
2362 Int::U8 => Instruction::I32Store8 { offset },
2363 });
2364 }
2365
2366 /// Runs the deallocation of `ty` for the operands currently on
2367 /// `self.stack`.
2368 ///
2369 /// This will pop the ABI items of `ty` from `self.stack`.
2370 fn deallocate(&mut self, ty: &Type, what: Deallocate) {
2371 use Instruction::*;
2372
2373 match *ty {
2374 Type::String => {
2375 self.emit(&Instruction::GuestDeallocateString);
2376 }
2377
2378 Type::Bool
2379 | Type::U8
2380 | Type::S8
2381 | Type::U16
2382 | Type::S16
2383 | Type::U32
2384 | Type::S32
2385 | Type::Char
2386 | Type::U64
2387 | Type::S64
2388 | Type::F32
2389 | Type::F64
2390 | Type::ErrorContext => {
2391 // No deallocation necessary, just discard the operand on the
2392 // stack.
2393 self.stack.pop().unwrap();
2394 }
2395
2396 Type::Id(id) => match &self.resolve.types[id].kind {
2397 TypeDefKind::Type(t) => self.deallocate(t, what),
2398
2399 TypeDefKind::List(element) => {
2400 self.push_block();
2401 self.emit(&IterBasePointer);
2402 let elemaddr = self.stack.pop().unwrap();
2403 self.deallocate_indirect(element, elemaddr, Default::default(), what);
2404 self.finish_block(0);
2405
2406 self.emit(&Instruction::GuestDeallocateList { element });
2407 }
2408
2409 TypeDefKind::Map(key, value) => {
2410 let value_offset = self.bindgen.sizes().field_offsets([key, value])[1].0;
2411 self.push_block();
2412 self.emit(&IterBasePointer);
2413 let entry_addr = self.stack.pop().unwrap();
2414 self.deallocate_indirect(key, entry_addr.clone(), Default::default(), what);
2415 self.deallocate_indirect(value, entry_addr, value_offset, what);
2416 self.finish_block(0);
2417
2418 self.emit(&Instruction::GuestDeallocateMap { key, value });
2419 }
2420
2421 TypeDefKind::Handle(Handle::Own(_))
2422 | TypeDefKind::Future(_)
2423 | TypeDefKind::Stream(_)
2424 if what.handles() =>
2425 {
2426 self.lift(ty);
2427 self.emit(&DropHandle { ty });
2428 }
2429
2430 TypeDefKind::Record(record) => {
2431 self.flat_for_each_record_type(
2432 ty,
2433 record.fields.iter().map(|f| &f.ty),
2434 |me, ty| me.deallocate(ty, what),
2435 );
2436 }
2437
2438 TypeDefKind::Tuple(tuple) => {
2439 self.flat_for_each_record_type(ty, tuple.types.iter(), |me, ty| {
2440 me.deallocate(ty, what)
2441 });
2442 }
2443
2444 TypeDefKind::Variant(variant) => {
2445 self.flat_for_each_variant_arm(
2446 ty,
2447 false,
2448 variant.cases.iter().map(|c| c.ty.as_ref()),
2449 |me, ty| me.deallocate(ty, what),
2450 );
2451 self.emit(&GuestDeallocateVariant {
2452 blocks: variant.cases.len(),
2453 });
2454 }
2455
2456 TypeDefKind::Option(t) => {
2457 self.flat_for_each_variant_arm(ty, false, [None, Some(t)], |me, ty| {
2458 me.deallocate(ty, what)
2459 });
2460 self.emit(&GuestDeallocateVariant { blocks: 2 });
2461 }
2462
2463 TypeDefKind::Result(e) => {
2464 self.flat_for_each_variant_arm(
2465 ty,
2466 false,
2467 [e.ok.as_ref(), e.err.as_ref()],
2468 |me, ty| me.deallocate(ty, what),
2469 );
2470 self.emit(&GuestDeallocateVariant { blocks: 2 });
2471 }
2472
2473 // discard the operand on the stack, otherwise nothing to free.
2474 TypeDefKind::Flags(_)
2475 | TypeDefKind::Enum(_)
2476 | TypeDefKind::Future(_)
2477 | TypeDefKind::Stream(_)
2478 | TypeDefKind::Handle(Handle::Own(_))
2479 | TypeDefKind::Handle(Handle::Borrow(_)) => {
2480 self.stack.pop().unwrap();
2481 }
2482
2483 TypeDefKind::Resource => unreachable!(),
2484 TypeDefKind::Unknown => unreachable!(),
2485
2486 TypeDefKind::FixedLengthList(..) => todo!(),
2487 },
2488 }
2489 }
2490
2491 fn deallocate_indirect(
2492 &mut self,
2493 ty: &Type,
2494 addr: B::Operand,
2495 offset: ArchitectureSize,
2496 what: Deallocate,
2497 ) {
2498 use Instruction::*;
2499
2500 // No need to execute any instructions if this type itself doesn't
2501 // require any form of post-return.
2502 if !needs_deallocate(self.resolve, ty, what) {
2503 return;
2504 }
2505
2506 match *ty {
2507 Type::String => {
2508 self.stack.push(addr.clone());
2509 self.emit(&Instruction::PointerLoad { offset });
2510 self.stack.push(addr);
2511 self.emit(&Instruction::LengthLoad {
2512 offset: offset + self.bindgen.sizes().align(ty).into(),
2513 });
2514 self.deallocate(ty, what);
2515 }
2516
2517 Type::Bool
2518 | Type::U8
2519 | Type::S8
2520 | Type::U16
2521 | Type::S16
2522 | Type::U32
2523 | Type::S32
2524 | Type::Char
2525 | Type::U64
2526 | Type::S64
2527 | Type::F32
2528 | Type::F64
2529 | Type::ErrorContext => {}
2530
2531 Type::Id(id) => match &self.resolve.types[id].kind {
2532 TypeDefKind::Type(t) => self.deallocate_indirect(t, addr, offset, what),
2533
2534 TypeDefKind::List(_) => {
2535 self.stack.push(addr.clone());
2536 self.emit(&Instruction::PointerLoad { offset });
2537 self.stack.push(addr);
2538 self.emit(&Instruction::LengthLoad {
2539 offset: offset + self.bindgen.sizes().align(ty).into(),
2540 });
2541
2542 self.deallocate(ty, what);
2543 }
2544
2545 TypeDefKind::Map(_, _) => {
2546 self.stack.push(addr.clone());
2547 self.emit(&Instruction::PointerLoad { offset });
2548 self.stack.push(addr);
2549 self.emit(&Instruction::LengthLoad {
2550 offset: offset + self.bindgen.sizes().align(ty).into(),
2551 });
2552
2553 self.deallocate(ty, what);
2554 }
2555
2556 TypeDefKind::Handle(Handle::Own(_))
2557 | TypeDefKind::Future(_)
2558 | TypeDefKind::Stream(_)
2559 if what.handles() =>
2560 {
2561 self.read_from_memory(ty, addr, offset);
2562 self.emit(&DropHandle { ty });
2563 }
2564
2565 TypeDefKind::Handle(Handle::Own(_)) => unreachable!(),
2566 TypeDefKind::Handle(Handle::Borrow(_)) => unreachable!(),
2567 TypeDefKind::Resource => unreachable!(),
2568
2569 TypeDefKind::Record(record) => {
2570 self.deallocate_indirect_fields(
2571 &record.fields.iter().map(|f| f.ty).collect::<Vec<_>>(),
2572 addr,
2573 offset,
2574 what,
2575 );
2576 }
2577
2578 TypeDefKind::Tuple(tuple) => {
2579 self.deallocate_indirect_fields(&tuple.types, addr, offset, what);
2580 }
2581
2582 TypeDefKind::Flags(_) => {}
2583
2584 TypeDefKind::Variant(variant) => {
2585 self.deallocate_indirect_variant(
2586 offset,
2587 addr,
2588 variant.tag(),
2589 variant.cases.iter().map(|c| c.ty.as_ref()),
2590 what,
2591 );
2592 self.emit(&GuestDeallocateVariant {
2593 blocks: variant.cases.len(),
2594 });
2595 }
2596
2597 TypeDefKind::Option(t) => {
2598 self.deallocate_indirect_variant(offset, addr, Int::U8, [None, Some(t)], what);
2599 self.emit(&GuestDeallocateVariant { blocks: 2 });
2600 }
2601
2602 TypeDefKind::Result(e) => {
2603 self.deallocate_indirect_variant(
2604 offset,
2605 addr,
2606 Int::U8,
2607 [e.ok.as_ref(), e.err.as_ref()],
2608 what,
2609 );
2610 self.emit(&GuestDeallocateVariant { blocks: 2 });
2611 }
2612
2613 TypeDefKind::Enum(_) => {}
2614
2615 TypeDefKind::Future(_) => unreachable!(),
2616 TypeDefKind::Stream(_) => unreachable!(),
2617 TypeDefKind::Unknown => unreachable!(),
2618 TypeDefKind::FixedLengthList(_, _) => {}
2619 },
2620 }
2621 }
2622
2623 fn deallocate_indirect_variant<'b>(
2624 &mut self,
2625 offset: ArchitectureSize,
2626 addr: B::Operand,
2627 tag: Int,
2628 cases: impl IntoIterator<Item = Option<&'b Type>> + Clone,
2629 what: Deallocate,
2630 ) {
2631 self.stack.push(addr.clone());
2632 self.load_intrepr(offset, tag);
2633 let payload_offset = offset + (self.bindgen.sizes().payload_offset(tag, cases.clone()));
2634 for ty in cases {
2635 self.push_block();
2636 if let Some(ty) = ty {
2637 self.deallocate_indirect(ty, addr.clone(), payload_offset, what);
2638 }
2639 self.finish_block(0);
2640 }
2641 }
2642
2643 fn deallocate_indirect_fields(
2644 &mut self,
2645 tys: &[Type],
2646 addr: B::Operand,
2647 offset: ArchitectureSize,
2648 what: Deallocate,
2649 ) {
2650 for (field_offset, ty) in self.bindgen.sizes().field_offsets(tys) {
2651 self.deallocate_indirect(ty, addr.clone(), offset + (field_offset), what);
2652 }
2653 }
2654}
2655
2656/// Returns the [`Bitcast`] required to move a value of flat type
2657/// `from` into flat type `to` under the canonical ABI's unification
2658/// rules. Panics if the pair isn't a legal bitcast per the spec.
2659pub fn cast(from: WasmType, to: WasmType) -> Bitcast {
2660 use WasmType::*;
2661
2662 match (from, to) {
2663 (I32, I32)
2664 | (I64, I64)
2665 | (F32, F32)
2666 | (F64, F64)
2667 | (Pointer, Pointer)
2668 | (PointerOrI64, PointerOrI64)
2669 | (Length, Length) => Bitcast::None,
2670
2671 (I32, I64) => Bitcast::I32ToI64,
2672 (F32, I32) => Bitcast::F32ToI32,
2673 (F64, I64) => Bitcast::F64ToI64,
2674
2675 (I64, I32) => Bitcast::I64ToI32,
2676 (I32, F32) => Bitcast::I32ToF32,
2677 (I64, F64) => Bitcast::I64ToF64,
2678
2679 (F32, I64) => Bitcast::F32ToI64,
2680 (I64, F32) => Bitcast::I64ToF32,
2681
2682 (I64, PointerOrI64) => Bitcast::I64ToP64,
2683 (Pointer, PointerOrI64) => Bitcast::PToP64,
2684 (_, PointerOrI64) => {
2685 Bitcast::Sequence(Box::new([cast(from, I64), cast(I64, PointerOrI64)]))
2686 }
2687
2688 (PointerOrI64, I64) => Bitcast::P64ToI64,
2689 (PointerOrI64, Pointer) => Bitcast::P64ToP,
2690 (PointerOrI64, _) => Bitcast::Sequence(Box::new([cast(PointerOrI64, I64), cast(I64, to)])),
2691
2692 (I32, Pointer) => Bitcast::I32ToP,
2693 (Pointer, I32) => Bitcast::PToI32,
2694 (I32, Length) => Bitcast::I32ToL,
2695 (Length, I32) => Bitcast::LToI32,
2696 (I64, Length) => Bitcast::I64ToL,
2697 (Length, I64) => Bitcast::LToI64,
2698 (Pointer, Length) => Bitcast::PToL,
2699 (Length, Pointer) => Bitcast::LToP,
2700
2701 (F32, Pointer | Length) => Bitcast::Sequence(Box::new([cast(F32, I32), cast(I32, to)])),
2702 (Pointer | Length, F32) => Bitcast::Sequence(Box::new([cast(from, I32), cast(I32, F32)])),
2703
2704 (F32, F64)
2705 | (F64, F32)
2706 | (F64, I32)
2707 | (I32, F64)
2708 | (Pointer | Length, I64 | F64)
2709 | (I64 | F64, Pointer | Length) => {
2710 unreachable!("Don't know how to bitcast from {:?} to {:?}", from, to);
2711 }
2712 }
2713}
2714
2715/// Flatten a component-level type into its canonical-ABI wasm-type
2716/// sequence, or return `None` if the result would exceed `max_params`.
2717///
2718/// `max_params` defaults to [`Resolve::MAX_FLAT_PARAMS`] (16). It is
2719/// sometimes necessary to restrict the cap dynamically — for example
2720/// during an async guest-import call, where flat params are limited
2721/// to 4.
2722pub fn flat_types(
2723 resolve: &Resolve,
2724 ty: &Type,
2725 max_params: Option<usize>,
2726) -> Option<Vec<WasmType>> {
2727 let max_params = max_params.unwrap_or(MAX_FLAT_PARAMS);
2728 let mut storage = iter::repeat_n(WasmType::I32, max_params).collect::<Vec<_>>();
2729 let mut flat = FlatTypes::new(storage.as_mut_slice());
2730 resolve.push_flat(ty, &mut flat).then_some(flat.to_vec())
2731}