1use crate::component::{
19 CanonicalAbiInfo, ComponentTypesBuilder, FLAG_MAY_LEAVE, FixedEncoding as FE, FlatType,
20 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, TypeFixedLengthListIndex,
23 TypeFlagsIndex, TypeFutureTableIndex, TypeListIndex, TypeMapIndex, TypeOptionIndex,
24 TypeRecordIndex, TypeResourceTableIndex, TypeResultIndex, TypeStreamTableIndex, TypeTupleIndex,
25 TypeVariantIndex, VariantInfo,
26};
27use crate::fact::signature::Signature;
28use crate::fact::transcode::Transcoder;
29use crate::fact::{
30 AdapterData, Body, Function, FunctionId, Helper, HelperLocation, HelperType,
31 LinearMemoryOptions, Module, Options,
32};
33use crate::prelude::*;
34use crate::{FuncIndex, GlobalIndex, IndexType, Trap};
35use std::collections::HashMap;
36use std::mem;
37use std::ops::Range;
38use wasm_encoder::{BlockType, Encode, Instruction, Instruction::*, MemArg, ValType};
39use wasmtime_component_util::{DiscriminantSize, FlagsSize};
40
41use super::DataModel;
42
43const MAX_STRING_BYTE_LENGTH: u32 = (1 << 31) - 1;
44const UTF16_TAG: u32 = 1 << 31;
45
46const INITIAL_FUEL: usize = 1_000;
49
50struct Compiler<'a, 'b> {
51 types: &'a ComponentTypesBuilder,
52 module: &'b mut Module<'a>,
53 result: FunctionId,
54
55 code: Vec<u8>,
57
58 nlocals: u32,
60
61 free_locals: HashMap<ValType, Vec<u32>>,
63
64 fuel: usize,
73
74 emit_resource_call: bool,
79}
80
81pub(super) fn compile(module: &mut Module<'_>, adapter: &AdapterData) {
82 fn compiler<'a, 'b>(
83 module: &'b mut Module<'a>,
84 adapter: &AdapterData,
85 ) -> (Compiler<'a, 'b>, Signature, Signature) {
86 let lower_sig = module.types.signature(&adapter.lower);
87 let lift_sig = module.types.signature(&adapter.lift);
88 let ty = module
89 .core_types
90 .function(&lower_sig.params, &lower_sig.results);
91 let result = module
92 .funcs
93 .push(Function::new(Some(adapter.name.clone()), ty));
94
95 let emit_resource_call = module.types.contains_borrow_resource(&adapter.lower);
100 assert_eq!(
101 emit_resource_call,
102 module.types.contains_borrow_resource(&adapter.lift)
103 );
104
105 (
106 Compiler::new(
107 module,
108 result,
109 lower_sig.params.len() as u32,
110 emit_resource_call,
111 ),
112 lower_sig,
113 lift_sig,
114 )
115 }
116
117 if adapter.lift.instance == adapter.lower.instance
121 || adapter.lower.ancestors.contains(&adapter.lift.instance)
122 || adapter.lift.ancestors.contains(&adapter.lower.instance)
123 {
124 let (mut compiler, _, _) = compiler(module, adapter);
125 compiler.trap(Trap::CannotEnterComponent);
126 compiler.finish();
127 return;
128 }
129
130 let async_start_adapter = |module: &mut Module| {
136 let sig = module
137 .types
138 .async_start_signature(&adapter.lower, &adapter.lift);
139 let ty = module.core_types.function(&sig.params, &sig.results);
140 let result = module.funcs.push(Function::new(
141 Some(format!("[async-start]{}", adapter.name)),
142 ty,
143 ));
144
145 Compiler::new(module, result, sig.params.len() as u32, false)
146 .compile_async_start_adapter(adapter, &sig);
147
148 result
149 };
150
151 let async_return_adapter = |module: &mut Module| {
160 let sig = module
161 .types
162 .async_return_signature(&adapter.lower, &adapter.lift);
163 let ty = module.core_types.function(&sig.params, &sig.results);
164 let result = module.funcs.push(Function::new(
165 Some(format!("[async-return]{}", adapter.name)),
166 ty,
167 ));
168
169 Compiler::new(module, result, sig.params.len() as u32, false)
170 .compile_async_return_adapter(adapter, &sig);
171
172 result
173 };
174
175 match (adapter.lower.options.async_, adapter.lift.options.async_) {
176 (false, false) => {
177 let (compiler, lower_sig, lift_sig) = compiler(module, adapter);
180 compiler.compile_sync_to_sync_adapter(adapter, &lower_sig, &lift_sig)
181 }
182 (true, true) => {
183 assert!(module.tunables.concurrency_support);
184
185 let start = async_start_adapter(module);
201 let return_ = async_return_adapter(module);
202 let (compiler, lower_sig, lift_sig) = compiler(module, adapter);
203 compiler.compile_async_to_async_adapter(
204 adapter,
205 start,
206 return_,
207 i32::try_from(lift_sig.params.len()).unwrap(),
208 &lower_sig,
209 );
210 }
211 (false, true) => {
212 assert!(module.tunables.concurrency_support);
213
214 let start = async_start_adapter(module);
227 let return_ = async_return_adapter(module);
228 let (compiler, lower_sig, lift_sig) = compiler(module, adapter);
229 compiler.compile_sync_to_async_adapter(
230 adapter,
231 start,
232 return_,
233 i32::try_from(lift_sig.params.len()).unwrap(),
234 &lower_sig,
235 );
236 }
237 (true, false) => {
238 assert!(module.tunables.concurrency_support);
239
240 let lift_sig = module.types.signature(&adapter.lift);
260 let start = async_start_adapter(module);
261 let return_ = async_return_adapter(module);
262 let (compiler, lower_sig, ..) = compiler(module, adapter);
263 compiler.compile_async_to_sync_adapter(
264 adapter,
265 start,
266 return_,
267 i32::try_from(lift_sig.params.len()).unwrap(),
268 i32::try_from(lift_sig.results.len()).unwrap(),
269 &lower_sig,
270 );
271 }
272 }
273}
274
275pub(super) fn compile_helper(module: &mut Module<'_>, result: FunctionId, helper: Helper) {
282 let mut nlocals = 0;
283 let src_flat;
284 let src = match helper.src.loc {
285 HelperLocation::Stack => {
290 src_flat = module
291 .types
292 .flatten_types(&helper.src.opts, usize::MAX, [helper.src.ty])
293 .unwrap()
294 .iter()
295 .enumerate()
296 .map(|(i, ty)| (i as u32, *ty))
297 .collect::<Vec<_>>();
298 nlocals += src_flat.len() as u32;
299 Source::Stack(Stack {
300 locals: &src_flat,
301 opts: &helper.src.opts,
302 })
303 }
304 HelperLocation::Memory => {
307 nlocals += 1;
308 Source::Memory(Memory {
309 opts: &helper.src.opts,
310 addr: TempLocal::new(0, helper.src.opts.data_model.unwrap_memory().ptr()),
311 offset: 0,
312 })
313 }
314 HelperLocation::StructField | HelperLocation::ArrayElement => todo!("CM+GC"),
315 };
316 let dst_flat;
317 let dst = match helper.dst.loc {
318 HelperLocation::Stack => {
321 dst_flat = module
322 .types
323 .flatten_types(&helper.dst.opts, usize::MAX, [helper.dst.ty])
324 .unwrap();
325 Destination::Stack(&dst_flat, &helper.dst.opts)
326 }
327 HelperLocation::Memory => {
330 nlocals += 1;
331 Destination::Memory(Memory {
332 opts: &helper.dst.opts,
333 addr: TempLocal::new(
334 nlocals - 1,
335 helper.dst.opts.data_model.unwrap_memory().ptr(),
336 ),
337 offset: 0,
338 })
339 }
340 HelperLocation::StructField | HelperLocation::ArrayElement => todo!("CM+GC"),
341 };
342 let mut compiler = Compiler {
343 types: module.types,
344 module,
345 code: Vec::new(),
346 nlocals,
347 free_locals: HashMap::new(),
348 result,
349 fuel: INITIAL_FUEL,
350 emit_resource_call: false,
353 };
354 compiler.translate(&helper.src.ty, &src, &helper.dst.ty, &dst);
355 compiler.finish();
356}
357
358enum Source<'a> {
361 Stack(Stack<'a>),
367
368 Memory(Memory<'a>),
371
372 #[allow(dead_code, reason = "CM+GC is still WIP")]
375 Struct(GcStruct<'a>),
376
377 #[allow(dead_code, reason = "CM+GC is still WIP")]
380 Array(GcArray<'a>),
381}
382
383enum Destination<'a> {
385 Stack(&'a [ValType], &'a Options),
391
392 Memory(Memory<'a>),
394
395 #[allow(dead_code, reason = "CM+GC is still WIP")]
398 Struct(GcStruct<'a>),
399
400 #[allow(dead_code, reason = "CM+GC is still WIP")]
403 Array(GcArray<'a>),
404}
405
406struct Stack<'a> {
407 locals: &'a [(u32, ValType)],
413 opts: &'a Options,
415}
416
417struct Memory<'a> {
419 opts: &'a Options,
421 addr: TempLocal,
424 offset: u32,
427}
428
429impl<'a> Memory<'a> {
430 fn mem_opts(&self) -> &'a LinearMemoryOptions {
431 self.opts.data_model.unwrap_memory()
432 }
433}
434
435struct GcStruct<'a> {
437 opts: &'a Options,
438 }
440
441struct GcArray<'a> {
443 opts: &'a Options,
444 }
446
447impl<'a, 'b> Compiler<'a, 'b> {
448 fn new(
449 module: &'b mut Module<'a>,
450 result: FunctionId,
451 nlocals: u32,
452 emit_resource_call: bool,
453 ) -> Self {
454 Self {
455 types: module.types,
456 module,
457 result,
458 code: Vec::new(),
459 nlocals,
460 free_locals: HashMap::new(),
461 fuel: INITIAL_FUEL,
462 emit_resource_call,
463 }
464 }
465
466 fn compile_async_to_async_adapter(
476 mut self,
477 adapter: &AdapterData,
478 start: FunctionId,
479 return_: FunctionId,
480 param_count: i32,
481 lower_sig: &Signature,
482 ) {
483 let start_call =
484 self.module
485 .import_async_start_call(&adapter.name, adapter.lift.options.callback, None);
486
487 self.call_prepare(adapter, start, return_, lower_sig, false);
488
489 self.module.exports.push((
498 adapter.callee.as_u32(),
499 format!("[adapter-callee]{}", adapter.name),
500 ));
501
502 self.instruction(RefFunc(adapter.callee.as_u32()));
503 self.instruction(I32Const(param_count));
504 self.instruction(I32Const(1));
508 self.instruction(I32Const(START_FLAG_ASYNC_CALLEE));
509 self.instruction(Call(start_call.as_u32()));
510
511 self.finish()
512 }
513
514 fn call_prepare(
527 &mut self,
528 adapter: &AdapterData,
529 start: FunctionId,
530 return_: FunctionId,
531 lower_sig: &Signature,
532 prepare_sync: bool,
533 ) {
534 let prepare = self.module.import_prepare_call(
535 &adapter.name,
536 &lower_sig.params,
537 match adapter.lift.options.data_model {
538 DataModel::Gc {} => todo!("CM+GC"),
539 DataModel::LinearMemory(LinearMemoryOptions { memory, .. }) => memory.map(|m| m.0),
540 },
541 );
542
543 self.flush_code();
544 self.module.funcs[self.result]
545 .body
546 .push(Body::RefFunc(start));
547 self.module.funcs[self.result]
548 .body
549 .push(Body::RefFunc(return_));
550 self.instruction(I32Const(
551 i32::try_from(adapter.lower.instance.as_u32()).unwrap(),
552 ));
553 self.instruction(I32Const(
554 i32::try_from(adapter.lift.instance.as_u32()).unwrap(),
555 ));
556 self.instruction(I32Const(
557 i32::try_from(self.types[adapter.lift.ty].results.as_u32()).unwrap(),
558 ));
559 self.instruction(I32Const(if self.types[adapter.lift.ty].async_ {
560 1
561 } else {
562 0
563 }));
564 self.instruction(I32Const(i32::from(
565 adapter.lift.options.string_encoding as u8,
566 )));
567
568 let result_types = &self.types[self.types[adapter.lower.ty].results].types;
571 if prepare_sync {
572 self.instruction(I32Const(
573 i32::try_from(
574 self.types
575 .flatten_types(
576 &adapter.lower.options,
577 usize::MAX,
578 result_types.iter().copied(),
579 )
580 .map(|v| v.len())
581 .unwrap_or(usize::try_from(i32::MAX).unwrap()),
582 )
583 .unwrap(),
584 ));
585 } else {
586 if result_types.len() > 0 {
587 self.instruction(I32Const(PREPARE_ASYNC_WITH_RESULT.cast_signed()));
588 } else {
589 self.instruction(I32Const(PREPARE_ASYNC_NO_RESULT.cast_signed()));
590 }
591 }
592
593 for index in 0..lower_sig.params.len() {
595 self.instruction(LocalGet(u32::try_from(index).unwrap()));
596 }
597 self.instruction(Call(prepare.as_u32()));
598 }
599
600 fn compile_sync_to_async_adapter(
610 mut self,
611 adapter: &AdapterData,
612 start: FunctionId,
613 return_: FunctionId,
614 lift_param_count: i32,
615 lower_sig: &Signature,
616 ) {
617 let start_call = self.module.import_sync_start_call(
618 &adapter.name,
619 adapter.lift.options.callback,
620 &lower_sig.results,
621 );
622
623 self.call_prepare(adapter, start, return_, lower_sig, true);
624
625 self.module.exports.push((
634 adapter.callee.as_u32(),
635 format!("[adapter-callee]{}", adapter.name),
636 ));
637
638 self.instruction(RefFunc(adapter.callee.as_u32()));
639 self.instruction(I32Const(lift_param_count));
640 self.instruction(Call(start_call.as_u32()));
641
642 self.finish()
643 }
644
645 fn compile_async_to_sync_adapter(
655 mut self,
656 adapter: &AdapterData,
657 start: FunctionId,
658 return_: FunctionId,
659 param_count: i32,
660 result_count: i32,
661 lower_sig: &Signature,
662 ) {
663 let start_call =
664 self.module
665 .import_async_start_call(&adapter.name, None, adapter.lift.post_return);
666
667 self.call_prepare(adapter, start, return_, lower_sig, false);
668
669 self.module.exports.push((
673 adapter.callee.as_u32(),
674 format!("[adapter-callee]{}", adapter.name),
675 ));
676
677 self.instruction(RefFunc(adapter.callee.as_u32()));
678 self.instruction(I32Const(param_count));
679 self.instruction(I32Const(result_count));
680 self.instruction(I32Const(0));
681 self.instruction(Call(start_call.as_u32()));
682
683 self.finish()
684 }
685
686 fn compile_async_start_adapter(mut self, adapter: &AdapterData, sig: &Signature) {
692 let param_locals = sig
693 .params
694 .iter()
695 .enumerate()
696 .map(|(i, ty)| (i as u32, *ty))
697 .collect::<Vec<_>>();
698
699 self.set_flag(adapter.lift.flags, FLAG_MAY_LEAVE, false);
700 self.translate_params(adapter, ¶m_locals);
701 self.set_flag(adapter.lift.flags, FLAG_MAY_LEAVE, true);
702
703 self.finish();
704 }
705
706 fn compile_async_return_adapter(mut self, adapter: &AdapterData, sig: &Signature) {
715 let param_locals = sig
716 .params
717 .iter()
718 .enumerate()
719 .map(|(i, ty)| (i as u32, *ty))
720 .collect::<Vec<_>>();
721
722 self.set_flag(adapter.lower.flags, FLAG_MAY_LEAVE, false);
723 self.translate_results(adapter, ¶m_locals, ¶m_locals);
734 self.set_flag(adapter.lower.flags, FLAG_MAY_LEAVE, true);
735
736 self.finish()
737 }
738
739 fn compile_sync_to_sync_adapter(
746 mut self,
747 adapter: &AdapterData,
748 lower_sig: &Signature,
749 lift_sig: &Signature,
750 ) {
751 self.trap_if_not_flag(
757 adapter.lower.flags,
758 FLAG_MAY_LEAVE,
759 Trap::CannotLeaveComponent,
760 );
761
762 let old_task_may_block = if self.module.tunables.concurrency_support {
763 let task_may_block = self.module.import_task_may_block();
765 let old_task_may_block = if self.types[adapter.lift.ty].async_ {
766 self.instruction(GlobalGet(task_may_block.as_u32()));
767 self.instruction(I32Eqz);
768 self.instruction(If(BlockType::Empty));
769 self.trap(Trap::CannotBlockSyncTask);
770 self.instruction(End);
771 None
772 } else {
773 let task_may_block = self.module.import_task_may_block();
774 self.instruction(GlobalGet(task_may_block.as_u32()));
775 let old_task_may_block = self.local_set_new_tmp(ValType::I32);
776 self.instruction(I32Const(0));
777 self.instruction(GlobalSet(task_may_block.as_u32()));
778 Some(old_task_may_block)
779 };
780
781 self.instruction(I32Const(
786 i32::try_from(adapter.lower.instance.as_u32()).unwrap(),
787 ));
788 self.instruction(I32Const(if self.types[adapter.lift.ty].async_ {
789 1
790 } else {
791 0
792 }));
793 self.instruction(I32Const(
794 i32::try_from(adapter.lift.instance.as_u32()).unwrap(),
795 ));
796 let enter_sync_call = self.module.import_enter_sync_call();
797 self.instruction(Call(enter_sync_call.as_u32()));
798
799 old_task_may_block
800 } else if self.emit_resource_call {
801 assert!(!self.types[adapter.lift.ty].async_);
802 self.instruction(I32Const(
803 i32::try_from(adapter.lower.instance.as_u32()).unwrap(),
804 ));
805 self.instruction(I32Const(0));
806 self.instruction(I32Const(
807 i32::try_from(adapter.lift.instance.as_u32()).unwrap(),
808 ));
809 let enter_sync_call = self.module.import_enter_sync_call();
810 self.instruction(Call(enter_sync_call.as_u32()));
811 None
812 } else {
813 None
814 };
815
816 self.set_flag(adapter.lift.flags, FLAG_MAY_LEAVE, false);
829 let param_locals = lower_sig
830 .params
831 .iter()
832 .enumerate()
833 .map(|(i, ty)| (i as u32, *ty))
834 .collect::<Vec<_>>();
835 self.translate_params(adapter, ¶m_locals);
836 self.set_flag(adapter.lift.flags, FLAG_MAY_LEAVE, true);
837
838 self.instruction(Call(adapter.callee.as_u32()));
842 let mut result_locals = Vec::with_capacity(lift_sig.results.len());
843 let mut temps = Vec::new();
844 for ty in lift_sig.results.iter().rev() {
845 let local = self.local_set_new_tmp(*ty);
846 result_locals.push((local.idx, *ty));
847 temps.push(local);
848 }
849 result_locals.reverse();
850
851 if self.emit_resource_call || self.module.tunables.concurrency_support {
866 let exit_sync_call = self.module.import_exit_sync_call();
867 self.instruction(Call(exit_sync_call.as_u32()));
868 }
869
870 self.set_flag(adapter.lower.flags, FLAG_MAY_LEAVE, false);
879 self.translate_results(adapter, ¶m_locals, &result_locals);
880 self.set_flag(adapter.lower.flags, FLAG_MAY_LEAVE, true);
881
882 if let Some(func) = adapter.lift.post_return {
885 for (result, _) in result_locals.iter() {
886 self.instruction(LocalGet(*result));
887 }
888 self.instruction(Call(func.as_u32()));
889 }
890
891 for tmp in temps {
892 self.free_temp_local(tmp);
893 }
894
895 if self.module.tunables.concurrency_support {
896 if let Some(old_task_may_block) = old_task_may_block {
898 let task_may_block = self.module.import_task_may_block();
899 self.instruction(LocalGet(old_task_may_block.idx));
900 self.instruction(GlobalSet(task_may_block.as_u32()));
901 self.free_temp_local(old_task_may_block);
902 }
903 }
904
905 self.finish()
906 }
907
908 fn translate_params(&mut self, adapter: &AdapterData, param_locals: &[(u32, ValType)]) {
909 let src_tys = self.types[adapter.lower.ty].params;
910 let src_tys = self.types[src_tys]
911 .types
912 .iter()
913 .copied()
914 .collect::<Vec<_>>();
915 let dst_tys = self.types[adapter.lift.ty].params;
916 let dst_tys = self.types[dst_tys]
917 .types
918 .iter()
919 .copied()
920 .collect::<Vec<_>>();
921 let lift_opts = &adapter.lift.options;
922 let lower_opts = &adapter.lower.options;
923
924 assert_eq!(src_tys.len(), dst_tys.len());
926
927 let max_flat_params = if adapter.lower.options.async_ {
931 MAX_FLAT_ASYNC_PARAMS
932 } else {
933 MAX_FLAT_PARAMS
934 };
935 let src_flat =
936 self.types
937 .flatten_types(lower_opts, max_flat_params, src_tys.iter().copied());
938 let dst_flat =
939 self.types
940 .flatten_types(lift_opts, MAX_FLAT_PARAMS, dst_tys.iter().copied());
941
942 let src = if let Some(flat) = &src_flat {
943 Source::Stack(Stack {
944 locals: ¶m_locals[..flat.len()],
945 opts: lower_opts,
946 })
947 } else {
948 let lower_mem_opts = lower_opts.data_model.unwrap_memory();
952 let (addr, ty) = param_locals[0];
953 assert_eq!(ty, lower_mem_opts.ptr());
954 let abi = CanonicalAbiInfo::record(src_tys.iter().map(|t| self.types.canonical_abi(t)));
955 Source::Memory(self.memory_operand_abi(
956 lower_opts,
957 TempLocal::new(addr, ty),
958 &abi,
959 Trap::MemoryOutOfBounds,
960 ))
961 };
962
963 let dst = if let Some(flat) = &dst_flat {
964 Destination::Stack(flat, lift_opts)
965 } else {
966 let abi = CanonicalAbiInfo::record(dst_tys.iter().map(|t| self.types.canonical_abi(t)));
969 Destination::Memory(self.malloc_abi(lift_opts, &abi, Trap::MemoryOutOfBounds))
970 };
971
972 let srcs = src
973 .record_field_srcs(self.types, src_tys.iter().copied())
974 .zip(src_tys.iter());
975 let dsts = dst
976 .record_field_dsts(self.types, dst_tys.iter().copied())
977 .zip(dst_tys.iter());
978 for ((src, src_ty), (dst, dst_ty)) in srcs.zip(dsts) {
979 self.translate(&src_ty, &src, &dst_ty, &dst);
980 }
981
982 if let Destination::Memory(mem) = dst {
986 self.instruction(LocalGet(mem.addr.idx));
987 self.free_temp_local(mem.addr);
988 }
989 }
990
991 fn translate_results(
992 &mut self,
993 adapter: &AdapterData,
994 param_locals: &[(u32, ValType)],
995 result_locals: &[(u32, ValType)],
996 ) {
997 let src_tys = self.types[adapter.lift.ty].results;
998 let src_tys = self.types[src_tys]
999 .types
1000 .iter()
1001 .copied()
1002 .collect::<Vec<_>>();
1003 let dst_tys = self.types[adapter.lower.ty].results;
1004 let dst_tys = self.types[dst_tys]
1005 .types
1006 .iter()
1007 .copied()
1008 .collect::<Vec<_>>();
1009 let lift_opts = &adapter.lift.options;
1010 let lower_opts = &adapter.lower.options;
1011
1012 let src_flat = self
1013 .types
1014 .flatten_lifting_types(lift_opts, src_tys.iter().copied());
1015 let dst_flat = self
1016 .types
1017 .flatten_lowering_types(lower_opts, dst_tys.iter().copied());
1018
1019 let src = if src_flat.is_some() {
1020 Source::Stack(Stack {
1021 locals: result_locals,
1022 opts: lift_opts,
1023 })
1024 } else {
1025 let abi = CanonicalAbiInfo::record(src_tys.iter().map(|t| self.types.canonical_abi(t)));
1030 assert_eq!(
1031 result_locals.len(),
1032 if lower_opts.async_ || lift_opts.async_ {
1033 2
1034 } else {
1035 1
1036 }
1037 );
1038 let (addr, ty) = result_locals[0];
1039 assert_eq!(ty, lift_opts.data_model.unwrap_memory().ptr());
1040 Source::Memory(self.memory_operand_abi(
1041 lift_opts,
1042 TempLocal::new(addr, ty),
1043 &abi,
1044 Trap::MemoryOutOfBounds,
1045 ))
1046 };
1047
1048 let dst = if let Some(flat) = &dst_flat {
1049 Destination::Stack(flat, lower_opts)
1050 } else {
1051 let abi = CanonicalAbiInfo::record(dst_tys.iter().map(|t| self.types.canonical_abi(t)));
1055 let (addr, ty) = *param_locals.last().expect("no retptr");
1056 assert_eq!(ty, lower_opts.data_model.unwrap_memory().ptr());
1057 Destination::Memory(self.memory_operand_abi(
1058 lower_opts,
1059 TempLocal::new(addr, ty),
1060 &abi,
1061 Trap::MemoryOutOfBounds,
1062 ))
1063 };
1064
1065 let srcs = src
1066 .record_field_srcs(self.types, src_tys.iter().copied())
1067 .zip(src_tys.iter());
1068 let dsts = dst
1069 .record_field_dsts(self.types, dst_tys.iter().copied())
1070 .zip(dst_tys.iter());
1071 for ((src, src_ty), (dst, dst_ty)) in srcs.zip(dsts) {
1072 self.translate(&src_ty, &src, &dst_ty, &dst);
1073 }
1074 }
1075
1076 fn translate(
1077 &mut self,
1078 src_ty: &InterfaceType,
1079 src: &Source<'_>,
1080 dst_ty: &InterfaceType,
1081 dst: &Destination,
1082 ) {
1083 if let Source::Memory(mem) = src {
1084 self.assert_aligned(src_ty, mem);
1085 }
1086 if let Destination::Memory(mem) = dst {
1087 self.assert_aligned(dst_ty, mem);
1088 }
1089
1090 let cost = match src_ty {
1120 InterfaceType::Bool
1124 | InterfaceType::U8
1125 | InterfaceType::S8
1126 | InterfaceType::U16
1127 | InterfaceType::S16
1128 | InterfaceType::U32
1129 | InterfaceType::S32
1130 | InterfaceType::U64
1131 | InterfaceType::S64
1132 | InterfaceType::Float32
1133 | InterfaceType::Float64 => 0,
1134
1135 InterfaceType::Char => 1,
1138
1139 InterfaceType::String => 40,
1142
1143 InterfaceType::List(_) => 40,
1146 InterfaceType::Map(_) => 40,
1148
1149 InterfaceType::Flags(i) => {
1150 let count = self.module.types[*i].names.len();
1151 match FlagsSize::from_count(count) {
1152 FlagsSize::Size0 => 0,
1153 FlagsSize::Size1 | FlagsSize::Size2 => 1,
1154 FlagsSize::Size4Plus(n) => n.into(),
1155 }
1156 }
1157
1158 InterfaceType::Record(i) => self.types[*i].fields.len(),
1159 InterfaceType::Tuple(i) => self.types[*i].types.len(),
1160 InterfaceType::Variant(i) => self.types[*i].cases.len(),
1161 InterfaceType::Enum(i) => self.types[*i].names.len(),
1162
1163 InterfaceType::Option(_) | InterfaceType::Result(_) => 2,
1165
1166 InterfaceType::Own(_)
1168 | InterfaceType::Borrow(_)
1169 | InterfaceType::Future(_)
1170 | InterfaceType::Stream(_)
1171 | InterfaceType::ErrorContext(_) => 1,
1172 InterfaceType::FixedLengthList(i) => self.types[*i].size as usize,
1173 };
1174
1175 match self.fuel.checked_sub(cost) {
1176 Some(n) => {
1182 self.fuel = n;
1183 match src_ty {
1184 InterfaceType::Bool => self.translate_bool(src, dst_ty, dst),
1185 InterfaceType::U8 => self.translate_u8(src, dst_ty, dst),
1186 InterfaceType::S8 => self.translate_s8(src, dst_ty, dst),
1187 InterfaceType::U16 => self.translate_u16(src, dst_ty, dst),
1188 InterfaceType::S16 => self.translate_s16(src, dst_ty, dst),
1189 InterfaceType::U32 => self.translate_u32(src, dst_ty, dst),
1190 InterfaceType::S32 => self.translate_s32(src, dst_ty, dst),
1191 InterfaceType::U64 => self.translate_u64(src, dst_ty, dst),
1192 InterfaceType::S64 => self.translate_s64(src, dst_ty, dst),
1193 InterfaceType::Float32 => self.translate_f32(src, dst_ty, dst),
1194 InterfaceType::Float64 => self.translate_f64(src, dst_ty, dst),
1195 InterfaceType::Char => self.translate_char(src, dst_ty, dst),
1196 InterfaceType::String => self.translate_string(src, dst_ty, dst),
1197 InterfaceType::List(t) => self.translate_list(*t, src, dst_ty, dst),
1198 InterfaceType::Map(t) => self.translate_map(*t, src, dst_ty, dst),
1199 InterfaceType::Record(t) => self.translate_record(*t, src, dst_ty, dst),
1200 InterfaceType::Flags(f) => self.translate_flags(*f, src, dst_ty, dst),
1201 InterfaceType::Tuple(t) => self.translate_tuple(*t, src, dst_ty, dst),
1202 InterfaceType::Variant(v) => self.translate_variant(*v, src, dst_ty, dst),
1203 InterfaceType::Enum(t) => self.translate_enum(*t, src, dst_ty, dst),
1204 InterfaceType::Option(t) => self.translate_option(*t, src, dst_ty, dst),
1205 InterfaceType::Result(t) => self.translate_result(*t, src, dst_ty, dst),
1206 InterfaceType::Own(t) => self.translate_own(*t, src, dst_ty, dst),
1207 InterfaceType::Borrow(t) => self.translate_borrow(*t, src, dst_ty, dst),
1208 InterfaceType::Future(t) => self.translate_future(*t, src, dst_ty, dst),
1209 InterfaceType::Stream(t) => self.translate_stream(*t, src, dst_ty, dst),
1210 InterfaceType::ErrorContext(t) => {
1211 self.translate_error_context(*t, src, dst_ty, dst)
1212 }
1213 InterfaceType::FixedLengthList(t) => {
1214 self.translate_fixed_length_list(*t, src, dst_ty, dst);
1215 }
1216 }
1217 }
1218
1219 None => {
1225 let src_loc = match src {
1226 Source::Stack(stack) => {
1230 for (i, ty) in stack
1231 .opts
1232 .flat_types(src_ty, self.types)
1233 .unwrap()
1234 .iter()
1235 .enumerate()
1236 {
1237 let stack = stack.slice(i..i + 1);
1238 self.stack_get(&stack, (*ty).into());
1239 }
1240 HelperLocation::Stack
1241 }
1242 Source::Memory(mem) => {
1247 self.push_mem_addr(mem);
1248 HelperLocation::Memory
1249 }
1250 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1251 };
1252 let dst_loc = match dst {
1253 Destination::Stack(..) => HelperLocation::Stack,
1254 Destination::Memory(mem) => {
1255 self.push_mem_addr(mem);
1256 HelperLocation::Memory
1257 }
1258 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1259 };
1260 let helper = self.module.translate_helper(Helper {
1266 src: HelperType {
1267 ty: *src_ty,
1268 opts: *src.opts(),
1269 loc: src_loc,
1270 },
1271 dst: HelperType {
1272 ty: *dst_ty,
1273 opts: *dst.opts(),
1274 loc: dst_loc,
1275 },
1276 });
1277 self.flush_code();
1280 self.module.funcs[self.result].body.push(Body::Call(helper));
1281
1282 if let Destination::Stack(tys, opts) = dst {
1291 let flat = self
1292 .types
1293 .flatten_types(opts, usize::MAX, [*dst_ty])
1294 .unwrap();
1295 assert_eq!(flat.len(), tys.len());
1296 let locals = flat
1297 .iter()
1298 .rev()
1299 .map(|ty| self.local_set_new_tmp(*ty))
1300 .collect::<Vec<_>>();
1301 for (ty, local) in tys.iter().zip(locals.into_iter().rev()) {
1302 self.instruction(LocalGet(local.idx));
1303 self.stack_set(std::slice::from_ref(ty), local.ty);
1304 self.free_temp_local(local);
1305 }
1306 }
1307 }
1308 }
1309 }
1310
1311 fn push_mem_addr(&mut self, mem: &Memory<'_>) {
1312 self.instruction(LocalGet(mem.addr.idx));
1313 if mem.offset != 0 {
1314 self.ptr_uconst(mem.mem_opts(), mem.offset);
1315 self.ptr_add(mem.mem_opts());
1316 }
1317 }
1318
1319 fn translate_bool(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1320 assert!(matches!(dst_ty, InterfaceType::Bool));
1322 self.push_dst_addr(dst);
1323
1324 self.instruction(I32Const(1));
1327 self.instruction(I32Const(0));
1328 match src {
1329 Source::Memory(mem) => self.i32_load8u(mem),
1330 Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1331 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1332 }
1333 self.instruction(Select);
1334
1335 match dst {
1336 Destination::Memory(mem) => self.i32_store8(mem),
1337 Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1338 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1339 }
1340 }
1341
1342 fn translate_u8(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1343 assert!(matches!(dst_ty, InterfaceType::U8));
1345 self.convert_u8_mask(src, dst, 0xff);
1346 }
1347
1348 fn convert_u8_mask(&mut self, src: &Source<'_>, dst: &Destination<'_>, mask: u8) {
1349 self.push_dst_addr(dst);
1350 let mut needs_mask = true;
1351 match src {
1352 Source::Memory(mem) => {
1353 self.i32_load8u(mem);
1354 needs_mask = mask != 0xff;
1355 }
1356 Source::Stack(stack) => {
1357 self.stack_get(stack, ValType::I32);
1358 }
1359 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1360 }
1361 if needs_mask {
1362 self.instruction(I32Const(i32::from(mask)));
1363 self.instruction(I32And);
1364 }
1365 match dst {
1366 Destination::Memory(mem) => self.i32_store8(mem),
1367 Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1368 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1369 }
1370 }
1371
1372 fn translate_s8(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1373 assert!(matches!(dst_ty, InterfaceType::S8));
1375 self.push_dst_addr(dst);
1376 match src {
1377 Source::Memory(mem) => self.i32_load8s(mem),
1378 Source::Stack(stack) => {
1379 self.stack_get(stack, ValType::I32);
1380 self.instruction(I32Extend8S);
1381 }
1382 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1383 }
1384 match dst {
1385 Destination::Memory(mem) => self.i32_store8(mem),
1386 Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1387 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1388 }
1389 }
1390
1391 fn translate_u16(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1392 assert!(matches!(dst_ty, InterfaceType::U16));
1394 self.convert_u16_mask(src, dst, 0xffff);
1395 }
1396
1397 fn convert_u16_mask(&mut self, src: &Source<'_>, dst: &Destination<'_>, mask: u16) {
1398 self.push_dst_addr(dst);
1399 let mut needs_mask = true;
1400 match src {
1401 Source::Memory(mem) => {
1402 self.i32_load16u(mem);
1403 needs_mask = mask != 0xffff;
1404 }
1405 Source::Stack(stack) => {
1406 self.stack_get(stack, ValType::I32);
1407 }
1408 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1409 }
1410 if needs_mask {
1411 self.instruction(I32Const(i32::from(mask)));
1412 self.instruction(I32And);
1413 }
1414 match dst {
1415 Destination::Memory(mem) => self.i32_store16(mem),
1416 Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1417 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1418 }
1419 }
1420
1421 fn translate_s16(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1422 assert!(matches!(dst_ty, InterfaceType::S16));
1424 self.push_dst_addr(dst);
1425 match src {
1426 Source::Memory(mem) => self.i32_load16s(mem),
1427 Source::Stack(stack) => {
1428 self.stack_get(stack, ValType::I32);
1429 self.instruction(I32Extend16S);
1430 }
1431 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1432 }
1433 match dst {
1434 Destination::Memory(mem) => self.i32_store16(mem),
1435 Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1436 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1437 }
1438 }
1439
1440 fn translate_u32(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1441 assert!(matches!(dst_ty, InterfaceType::U32));
1443 self.convert_u32_mask(src, dst, 0xffffffff)
1444 }
1445
1446 fn convert_u32_mask(&mut self, src: &Source<'_>, dst: &Destination<'_>, mask: u32) {
1447 self.push_dst_addr(dst);
1448 match src {
1449 Source::Memory(mem) => self.i32_load(mem),
1450 Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1451 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1452 }
1453 if mask != 0xffffffff {
1454 self.instruction(I32Const(mask as i32));
1455 self.instruction(I32And);
1456 }
1457 match dst {
1458 Destination::Memory(mem) => self.i32_store(mem),
1459 Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1460 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1461 }
1462 }
1463
1464 fn translate_s32(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1465 assert!(matches!(dst_ty, InterfaceType::S32));
1467 self.push_dst_addr(dst);
1468 match src {
1469 Source::Memory(mem) => self.i32_load(mem),
1470 Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1471 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1472 }
1473 match dst {
1474 Destination::Memory(mem) => self.i32_store(mem),
1475 Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1476 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1477 }
1478 }
1479
1480 fn translate_u64(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1481 assert!(matches!(dst_ty, InterfaceType::U64));
1483 self.push_dst_addr(dst);
1484 match src {
1485 Source::Memory(mem) => self.i64_load(mem),
1486 Source::Stack(stack) => self.stack_get(stack, ValType::I64),
1487 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1488 }
1489 match dst {
1490 Destination::Memory(mem) => self.i64_store(mem),
1491 Destination::Stack(stack, _) => self.stack_set(stack, ValType::I64),
1492 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1493 }
1494 }
1495
1496 fn translate_s64(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1497 assert!(matches!(dst_ty, InterfaceType::S64));
1499 self.push_dst_addr(dst);
1500 match src {
1501 Source::Memory(mem) => self.i64_load(mem),
1502 Source::Stack(stack) => self.stack_get(stack, ValType::I64),
1503 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1504 }
1505 match dst {
1506 Destination::Memory(mem) => self.i64_store(mem),
1507 Destination::Stack(stack, _) => self.stack_set(stack, ValType::I64),
1508 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1509 }
1510 }
1511
1512 fn translate_f32(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1513 assert!(matches!(dst_ty, InterfaceType::Float32));
1515 self.push_dst_addr(dst);
1516 match src {
1517 Source::Memory(mem) => self.f32_load(mem),
1518 Source::Stack(stack) => self.stack_get(stack, ValType::F32),
1519 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1520 }
1521 match dst {
1522 Destination::Memory(mem) => self.f32_store(mem),
1523 Destination::Stack(stack, _) => self.stack_set(stack, ValType::F32),
1524 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1525 }
1526 }
1527
1528 fn translate_f64(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1529 assert!(matches!(dst_ty, InterfaceType::Float64));
1531 self.push_dst_addr(dst);
1532 match src {
1533 Source::Memory(mem) => self.f64_load(mem),
1534 Source::Stack(stack) => self.stack_get(stack, ValType::F64),
1535 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1536 }
1537 match dst {
1538 Destination::Memory(mem) => self.f64_store(mem),
1539 Destination::Stack(stack, _) => self.stack_set(stack, ValType::F64),
1540 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1541 }
1542 }
1543
1544 fn translate_char(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1545 assert!(matches!(dst_ty, InterfaceType::Char));
1546 match src {
1547 Source::Memory(mem) => self.i32_load(mem),
1548 Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1549 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1550 }
1551 let local = self.local_set_new_tmp(ValType::I32);
1552
1553 self.instruction(Block(BlockType::Empty));
1569 self.instruction(Block(BlockType::Empty));
1570 self.instruction(LocalGet(local.idx));
1571 self.instruction(I32Const(0xd800));
1572 self.instruction(I32Xor);
1573 self.instruction(I32Const(-0x110000));
1574 self.instruction(I32Add);
1575 self.instruction(I32Const(-0x10f800));
1576 self.instruction(I32LtU);
1577 self.instruction(BrIf(0));
1578 self.instruction(LocalGet(local.idx));
1579 self.instruction(I32Const(0x110000));
1580 self.instruction(I32Ne);
1581 self.instruction(BrIf(1));
1582 self.instruction(End);
1583 self.trap(Trap::InvalidChar);
1584 self.instruction(End);
1585
1586 self.push_dst_addr(dst);
1587 self.instruction(LocalGet(local.idx));
1588 match dst {
1589 Destination::Memory(mem) => {
1590 self.i32_store(mem);
1591 }
1592 Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1593 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1594 }
1595
1596 self.free_temp_local(local);
1597 }
1598
1599 fn translate_string(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1600 assert!(matches!(dst_ty, InterfaceType::String));
1601 let src_opts = src.opts();
1602 let dst_opts = dst.opts();
1603
1604 let src_mem_opts = match &src_opts.data_model {
1605 DataModel::Gc {} => todo!("CM+GC"),
1606 DataModel::LinearMemory(opts) => opts,
1607 };
1608 let dst_mem_opts = match &dst_opts.data_model {
1609 DataModel::Gc {} => todo!("CM+GC"),
1610 DataModel::LinearMemory(opts) => opts,
1611 };
1612
1613 match src {
1618 Source::Stack(s) => {
1619 assert_eq!(s.locals.len(), 2);
1620 self.stack_get(&s.slice(0..1), src_mem_opts.ptr());
1621 self.stack_get(&s.slice(1..2), src_mem_opts.ptr());
1622 }
1623 Source::Memory(mem) => {
1624 self.ptr_load(mem);
1625 self.ptr_load(&mem.bump(src_mem_opts.ptr_size().into()));
1626 }
1627 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1628 }
1629 let src_len = self.local_set_new_tmp(src_mem_opts.ptr());
1630 let src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
1631 let src_str = WasmString {
1632 ptr: src_ptr,
1633 len: src_len,
1634 opts: src_opts,
1635 };
1636
1637 let dst_str = match src_opts.string_encoding {
1638 StringEncoding::Utf8 => {
1639 self.validate_guest_pointer(
1640 src_opts,
1641 &src_str.ptr,
1642 &AllocSize::Local(src_str.len.idx),
1643 1,
1644 Trap::StringOutOfBounds,
1645 );
1646 match dst_opts.string_encoding {
1647 StringEncoding::Utf8 => {
1648 self.string_copy(&src_str, FE::Utf8, dst_opts, FE::Utf8)
1649 }
1650 StringEncoding::Utf16 => self.string_utf8_to_utf16(&src_str, dst_opts),
1651 StringEncoding::CompactUtf16 => {
1652 self.string_to_compact(&src_str, FE::Utf8, dst_opts)
1653 }
1654 }
1655 }
1656
1657 StringEncoding::Utf16 => {
1658 self.validate_guest_pointer(
1659 src_opts,
1660 &src_str.ptr,
1661 &AllocSize::DoubleLocal(src_str.len.idx),
1662 2,
1663 Trap::StringOutOfBounds,
1664 );
1665 match dst_opts.string_encoding {
1666 StringEncoding::Utf8 => {
1667 self.string_deflate_to_utf8(&src_str, FE::Utf16, dst_opts)
1668 }
1669 StringEncoding::Utf16 => {
1670 self.string_copy(&src_str, FE::Utf16, dst_opts, FE::Utf16)
1671 }
1672 StringEncoding::CompactUtf16 => {
1673 self.string_to_compact(&src_str, FE::Utf16, dst_opts)
1674 }
1675 }
1676 }
1677
1678 StringEncoding::CompactUtf16 => {
1679 self.instruction(LocalGet(src_str.len.idx));
1682 self.ptr_uconst(src_mem_opts, UTF16_TAG);
1683 self.ptr_and(src_mem_opts);
1684 self.ptr_if(src_mem_opts, BlockType::Empty);
1685
1686 self.instruction(LocalGet(src_str.len.idx));
1690 self.ptr_uconst(src_mem_opts, UTF16_TAG);
1691 self.ptr_xor(src_mem_opts);
1692 self.instruction(LocalSet(src_str.len.idx));
1693
1694 self.validate_guest_pointer(
1698 src_opts,
1699 &src_str.ptr,
1700 &AllocSize::DoubleLocal(src_str.len.idx),
1701 2,
1702 Trap::StringOutOfBounds,
1703 );
1704
1705 let s1 = match dst_opts.string_encoding {
1706 StringEncoding::Utf8 => {
1707 self.string_deflate_to_utf8(&src_str, FE::Utf16, dst_opts)
1708 }
1709 StringEncoding::Utf16 => {
1710 self.string_copy(&src_str, FE::Utf16, dst_opts, FE::Utf16)
1711 }
1712 StringEncoding::CompactUtf16 => {
1713 self.string_compact_utf16_to_compact(&src_str, dst_opts)
1714 }
1715 };
1716
1717 self.instruction(Else);
1718
1719 self.validate_guest_pointer(
1722 src_opts,
1723 &src_str.ptr,
1724 &AllocSize::Local(src_str.len.idx),
1725 2,
1726 Trap::StringOutOfBounds,
1727 );
1728
1729 let s2 = match dst_opts.string_encoding {
1733 StringEncoding::Utf16 => {
1734 self.string_copy(&src_str, FE::Latin1, dst_opts, FE::Utf16)
1735 }
1736 StringEncoding::Utf8 => {
1737 self.string_deflate_to_utf8(&src_str, FE::Latin1, dst_opts)
1738 }
1739 StringEncoding::CompactUtf16 => {
1740 self.string_copy(&src_str, FE::Latin1, dst_opts, FE::Latin1)
1741 }
1742 };
1743 self.instruction(LocalGet(s2.ptr.idx));
1746 self.instruction(LocalSet(s1.ptr.idx));
1747 self.instruction(LocalGet(s2.len.idx));
1748 self.instruction(LocalSet(s1.len.idx));
1749 self.instruction(End);
1750 self.free_temp_local(s2.ptr);
1751 self.free_temp_local(s2.len);
1752 s1
1753 }
1754 };
1755
1756 match dst {
1758 Destination::Stack(s, _) => {
1759 self.instruction(LocalGet(dst_str.ptr.idx));
1760 self.stack_set(&s[..1], dst_mem_opts.ptr());
1761 self.instruction(LocalGet(dst_str.len.idx));
1762 self.stack_set(&s[1..], dst_mem_opts.ptr());
1763 }
1764 Destination::Memory(mem) => {
1765 self.instruction(LocalGet(mem.addr.idx));
1766 self.instruction(LocalGet(dst_str.ptr.idx));
1767 self.ptr_store(mem);
1768 self.instruction(LocalGet(mem.addr.idx));
1769 self.instruction(LocalGet(dst_str.len.idx));
1770 self.ptr_store(&mem.bump(dst_mem_opts.ptr_size().into()));
1771 }
1772 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1773 }
1774
1775 self.free_temp_local(src_str.ptr);
1776 self.free_temp_local(src_str.len);
1777 self.free_temp_local(dst_str.ptr);
1778 self.free_temp_local(dst_str.len);
1779 }
1780
1781 fn string_copy<'c>(
1794 &mut self,
1795 src: &WasmString<'_>,
1796 src_enc: FE,
1797 dst_opts: &'c Options,
1798 dst_enc: FE,
1799 ) -> WasmString<'c> {
1800 assert!(dst_enc.width() >= src_enc.width());
1801
1802 self.validate_string_length(src, dst_enc);
1807
1808 let src_mem_opts = {
1809 match &src.opts.data_model {
1810 DataModel::Gc {} => todo!("CM+GC"),
1811 DataModel::LinearMemory(opts) => opts,
1812 }
1813 };
1814 let dst_mem_opts = {
1815 match &dst_opts.data_model {
1816 DataModel::Gc {} => todo!("CM+GC"),
1817 DataModel::LinearMemory(opts) => opts,
1818 }
1819 };
1820
1821 self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
1824 let dst_len = self.local_tee_new_tmp(dst_mem_opts.ptr());
1825 if dst_enc.width() > 1 {
1826 assert_eq!(dst_enc.width(), 2);
1827 self.ptr_uconst(dst_mem_opts, 1);
1828 self.ptr_shl(dst_mem_opts);
1829 }
1830 let dst_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
1831
1832 let dst = {
1835 let dst_mem = self.malloc(
1836 dst_opts,
1837 AllocSize::Local(dst_byte_len.idx),
1838 dst_enc.align().into(),
1839 Trap::StringOutOfBounds,
1840 );
1841 WasmString {
1842 ptr: dst_mem.addr,
1843 len: dst_len,
1844 opts: dst_opts,
1845 }
1846 };
1847
1848 let op = if src_enc == dst_enc {
1852 Transcode::Copy(src_enc)
1853 } else {
1854 assert_eq!(src_enc, FE::Latin1);
1855 assert_eq!(dst_enc, FE::Utf16);
1856 Transcode::Latin1ToUtf16
1857 };
1858 let transcode = self.transcoder(src, &dst, op);
1859 self.instruction(LocalGet(src.ptr.idx));
1860 self.instruction(LocalGet(src.len.idx));
1861 self.instruction(LocalGet(dst.ptr.idx));
1862 self.instruction(Call(transcode.as_u32()));
1863
1864 self.free_temp_local(dst_byte_len);
1865
1866 dst
1867 }
1868
1869 fn string_deflate_to_utf8<'c>(
1882 &mut self,
1883 src: &WasmString<'_>,
1884 src_enc: FE,
1885 dst_opts: &'c Options,
1886 ) -> WasmString<'c> {
1887 let src_mem_opts = match &src.opts.data_model {
1888 DataModel::Gc {} => todo!("CM+GC"),
1889 DataModel::LinearMemory(opts) => opts,
1890 };
1891 let dst_mem_opts = match &dst_opts.data_model {
1892 DataModel::Gc {} => todo!("CM+GC"),
1893 DataModel::LinearMemory(opts) => opts,
1894 };
1895
1896 self.validate_string_length(src, src_enc);
1897
1898 self.convert_src_len_to_dst(
1902 src.len.idx,
1903 src.opts.data_model.unwrap_memory().ptr(),
1904 dst_opts.data_model.unwrap_memory().ptr(),
1905 );
1906 let dst_len = self.local_tee_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
1907 let dst_byte_len = self.local_set_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
1908
1909 let dst = {
1910 let dst_mem = self.malloc(
1911 dst_opts,
1912 AllocSize::Local(dst_byte_len.idx),
1913 1,
1914 Trap::StringOutOfBounds,
1915 );
1916 WasmString {
1917 ptr: dst_mem.addr,
1918 len: dst_len,
1919 opts: dst_opts,
1920 }
1921 };
1922
1923 let op = match src_enc {
1925 FE::Latin1 => Transcode::Latin1ToUtf8,
1926 FE::Utf16 => Transcode::Utf16ToUtf8,
1927 FE::Utf8 => unreachable!(),
1928 };
1929 let transcode = self.transcoder(src, &dst, op);
1930 self.instruction(LocalGet(src.ptr.idx));
1931 self.instruction(LocalGet(src.len.idx));
1932 self.instruction(LocalGet(dst.ptr.idx));
1933 self.instruction(LocalGet(dst_byte_len.idx));
1934 self.instruction(I32Const(1)); self.instruction(Call(transcode.as_u32()));
1936 self.instruction(LocalSet(dst.len.idx));
1937 let src_len_tmp = self.local_set_new_tmp(src.opts.data_model.unwrap_memory().ptr());
1938
1939 self.instruction(LocalGet(src_len_tmp.idx));
1943 self.instruction(LocalGet(src.len.idx));
1944 self.ptr_ne(src_mem_opts);
1945 self.instruction(If(BlockType::Empty));
1946
1947 let factor = match src_enc {
1950 FE::Latin1 => 2,
1951 FE::Utf16 => 3,
1952 _ => unreachable!(),
1953 };
1954 self.validate_string_length_u8(src, factor);
1955 self.convert_src_len_to_dst(
1956 src.len.idx,
1957 src.opts.data_model.unwrap_memory().ptr(),
1958 dst_opts.data_model.unwrap_memory().ptr(),
1959 );
1960 self.ptr_uconst(dst_mem_opts, factor.into());
1961 self.ptr_mul(dst_mem_opts);
1962 let new_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
1963
1964 self.realloc(
1968 dst_opts,
1969 &dst.ptr,
1970 AllocSize::Local(dst_byte_len.idx),
1971 AllocSize::Local(new_byte_len.idx),
1972 1,
1973 Trap::StringOutOfBounds,
1974 );
1975 self.instruction(LocalGet(new_byte_len.idx));
1976 self.instruction(LocalSet(dst_byte_len.idx));
1977 self.free_temp_local(new_byte_len);
1978
1979 self.instruction(LocalGet(src.ptr.idx));
1984 self.instruction(LocalGet(src_len_tmp.idx));
1985 if let FE::Utf16 = src_enc {
1986 self.ptr_uconst(src_mem_opts, 1);
1987 self.ptr_shl(src_mem_opts);
1988 }
1989 self.ptr_add(src_mem_opts);
1990 self.instruction(LocalGet(src.len.idx));
1991 self.instruction(LocalGet(src_len_tmp.idx));
1992 self.ptr_sub(src_mem_opts);
1993 self.instruction(LocalGet(dst.ptr.idx));
1994 self.instruction(LocalGet(dst.len.idx));
1995 self.ptr_add(dst_mem_opts);
1996 self.instruction(LocalGet(dst_byte_len.idx));
1997 self.instruction(LocalGet(dst.len.idx));
1998 self.ptr_sub(dst_mem_opts);
1999 self.instruction(I32Const(0)); self.instruction(Call(transcode.as_u32()));
2001
2002 self.instruction(LocalGet(dst.len.idx));
2006 self.ptr_add(dst_mem_opts);
2007 self.instruction(LocalSet(dst.len.idx));
2008
2009 if self.module.tunables.debug_adapter_modules {
2012 self.instruction(LocalGet(src.len.idx));
2013 self.instruction(LocalGet(src_len_tmp.idx));
2014 self.ptr_sub(src_mem_opts);
2015 self.ptr_ne(src_mem_opts);
2016 self.instruction(If(BlockType::Empty));
2017 self.trap(Trap::DebugAssertStringEncodingFinished);
2018 self.instruction(End);
2019 } else {
2020 self.instruction(Drop);
2021 }
2022
2023 self.instruction(LocalGet(dst.len.idx));
2025 self.instruction(LocalGet(dst_byte_len.idx));
2026 self.ptr_ne(dst_mem_opts);
2027 self.instruction(If(BlockType::Empty));
2028 self.realloc(
2029 dst_opts,
2030 &dst.ptr,
2031 AllocSize::Local(dst_byte_len.idx),
2032 AllocSize::Local(dst.len.idx),
2033 1,
2034 Trap::StringOutOfBounds,
2035 );
2036 self.instruction(End);
2037
2038 if self.module.tunables.debug_adapter_modules {
2041 self.instruction(Else);
2042
2043 self.instruction(LocalGet(dst.len.idx));
2044 self.instruction(LocalGet(dst_byte_len.idx));
2045 self.ptr_ne(dst_mem_opts);
2046 self.instruction(If(BlockType::Empty));
2047 self.trap(Trap::DebugAssertStringEncodingFinished);
2048 self.instruction(End);
2049 }
2050
2051 self.instruction(End); self.free_temp_local(src_len_tmp);
2054 self.free_temp_local(dst_byte_len);
2055
2056 dst
2057 }
2058
2059 fn string_utf8_to_utf16<'c>(
2074 &mut self,
2075 src: &WasmString<'_>,
2076 dst_opts: &'c Options,
2077 ) -> WasmString<'c> {
2078 let src_mem_opts = match &src.opts.data_model {
2079 DataModel::Gc {} => todo!("CM+GC"),
2080 DataModel::LinearMemory(opts) => opts,
2081 };
2082 let dst_mem_opts = match &dst_opts.data_model {
2083 DataModel::Gc {} => todo!("CM+GC"),
2084 DataModel::LinearMemory(opts) => opts,
2085 };
2086
2087 self.validate_string_length(src, FE::Utf16);
2088 self.convert_src_len_to_dst(
2089 src.len.idx,
2090 src_mem_opts.ptr(),
2091 dst_opts.data_model.unwrap_memory().ptr(),
2092 );
2093 let dst_len = self.local_tee_new_tmp(dst_opts.data_model.unwrap_memory().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_opts.data_model.unwrap_memory().ptr());
2097 let dst = {
2098 let dst_mem = self.malloc(
2099 dst_opts,
2100 AllocSize::Local(dst_byte_len.idx),
2101 2,
2102 Trap::StringOutOfBounds,
2103 );
2104 WasmString {
2105 ptr: dst_mem.addr,
2106 len: dst_len,
2107 opts: dst_opts,
2108 }
2109 };
2110
2111 let transcode = self.transcoder(src, &dst, Transcode::Utf8ToUtf16);
2112 self.instruction(LocalGet(src.ptr.idx));
2113 self.instruction(LocalGet(src.len.idx));
2114 self.instruction(LocalGet(dst.ptr.idx));
2115 self.instruction(Call(transcode.as_u32()));
2116 self.instruction(LocalSet(dst.len.idx));
2117
2118 self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2126 self.instruction(LocalGet(dst.len.idx));
2127 self.ptr_ne(dst_mem_opts);
2128 self.instruction(If(BlockType::Empty));
2129 self.realloc(
2130 dst.opts,
2131 &dst.ptr,
2132 AllocSize::Local(dst_byte_len.idx),
2133 AllocSize::DoubleLocal(dst.len.idx),
2134 2,
2135 Trap::StringOutOfBounds,
2136 );
2137 self.instruction(End); self.free_temp_local(dst_byte_len);
2140
2141 dst
2142 }
2143
2144 fn string_compact_utf16_to_compact<'c>(
2158 &mut self,
2159 src: &WasmString<'_>,
2160 dst_opts: &'c Options,
2161 ) -> WasmString<'c> {
2162 let src_mem_opts = match &src.opts.data_model {
2163 DataModel::Gc {} => todo!("CM+GC"),
2164 DataModel::LinearMemory(opts) => opts,
2165 };
2166 let dst_mem_opts = match &dst_opts.data_model {
2167 DataModel::Gc {} => todo!("CM+GC"),
2168 DataModel::LinearMemory(opts) => opts,
2169 };
2170
2171 self.validate_string_length(src, FE::Utf16);
2172 self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2173 let dst_len = self.local_tee_new_tmp(dst_mem_opts.ptr());
2174 self.ptr_uconst(dst_mem_opts, 1);
2175 self.ptr_shl(dst_mem_opts);
2176 let dst_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
2177 let dst = {
2178 let dst_mem = self.malloc(
2179 dst_opts,
2180 AllocSize::Local(dst_byte_len.idx),
2181 2,
2182 Trap::StringOutOfBounds,
2183 );
2184 WasmString {
2185 ptr: dst_mem.addr,
2186 len: dst_len,
2187 opts: dst_opts,
2188 }
2189 };
2190
2191 self.convert_src_len_to_dst(
2192 dst_byte_len.idx,
2193 dst.opts.data_model.unwrap_memory().ptr(),
2194 src_mem_opts.ptr(),
2195 );
2196 let src_byte_len = self.local_set_new_tmp(src_mem_opts.ptr());
2197
2198 let transcode = self.transcoder(src, &dst, Transcode::Utf16ToCompactProbablyUtf16);
2199 self.instruction(LocalGet(src.ptr.idx));
2200 self.instruction(LocalGet(src.len.idx));
2201 self.instruction(LocalGet(dst.ptr.idx));
2202 self.instruction(Call(transcode.as_u32()));
2203 self.instruction(LocalSet(dst.len.idx));
2204
2205 if self.module.tunables.debug_adapter_modules {
2208 self.instruction(LocalGet(dst.len.idx));
2209 self.ptr_uconst(dst_mem_opts, !UTF16_TAG);
2210 self.ptr_and(dst_mem_opts);
2211 self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2212 self.ptr_ne(dst_mem_opts);
2213 self.instruction(If(BlockType::Empty));
2214 self.trap(Trap::DebugAssertEqualCodeUnits);
2215 self.instruction(End);
2216 }
2217
2218 self.instruction(LocalGet(dst.len.idx));
2222 self.ptr_uconst(dst_mem_opts, UTF16_TAG);
2223 self.ptr_and(dst_mem_opts);
2224 self.ptr_br_if(dst_mem_opts, 0);
2225
2226 self.realloc(
2228 dst.opts,
2229 &dst.ptr,
2230 AllocSize::Local(dst_byte_len.idx),
2231 AllocSize::Local(dst.len.idx),
2232 2,
2233 Trap::StringOutOfBounds,
2234 );
2235
2236 self.free_temp_local(dst_byte_len);
2237 self.free_temp_local(src_byte_len);
2238
2239 dst
2240 }
2241
2242 fn string_to_compact<'c>(
2249 &mut self,
2250 src: &WasmString<'_>,
2251 src_enc: FE,
2252 dst_opts: &'c Options,
2253 ) -> WasmString<'c> {
2254 let src_mem_opts = match &src.opts.data_model {
2255 DataModel::Gc {} => todo!("CM+GC"),
2256 DataModel::LinearMemory(opts) => opts,
2257 };
2258 let dst_mem_opts = match &dst_opts.data_model {
2259 DataModel::Gc {} => todo!("CM+GC"),
2260 DataModel::LinearMemory(opts) => opts,
2261 };
2262
2263 self.validate_string_length(src, src_enc);
2264
2265 self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2266 let dst_len = self.local_tee_new_tmp(dst_mem_opts.ptr());
2267 let dst_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
2268 let dst = {
2269 let dst_mem = self.malloc(
2270 dst_opts,
2271 AllocSize::Local(dst_byte_len.idx),
2272 2,
2273 Trap::StringOutOfBounds,
2274 );
2275 WasmString {
2276 ptr: dst_mem.addr,
2277 len: dst_len,
2278 opts: dst_opts,
2279 }
2280 };
2281
2282 let (latin1, utf16) = match src_enc {
2286 FE::Utf8 => (Transcode::Utf8ToLatin1, Transcode::Utf8ToCompactUtf16),
2287 FE::Utf16 => (Transcode::Utf16ToLatin1, Transcode::Utf16ToCompactUtf16),
2288 FE::Latin1 => unreachable!(),
2289 };
2290 let transcode_latin1 = self.transcoder(src, &dst, latin1);
2291 let transcode_utf16 = self.transcoder(src, &dst, utf16);
2292 self.instruction(LocalGet(src.ptr.idx));
2293 self.instruction(LocalGet(src.len.idx));
2294 self.instruction(LocalGet(dst.ptr.idx));
2295 self.instruction(Call(transcode_latin1.as_u32()));
2296 self.instruction(LocalSet(dst.len.idx));
2297 let src_len_tmp = self.local_set_new_tmp(src_mem_opts.ptr());
2298
2299 self.instruction(LocalGet(src_len_tmp.idx));
2302 self.instruction(LocalGet(src.len.idx));
2303 self.ptr_eq(src_mem_opts);
2304 self.instruction(If(BlockType::Empty)); self.instruction(LocalGet(dst_byte_len.idx));
2310 self.instruction(LocalGet(dst.len.idx));
2311 self.ptr_ne(dst_mem_opts);
2312 self.instruction(If(BlockType::Empty));
2313 self.realloc(
2314 dst.opts,
2315 &dst.ptr,
2316 AllocSize::Local(dst_byte_len.idx),
2317 AllocSize::Local(dst.len.idx),
2318 2,
2319 Trap::StringOutOfBounds,
2320 );
2321 self.instruction(End);
2322
2323 self.instruction(Else); if src_enc.width() == 1 {
2332 self.validate_string_length_u8(src, 2);
2333 }
2334
2335 self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2338 self.ptr_uconst(dst_mem_opts, 1);
2339 self.ptr_shl(dst_mem_opts);
2340 let new_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
2341 self.realloc(
2342 dst.opts,
2343 &dst.ptr,
2344 AllocSize::Local(dst_byte_len.idx),
2345 AllocSize::Local(new_byte_len.idx),
2346 2,
2347 Trap::StringOutOfBounds,
2348 );
2349 self.instruction(LocalGet(new_byte_len.idx));
2350 self.instruction(LocalSet(dst_byte_len.idx));
2351 self.free_temp_local(new_byte_len);
2352
2353 self.instruction(LocalGet(src.ptr.idx));
2357 self.instruction(LocalGet(src_len_tmp.idx));
2358 if let FE::Utf16 = src_enc {
2359 self.ptr_uconst(src_mem_opts, 1);
2360 self.ptr_shl(src_mem_opts);
2361 }
2362 self.ptr_add(src_mem_opts);
2363 self.instruction(LocalGet(src.len.idx));
2364 self.instruction(LocalGet(src_len_tmp.idx));
2365 self.ptr_sub(src_mem_opts);
2366 self.instruction(LocalGet(dst.ptr.idx));
2367 self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2368 self.instruction(LocalGet(dst.len.idx));
2369 self.instruction(Call(transcode_utf16.as_u32()));
2370 self.instruction(LocalSet(dst.len.idx));
2371
2372 self.instruction(LocalGet(dst.len.idx));
2380 self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2381 self.ptr_ne(dst_mem_opts);
2382 self.instruction(If(BlockType::Empty));
2383 self.realloc(
2384 dst.opts,
2385 &dst.ptr,
2386 AllocSize::Local(dst_byte_len.idx),
2387 AllocSize::DoubleLocal(dst.len.idx),
2388 2,
2389 Trap::StringOutOfBounds,
2390 );
2391 self.instruction(End);
2392
2393 self.instruction(LocalGet(dst.len.idx));
2395 self.ptr_uconst(dst_mem_opts, UTF16_TAG);
2396 self.ptr_or(dst_mem_opts);
2397 self.instruction(LocalSet(dst.len.idx));
2398
2399 self.instruction(End); self.free_temp_local(src_len_tmp);
2402 self.free_temp_local(dst_byte_len);
2403
2404 dst
2405 }
2406
2407 fn validate_string_length(&mut self, src: &WasmString<'_>, dst: FE) {
2408 self.validate_string_length_u8(src, dst.width())
2409 }
2410
2411 fn validate_string_length_u8(&mut self, s: &WasmString<'_>, dst: u8) {
2412 let mem_opts = match &s.opts.data_model {
2413 DataModel::Gc {} => todo!("CM+GC"),
2414 DataModel::LinearMemory(opts) => opts,
2415 };
2416
2417 self.instruction(LocalGet(s.len.idx));
2420 let max = MAX_STRING_BYTE_LENGTH / u32::from(dst);
2421 self.ptr_uconst(mem_opts, max);
2422 self.ptr_gt_u(mem_opts);
2423 self.instruction(If(BlockType::Empty));
2424 self.trap(Trap::StringOutOfBounds);
2425 self.instruction(End);
2426 }
2427
2428 fn transcoder(
2429 &mut self,
2430 src: &WasmString<'_>,
2431 dst: &WasmString<'_>,
2432 op: Transcode,
2433 ) -> FuncIndex {
2434 match (src.opts.data_model, dst.opts.data_model) {
2435 (DataModel::Gc {}, _) | (_, DataModel::Gc {}) => {
2436 todo!("CM+GC")
2437 }
2438 (
2439 DataModel::LinearMemory(LinearMemoryOptions {
2440 memory: Some((src_mem, src_ty)),
2441 realloc: _,
2442 }),
2443 DataModel::LinearMemory(LinearMemoryOptions {
2444 memory: Some((dst_mem, dst_ty)),
2445 realloc: _,
2446 }),
2447 ) => self.module.import_transcoder(Transcoder {
2448 from_memory: src_mem,
2449 from_memory64: src_ty.idx_type == IndexType::I64,
2450 to_memory: dst_mem,
2451 to_memory64: dst_ty.idx_type == IndexType::I64,
2452 op,
2453 }),
2454 (DataModel::LinearMemory(LinearMemoryOptions { memory: None, .. }), _)
2455 | (_, DataModel::LinearMemory(LinearMemoryOptions { memory: None, .. })) => {
2456 unreachable!()
2457 }
2458 }
2459 }
2460
2461 fn begin_translate_sequence<'c>(
2470 &mut self,
2471 src: &Source<'c>,
2472 dst: &Destination<'c>,
2473 src_element_size: u32,
2474 src_element_align: u32,
2475 dst_element_size: u32,
2476 dst_element_align: u32,
2477 ) -> SequenceTranslation<'c> {
2478 let src_mem_opts = match &src.opts().data_model {
2479 DataModel::Gc {} => todo!("CM+GC"),
2480 DataModel::LinearMemory(opts) => opts,
2481 };
2482 let dst_mem_opts = match &dst.opts().data_model {
2483 DataModel::Gc {} => todo!("CM+GC"),
2484 DataModel::LinearMemory(opts) => opts,
2485 };
2486
2487 let src_opts = src.opts();
2488 let dst_opts = dst.opts();
2489
2490 match src {
2495 Source::Stack(s) => {
2496 assert_eq!(s.locals.len(), 2);
2497 self.stack_get(&s.slice(0..1), src_mem_opts.ptr());
2498 self.stack_get(&s.slice(1..2), src_mem_opts.ptr());
2499 }
2500 Source::Memory(mem) => {
2501 self.ptr_load(mem);
2502 self.ptr_load(&mem.bump(src_mem_opts.ptr_size().into()));
2503 }
2504 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
2505 }
2506 let src_len = self.local_set_new_tmp(src_mem_opts.ptr());
2507 let src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
2508
2509 let src_byte_len =
2511 self.calculate_list_byte_len(src_mem_opts, src_len.idx, src_element_size);
2512 let dst_byte_len = if src_element_size == dst_element_size {
2513 self.convert_src_len_to_dst(src_byte_len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2514 self.local_set_new_tmp(dst_mem_opts.ptr())
2515 } else if src_mem_opts.ptr() == dst_mem_opts.ptr() {
2516 self.calculate_list_byte_len(dst_mem_opts, src_len.idx, dst_element_size)
2517 } else {
2518 self.convert_src_len_to_dst(src_len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2519 let tmp = self.local_set_new_tmp(dst_mem_opts.ptr());
2520 let ret = self.calculate_list_byte_len(dst_mem_opts, tmp.idx, dst_element_size);
2521 self.free_temp_local(tmp);
2522 ret
2523 };
2524
2525 let src_mem = self.memory_operand(
2528 src_opts,
2529 src_ptr,
2530 AllocSize::Local(src_byte_len.idx),
2531 src_element_align,
2532 Trap::ListOutOfBounds,
2533 );
2534
2535 let dst_mem = self.malloc(
2540 dst_opts,
2541 AllocSize::Local(dst_byte_len.idx),
2542 dst_element_align,
2543 Trap::ListOutOfBounds,
2544 );
2545
2546 self.free_temp_local(src_byte_len);
2547 self.free_temp_local(dst_byte_len);
2548
2549 let loop_state = if src_element_size > 0 || dst_element_size > 0 {
2553 self.instruction(Block(BlockType::Empty));
2554
2555 self.instruction(LocalGet(src_len.idx));
2557 let remaining = self.local_tee_new_tmp(src_mem_opts.ptr());
2558 self.ptr_eqz(src_mem_opts);
2559 self.instruction(BrIf(0));
2560
2561 self.instruction(LocalGet(src_mem.addr.idx));
2563 let cur_src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
2564 self.instruction(LocalGet(dst_mem.addr.idx));
2565 let cur_dst_ptr = self.local_set_new_tmp(dst_mem_opts.ptr());
2566
2567 self.instruction(Loop(BlockType::Empty));
2568
2569 Some(SequenceLoopState {
2570 remaining,
2571 cur_src_ptr,
2572 cur_dst_ptr,
2573 })
2574 } else {
2575 None
2576 };
2577
2578 SequenceTranslation {
2579 src_len,
2580 src_mem,
2581 dst_mem,
2582 src_opts,
2583 dst_opts,
2584 src_mem_opts,
2585 dst_mem_opts,
2586 loop_state,
2587 }
2588 }
2589
2590 fn end_translate_sequence(&mut self, seq: SequenceTranslation<'_>, dst: &Destination) {
2596 if let Some(loop_state) = seq.loop_state {
2597 self.instruction(LocalGet(loop_state.remaining.idx));
2600 self.ptr_iconst(seq.src_mem_opts, -1);
2601 self.ptr_add(seq.src_mem_opts);
2602 self.instruction(LocalTee(loop_state.remaining.idx));
2603 self.ptr_br_if(seq.src_mem_opts, 0);
2604 self.instruction(End); self.instruction(End); self.free_temp_local(loop_state.cur_dst_ptr);
2608 self.free_temp_local(loop_state.cur_src_ptr);
2609 self.free_temp_local(loop_state.remaining);
2610 }
2611
2612 match dst {
2614 Destination::Stack(s, _) => {
2615 self.instruction(LocalGet(seq.dst_mem.addr.idx));
2616 self.stack_set(&s[..1], seq.dst_mem_opts.ptr());
2617 self.convert_src_len_to_dst(
2618 seq.src_len.idx,
2619 seq.src_mem_opts.ptr(),
2620 seq.dst_mem_opts.ptr(),
2621 );
2622 self.stack_set(&s[1..], seq.dst_mem_opts.ptr());
2623 }
2624 Destination::Memory(mem) => {
2625 self.instruction(LocalGet(mem.addr.idx));
2626 self.instruction(LocalGet(seq.dst_mem.addr.idx));
2627 self.ptr_store(mem);
2628 self.instruction(LocalGet(mem.addr.idx));
2629 self.convert_src_len_to_dst(
2630 seq.src_len.idx,
2631 seq.src_mem_opts.ptr(),
2632 seq.dst_mem_opts.ptr(),
2633 );
2634 self.ptr_store(&mem.bump(seq.dst_mem_opts.ptr_size().into()));
2635 }
2636 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
2637 }
2638
2639 self.free_temp_local(seq.src_len);
2640 self.free_temp_local(seq.src_mem.addr);
2641 self.free_temp_local(seq.dst_mem.addr);
2642 }
2643
2644 fn translate_list(
2645 &mut self,
2646 src_ty: TypeListIndex,
2647 src: &Source<'_>,
2648 dst_ty: &InterfaceType,
2649 dst: &Destination,
2650 ) {
2651 let src_mem_opts = match &src.opts().data_model {
2652 DataModel::Gc {} => todo!("CM+GC"),
2653 DataModel::LinearMemory(opts) => opts,
2654 };
2655 let dst_mem_opts = match &dst.opts().data_model {
2656 DataModel::Gc {} => todo!("CM+GC"),
2657 DataModel::LinearMemory(opts) => opts,
2658 };
2659
2660 let src_element_ty = &self.types[src_ty].element;
2661 let dst_element_ty = match dst_ty {
2662 InterfaceType::List(r) => &self.types[*r].element,
2663 _ => panic!("expected a list"),
2664 };
2665 let (src_size, src_align) = self.types.size_align(src_mem_opts, src_element_ty);
2666 let (dst_size, dst_align) = self.types.size_align(dst_mem_opts, dst_element_ty);
2667
2668 let seq = self.begin_translate_sequence(src, dst, src_size, src_align, dst_size, dst_align);
2669
2670 if let Some(ref loop_state) = seq.loop_state {
2671 let element_src = Source::Memory(Memory {
2672 opts: seq.src_opts,
2673 offset: 0,
2674 addr: TempLocal::new(loop_state.cur_src_ptr.idx, loop_state.cur_src_ptr.ty),
2675 });
2676 let element_dst = Destination::Memory(Memory {
2677 opts: seq.dst_opts,
2678 offset: 0,
2679 addr: TempLocal::new(loop_state.cur_dst_ptr.idx, loop_state.cur_dst_ptr.ty),
2680 });
2681 self.translate(src_element_ty, &element_src, dst_element_ty, &element_dst);
2682
2683 if src_size > 0 {
2684 self.instruction(LocalGet(loop_state.cur_src_ptr.idx));
2685 self.ptr_uconst(src_mem_opts, src_size);
2686 self.ptr_add(src_mem_opts);
2687 self.instruction(LocalSet(loop_state.cur_src_ptr.idx));
2688 }
2689 if dst_size > 0 {
2690 self.instruction(LocalGet(loop_state.cur_dst_ptr.idx));
2691 self.ptr_uconst(dst_mem_opts, dst_size);
2692 self.ptr_add(dst_mem_opts);
2693 self.instruction(LocalSet(loop_state.cur_dst_ptr.idx));
2694 }
2695 }
2696
2697 self.end_translate_sequence(seq, dst);
2698 }
2699
2700 fn translate_map(
2706 &mut self,
2707 src_ty: TypeMapIndex,
2708 src: &Source<'_>,
2709 dst_ty: &InterfaceType,
2710 dst: &Destination,
2711 ) {
2712 let src_mem_opts = match &src.opts().data_model {
2713 DataModel::Gc {} => todo!("CM+GC"),
2714 DataModel::LinearMemory(opts) => opts,
2715 };
2716 let dst_mem_opts = match &dst.opts().data_model {
2717 DataModel::Gc {} => todo!("CM+GC"),
2718 DataModel::LinearMemory(opts) => opts,
2719 };
2720
2721 let src_map_ty = &self.types[src_ty];
2722 let dst_map_ty = match dst_ty {
2723 InterfaceType::Map(r) => &self.types[*r],
2724 _ => panic!("expected a map"),
2725 };
2726
2727 let src_key_abi = self.types.canonical_abi(&src_map_ty.key);
2729 let src_value_abi = self.types.canonical_abi(&src_map_ty.value);
2730 let src_entry_abi = CanonicalAbiInfo::record([src_key_abi, src_value_abi].into_iter());
2731 let (src_tuple_size, src_entry_align) = src_mem_opts.sizealign(&src_entry_abi);
2732 let src_value_offset = {
2733 let mut offset = 0u32;
2734 if src_mem_opts.memory64() {
2735 src_key_abi.next_field64(&mut offset);
2736 src_value_abi.next_field64(&mut offset)
2737 } else {
2738 src_key_abi.next_field32(&mut offset);
2739 src_value_abi.next_field32(&mut offset)
2740 }
2741 };
2742
2743 let dst_key_abi = self.types.canonical_abi(&dst_map_ty.key);
2744 let dst_value_abi = self.types.canonical_abi(&dst_map_ty.value);
2745 let dst_entry_abi = CanonicalAbiInfo::record([dst_key_abi, dst_value_abi].into_iter());
2746 let (dst_tuple_size, dst_entry_align) = dst_mem_opts.sizealign(&dst_entry_abi);
2747 let dst_value_offset = {
2748 let mut offset = 0u32;
2749 if dst_mem_opts.memory64() {
2750 dst_key_abi.next_field64(&mut offset);
2751 dst_value_abi.next_field64(&mut offset)
2752 } else {
2753 dst_key_abi.next_field32(&mut offset);
2754 dst_value_abi.next_field32(&mut offset)
2755 }
2756 };
2757
2758 let seq = self.begin_translate_sequence(
2759 src,
2760 dst,
2761 src_tuple_size,
2762 src_entry_align,
2763 dst_tuple_size,
2764 dst_entry_align,
2765 );
2766
2767 if let Some(ref loop_state) = seq.loop_state {
2768 let key_src = Source::Memory(Memory {
2769 opts: seq.src_opts,
2770 offset: 0,
2771 addr: TempLocal::new(loop_state.cur_src_ptr.idx, src_mem_opts.ptr()),
2772 });
2773 let key_dst = Destination::Memory(Memory {
2774 opts: seq.dst_opts,
2775 offset: 0,
2776 addr: TempLocal::new(loop_state.cur_dst_ptr.idx, dst_mem_opts.ptr()),
2777 });
2778 self.translate(&src_map_ty.key, &key_src, &dst_map_ty.key, &key_dst);
2779
2780 let value_src = Source::Memory(Memory {
2781 opts: seq.src_opts,
2782 offset: src_value_offset,
2783 addr: TempLocal::new(loop_state.cur_src_ptr.idx, src_mem_opts.ptr()),
2784 });
2785 let value_dst = Destination::Memory(Memory {
2786 opts: seq.dst_opts,
2787 offset: dst_value_offset,
2788 addr: TempLocal::new(loop_state.cur_dst_ptr.idx, dst_mem_opts.ptr()),
2789 });
2790 self.translate(&src_map_ty.value, &value_src, &dst_map_ty.value, &value_dst);
2791
2792 if src_tuple_size > 0 {
2794 self.instruction(LocalGet(loop_state.cur_src_ptr.idx));
2795 self.ptr_uconst(src_mem_opts, src_tuple_size);
2796 self.ptr_add(src_mem_opts);
2797 self.instruction(LocalSet(loop_state.cur_src_ptr.idx));
2798 }
2799 if dst_tuple_size > 0 {
2800 self.instruction(LocalGet(loop_state.cur_dst_ptr.idx));
2801 self.ptr_uconst(dst_mem_opts, dst_tuple_size);
2802 self.ptr_add(dst_mem_opts);
2803 self.instruction(LocalSet(loop_state.cur_dst_ptr.idx));
2804 }
2805 }
2806
2807 self.end_translate_sequence(seq, dst);
2808 }
2809
2810 fn calculate_list_byte_len(
2811 &mut self,
2812 opts: &LinearMemoryOptions,
2813 len_local: u32,
2814 elt_size: u32,
2815 ) -> TempLocal {
2816 if elt_size == 0 {
2819 self.ptr_uconst(opts, 0);
2820 return self.local_set_new_tmp(opts.ptr());
2821 }
2822
2823 if elt_size == 1 {
2831 if let ValType::I64 = opts.ptr() {
2832 self.instruction(LocalGet(len_local));
2833 self.instruction(I64Const(32));
2834 self.instruction(I64ShrU);
2835 self.instruction(I32WrapI64);
2836 self.instruction(If(BlockType::Empty));
2837 self.trap(Trap::ListOutOfBounds);
2838 self.instruction(End);
2839 }
2840 self.instruction(LocalGet(len_local));
2841 return self.local_set_new_tmp(opts.ptr());
2842 }
2843
2844 self.instruction(Block(BlockType::Empty));
2849 self.instruction(Block(BlockType::Empty));
2850 self.instruction(LocalGet(len_local));
2851 match opts.ptr() {
2852 ValType::I32 => self.instruction(I64ExtendI32U),
2856
2857 ValType::I64 => {
2861 self.instruction(I64Const(32));
2862 self.instruction(I64ShrU);
2863 self.instruction(I32WrapI64);
2864 self.instruction(BrIf(0));
2865 self.instruction(LocalGet(len_local));
2866 }
2867
2868 _ => unreachable!(),
2869 }
2870
2871 self.instruction(I64Const(elt_size.into()));
2880 self.instruction(I64Mul);
2881 let tmp = self.local_tee_new_tmp(ValType::I64);
2882 self.instruction(I64Const(32));
2885 self.instruction(I64ShrU);
2886 self.instruction(I64Eqz);
2887 self.instruction(BrIf(1));
2888 self.instruction(End);
2889 self.trap(Trap::ListOutOfBounds);
2890 self.instruction(End);
2891
2892 if opts.ptr() == ValType::I64 {
2896 tmp
2897 } else {
2898 self.instruction(LocalGet(tmp.idx));
2899 self.instruction(I32WrapI64);
2900 self.free_temp_local(tmp);
2901 self.local_set_new_tmp(ValType::I32)
2902 }
2903 }
2904
2905 fn convert_src_len_to_dst(
2906 &mut self,
2907 src_len_local: u32,
2908 src_ptr_ty: ValType,
2909 dst_ptr_ty: ValType,
2910 ) {
2911 self.instruction(LocalGet(src_len_local));
2912 match (src_ptr_ty, dst_ptr_ty) {
2913 (ValType::I32, ValType::I64) => self.instruction(I64ExtendI32U),
2914 (ValType::I64, ValType::I32) => self.instruction(I32WrapI64),
2915 (src, dst) => assert_eq!(src, dst),
2916 }
2917 }
2918
2919 fn translate_record(
2920 &mut self,
2921 src_ty: TypeRecordIndex,
2922 src: &Source<'_>,
2923 dst_ty: &InterfaceType,
2924 dst: &Destination,
2925 ) {
2926 let src_ty = &self.types[src_ty];
2927 let dst_ty = match dst_ty {
2928 InterfaceType::Record(r) => &self.types[*r],
2929 _ => panic!("expected a record"),
2930 };
2931
2932 assert_eq!(src_ty.fields.len(), dst_ty.fields.len());
2934
2935 let mut src_fields = HashMap::new();
2939 for (i, src) in src
2940 .record_field_srcs(self.types, src_ty.fields.iter().map(|f| f.ty))
2941 .enumerate()
2942 {
2943 let field = &src_ty.fields[i];
2944 src_fields.insert(&field.name, (src, &field.ty));
2945 }
2946
2947 for (i, dst) in dst
2956 .record_field_dsts(self.types, dst_ty.fields.iter().map(|f| f.ty))
2957 .enumerate()
2958 {
2959 let field = &dst_ty.fields[i];
2960 let (src, src_ty) = &src_fields[&field.name];
2961 self.translate(src_ty, src, &field.ty, &dst);
2962 }
2963 }
2964
2965 fn translate_flags(
2966 &mut self,
2967 src_ty: TypeFlagsIndex,
2968 src: &Source<'_>,
2969 dst_ty: &InterfaceType,
2970 dst: &Destination,
2971 ) {
2972 let src_ty = &self.types[src_ty];
2973 let dst_ty = match dst_ty {
2974 InterfaceType::Flags(r) => &self.types[*r],
2975 _ => panic!("expected a record"),
2976 };
2977
2978 assert_eq!(src_ty.names, dst_ty.names);
2986 let cnt = src_ty.names.len();
2987 match FlagsSize::from_count(cnt) {
2988 FlagsSize::Size0 => {}
2989 FlagsSize::Size1 => {
2990 let mask = if cnt == 8 { 0xff } else { (1 << cnt) - 1 };
2991 self.convert_u8_mask(src, dst, mask);
2992 }
2993 FlagsSize::Size2 => {
2994 let mask = if cnt == 16 { 0xffff } else { (1 << cnt) - 1 };
2995 self.convert_u16_mask(src, dst, mask);
2996 }
2997 FlagsSize::Size4Plus(n) => {
2998 let srcs = src.record_field_srcs(self.types, (0..n).map(|_| InterfaceType::U32));
2999 let dsts = dst.record_field_dsts(self.types, (0..n).map(|_| InterfaceType::U32));
3000 let n = usize::from(n);
3001 for (i, (src, dst)) in srcs.zip(dsts).enumerate() {
3002 let mask = if i == n - 1 && (cnt % 32 != 0) {
3003 (1 << (cnt % 32)) - 1
3004 } else {
3005 0xffffffff
3006 };
3007 self.convert_u32_mask(&src, &dst, mask);
3008 }
3009 }
3010 }
3011 }
3012
3013 fn translate_tuple(
3014 &mut self,
3015 src_ty: TypeTupleIndex,
3016 src: &Source<'_>,
3017 dst_ty: &InterfaceType,
3018 dst: &Destination,
3019 ) {
3020 let src_ty = &self.types[src_ty];
3021 let dst_ty = match dst_ty {
3022 InterfaceType::Tuple(t) => &self.types[*t],
3023 _ => panic!("expected a tuple"),
3024 };
3025
3026 assert_eq!(src_ty.types.len(), dst_ty.types.len());
3028
3029 let srcs = src
3030 .record_field_srcs(self.types, src_ty.types.iter().copied())
3031 .zip(src_ty.types.iter());
3032 let dsts = dst
3033 .record_field_dsts(self.types, dst_ty.types.iter().copied())
3034 .zip(dst_ty.types.iter());
3035 for ((src, src_ty), (dst, dst_ty)) in srcs.zip(dsts) {
3036 self.translate(src_ty, &src, dst_ty, &dst);
3037 }
3038 }
3039
3040 fn translate_fixed_length_list(
3041 &mut self,
3042 src_ty: TypeFixedLengthListIndex,
3043 src: &Source<'_>,
3044 dst_ty: &InterfaceType,
3045 dst: &Destination,
3046 ) {
3047 let src_ty = &self.types[src_ty];
3048 let dst_ty = match dst_ty {
3049 InterfaceType::FixedLengthList(t) => &self.types[*t],
3050 _ => panic!("expected a fixed size list"),
3051 };
3052
3053 assert_eq!(src_ty.size, dst_ty.size);
3055
3056 match (&src, &dst) {
3057 (Source::Memory(src_mem), Destination::Memory(dst_mem)) => {
3059 let src_mem_opts = match &src_mem.opts.data_model {
3060 DataModel::Gc {} => todo!("CM+GC"),
3061 DataModel::LinearMemory(opts) => opts,
3062 };
3063 let dst_mem_opts = match &dst_mem.opts.data_model {
3064 DataModel::Gc {} => todo!("CM+GC"),
3065 DataModel::LinearMemory(opts) => opts,
3066 };
3067 let src_element_bytes = self.types.size_align(src_mem_opts, &src_ty.element).0;
3068 let dst_element_bytes = self.types.size_align(dst_mem_opts, &dst_ty.element).0;
3069 assert_ne!(src_element_bytes, 0);
3070 assert_ne!(dst_element_bytes, 0);
3071
3072 self.instruction(LocalGet(src_mem.addr.idx));
3075 if src_mem.offset != 0 {
3076 self.ptr_uconst(src_mem_opts, src_mem.offset);
3077 self.ptr_add(src_mem_opts);
3078 }
3079 let cur_src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
3080 self.instruction(LocalGet(dst_mem.addr.idx));
3081 if dst_mem.offset != 0 {
3082 self.ptr_uconst(dst_mem_opts, dst_mem.offset);
3083 self.ptr_add(dst_mem_opts);
3084 }
3085 let cur_dst_ptr = self.local_set_new_tmp(dst_mem_opts.ptr());
3086
3087 self.instruction(I32Const(src_ty.size as i32));
3088 let remaining = self.local_set_new_tmp(ValType::I32);
3089
3090 self.instruction(Loop(BlockType::Empty));
3091
3092 let element_src = Source::Memory(Memory {
3094 opts: src_mem.opts,
3095 offset: 0,
3096 addr: TempLocal::new(cur_src_ptr.idx, cur_src_ptr.ty),
3097 });
3098 let element_dst = Destination::Memory(Memory {
3099 opts: dst_mem.opts,
3100 offset: 0,
3101 addr: TempLocal::new(cur_dst_ptr.idx, cur_dst_ptr.ty),
3102 });
3103 self.translate(&src_ty.element, &element_src, &dst_ty.element, &element_dst);
3104
3105 self.instruction(LocalGet(cur_src_ptr.idx));
3107 self.ptr_uconst(src_mem_opts, src_element_bytes);
3108 self.ptr_add(src_mem_opts);
3109 self.instruction(LocalSet(cur_src_ptr.idx));
3110 self.instruction(LocalGet(cur_dst_ptr.idx));
3111 self.ptr_uconst(dst_mem_opts, dst_element_bytes);
3112 self.ptr_add(dst_mem_opts);
3113 self.instruction(LocalSet(cur_dst_ptr.idx));
3114
3115 self.instruction(LocalGet(remaining.idx));
3118 self.ptr_iconst(src_mem_opts, -1);
3119 self.ptr_add(src_mem_opts);
3120 self.instruction(LocalTee(remaining.idx));
3121 self.ptr_br_if(src_mem_opts, 0);
3122 self.instruction(End); self.free_temp_local(cur_dst_ptr);
3125 self.free_temp_local(cur_src_ptr);
3126 self.free_temp_local(remaining);
3127 return;
3128 }
3129 (_, _) => {
3131 assert!(
3133 src_ty.size as usize <= MAX_FLAT_PARAMS
3134 && dst_ty.size as usize <= MAX_FLAT_PARAMS
3135 );
3136 let srcs =
3137 src.record_field_srcs(self.types, (0..src_ty.size).map(|_| src_ty.element));
3138 let dsts =
3139 dst.record_field_dsts(self.types, (0..dst_ty.size).map(|_| dst_ty.element));
3140 for (src, dst) in srcs.zip(dsts) {
3141 self.translate(&src_ty.element, &src, &dst_ty.element, &dst);
3142 }
3143 }
3144 }
3145 }
3146
3147 fn translate_variant(
3148 &mut self,
3149 src_ty: TypeVariantIndex,
3150 src: &Source<'_>,
3151 dst_ty: &InterfaceType,
3152 dst: &Destination,
3153 ) {
3154 let src_ty = &self.types[src_ty];
3155 let dst_ty = match dst_ty {
3156 InterfaceType::Variant(t) => &self.types[*t],
3157 _ => panic!("expected a variant"),
3158 };
3159
3160 let src_info = variant_info(self.types, src_ty.cases.iter().map(|(_, c)| c.as_ref()));
3161 let dst_info = variant_info(self.types, dst_ty.cases.iter().map(|(_, c)| c.as_ref()));
3162
3163 let iter = src_ty
3164 .cases
3165 .iter()
3166 .enumerate()
3167 .map(|(src_i, (src_case, src_case_ty))| {
3168 let dst_i = dst_ty
3169 .cases
3170 .iter()
3171 .position(|(c, _)| c == src_case)
3172 .unwrap();
3173 let dst_case_ty = &dst_ty.cases[dst_i];
3174 let src_i = u32::try_from(src_i).unwrap();
3175 let dst_i = u32::try_from(dst_i).unwrap();
3176 VariantCase {
3177 src_i,
3178 src_ty: src_case_ty.as_ref(),
3179 dst_i,
3180 dst_ty: dst_case_ty.as_ref(),
3181 }
3182 });
3183 self.convert_variant(src, &src_info, dst, &dst_info, iter);
3184 }
3185
3186 fn translate_enum(
3187 &mut self,
3188 src_ty: TypeEnumIndex,
3189 src: &Source<'_>,
3190 dst_ty: &InterfaceType,
3191 dst: &Destination,
3192 ) {
3193 let src_ty = &self.types[src_ty];
3194 let dst_ty = match dst_ty {
3195 InterfaceType::Enum(t) => &self.types[*t],
3196 _ => panic!("expected an option"),
3197 };
3198
3199 debug_assert_eq!(src_ty.info.size, dst_ty.info.size);
3200 debug_assert_eq!(src_ty.names.len(), dst_ty.names.len());
3201 debug_assert!(
3202 src_ty
3203 .names
3204 .iter()
3205 .zip(dst_ty.names.iter())
3206 .all(|(a, b)| a == b)
3207 );
3208
3209 match src {
3211 Source::Stack(s) => self.stack_get(&s.slice(0..1), ValType::I32),
3212 Source::Memory(mem) => match src_ty.info.size {
3213 DiscriminantSize::Size1 => self.i32_load8u(mem),
3214 DiscriminantSize::Size2 => self.i32_load16u(mem),
3215 DiscriminantSize::Size4 => self.i32_load(mem),
3216 },
3217 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
3218 }
3219 let tmp = self.local_tee_new_tmp(ValType::I32);
3220
3221 self.instruction(I32Const(i32::try_from(src_ty.names.len()).unwrap()));
3223 self.instruction(I32GeU);
3224 self.instruction(If(BlockType::Empty));
3225 self.trap(Trap::InvalidDiscriminant);
3226 self.instruction(End);
3227
3228 match dst {
3230 Destination::Stack(stack, _) => {
3231 self.local_get_tmp(&tmp);
3232 self.stack_set(&stack[..1], ValType::I32)
3233 }
3234 Destination::Memory(mem) => {
3235 self.push_dst_addr(dst);
3236 self.local_get_tmp(&tmp);
3237 match dst_ty.info.size {
3238 DiscriminantSize::Size1 => self.i32_store8(mem),
3239 DiscriminantSize::Size2 => self.i32_store16(mem),
3240 DiscriminantSize::Size4 => self.i32_store(mem),
3241 }
3242 }
3243 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3244 }
3245 self.free_temp_local(tmp);
3246 }
3247
3248 fn translate_option(
3249 &mut self,
3250 src_ty: TypeOptionIndex,
3251 src: &Source<'_>,
3252 dst_ty: &InterfaceType,
3253 dst: &Destination,
3254 ) {
3255 let src_ty = &self.types[src_ty].ty;
3256 let dst_ty = match dst_ty {
3257 InterfaceType::Option(t) => &self.types[*t].ty,
3258 _ => panic!("expected an option"),
3259 };
3260 let src_ty = Some(src_ty);
3261 let dst_ty = Some(dst_ty);
3262
3263 let src_info = variant_info(self.types, [None, src_ty]);
3264 let dst_info = variant_info(self.types, [None, dst_ty]);
3265
3266 self.convert_variant(
3267 src,
3268 &src_info,
3269 dst,
3270 &dst_info,
3271 [
3272 VariantCase {
3273 src_i: 0,
3274 dst_i: 0,
3275 src_ty: None,
3276 dst_ty: None,
3277 },
3278 VariantCase {
3279 src_i: 1,
3280 dst_i: 1,
3281 src_ty,
3282 dst_ty,
3283 },
3284 ]
3285 .into_iter(),
3286 );
3287 }
3288
3289 fn translate_result(
3290 &mut self,
3291 src_ty: TypeResultIndex,
3292 src: &Source<'_>,
3293 dst_ty: &InterfaceType,
3294 dst: &Destination,
3295 ) {
3296 let src_ty = &self.types[src_ty];
3297 let dst_ty = match dst_ty {
3298 InterfaceType::Result(t) => &self.types[*t],
3299 _ => panic!("expected a result"),
3300 };
3301
3302 let src_info = variant_info(self.types, [src_ty.ok.as_ref(), src_ty.err.as_ref()]);
3303 let dst_info = variant_info(self.types, [dst_ty.ok.as_ref(), dst_ty.err.as_ref()]);
3304
3305 self.convert_variant(
3306 src,
3307 &src_info,
3308 dst,
3309 &dst_info,
3310 [
3311 VariantCase {
3312 src_i: 0,
3313 dst_i: 0,
3314 src_ty: src_ty.ok.as_ref(),
3315 dst_ty: dst_ty.ok.as_ref(),
3316 },
3317 VariantCase {
3318 src_i: 1,
3319 dst_i: 1,
3320 src_ty: src_ty.err.as_ref(),
3321 dst_ty: dst_ty.err.as_ref(),
3322 },
3323 ]
3324 .into_iter(),
3325 );
3326 }
3327
3328 fn convert_variant<'c>(
3329 &mut self,
3330 src: &Source<'_>,
3331 src_info: &VariantInfo,
3332 dst: &Destination,
3333 dst_info: &VariantInfo,
3334 src_cases: impl ExactSizeIterator<Item = VariantCase<'c>>,
3335 ) {
3336 let outer_block_ty = match dst {
3339 Destination::Stack(dst_flat, _) => match dst_flat.len() {
3340 0 => BlockType::Empty,
3341 1 => BlockType::Result(dst_flat[0]),
3342 _ => {
3343 let ty = self.module.core_types.function(&[], &dst_flat);
3344 BlockType::FunctionType(ty)
3345 }
3346 },
3347 Destination::Memory(_) => BlockType::Empty,
3348 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3349 };
3350 self.instruction(Block(outer_block_ty));
3351
3352 let src_cases_len = src_cases.len();
3355 for _ in 0..src_cases_len - 1 {
3356 self.instruction(Block(BlockType::Empty));
3357 }
3358
3359 self.instruction(Block(BlockType::Empty));
3361
3362 self.instruction(Block(BlockType::Empty));
3365
3366 match src {
3368 Source::Stack(s) => self.stack_get(&s.slice(0..1), ValType::I32),
3369 Source::Memory(mem) => match src_info.size {
3370 DiscriminantSize::Size1 => self.i32_load8u(mem),
3371 DiscriminantSize::Size2 => self.i32_load16u(mem),
3372 DiscriminantSize::Size4 => self.i32_load(mem),
3373 },
3374 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
3375 }
3376
3377 let mut targets = Vec::new();
3380 for i in 0..src_cases_len {
3381 targets.push((i + 1) as u32);
3382 }
3383 self.instruction(BrTable(targets[..].into(), 0));
3384 self.instruction(End); self.trap(Trap::InvalidDiscriminant);
3387 self.instruction(End); let src_cases_len = u32::try_from(src_cases_len).unwrap();
3394 for case in src_cases {
3395 let VariantCase {
3396 src_i,
3397 src_ty,
3398 dst_i,
3399 dst_ty,
3400 } = case;
3401
3402 self.push_dst_addr(dst);
3405 self.instruction(I32Const(dst_i as i32));
3406 match dst {
3407 Destination::Stack(stack, _) => self.stack_set(&stack[..1], ValType::I32),
3408 Destination::Memory(mem) => match dst_info.size {
3409 DiscriminantSize::Size1 => self.i32_store8(mem),
3410 DiscriminantSize::Size2 => self.i32_store16(mem),
3411 DiscriminantSize::Size4 => self.i32_store(mem),
3412 },
3413 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3414 }
3415
3416 let src_payload = src.payload_src(self.types, src_info, src_ty);
3417 let dst_payload = dst.payload_dst(self.types, dst_info, dst_ty);
3418
3419 match (src_ty, dst_ty) {
3422 (Some(src_ty), Some(dst_ty)) => {
3423 self.translate(src_ty, &src_payload, dst_ty, &dst_payload);
3424 }
3425 (None, None) => {}
3426 _ => unimplemented!(),
3427 }
3428
3429 if let Destination::Stack(payload_results, _) = dst_payload {
3436 if let Destination::Stack(dst_results, _) = dst {
3437 let remaining = &dst_results[1..][payload_results.len()..];
3438 for ty in remaining {
3439 match ty {
3440 ValType::I32 => self.instruction(I32Const(0)),
3441 ValType::I64 => self.instruction(I64Const(0)),
3442 ValType::F32 => self.instruction(F32Const(0.0.into())),
3443 ValType::F64 => self.instruction(F64Const(0.0.into())),
3444 _ => unreachable!(),
3445 }
3446 }
3447 }
3448 }
3449
3450 if src_i != src_cases_len - 1 {
3453 self.instruction(Br(src_cases_len - src_i - 1));
3454 }
3455 self.instruction(End); }
3457 }
3458
3459 fn translate_future(
3460 &mut self,
3461 src_ty: TypeFutureTableIndex,
3462 src: &Source<'_>,
3463 dst_ty: &InterfaceType,
3464 dst: &Destination,
3465 ) {
3466 let dst_ty = match dst_ty {
3467 InterfaceType::Future(t) => *t,
3468 _ => panic!("expected a `Future`"),
3469 };
3470 let transfer = self.module.import_future_transfer();
3471 self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3472 }
3473
3474 fn translate_stream(
3475 &mut self,
3476 src_ty: TypeStreamTableIndex,
3477 src: &Source<'_>,
3478 dst_ty: &InterfaceType,
3479 dst: &Destination,
3480 ) {
3481 let dst_ty = match dst_ty {
3482 InterfaceType::Stream(t) => *t,
3483 _ => panic!("expected a `Stream`"),
3484 };
3485 let transfer = self.module.import_stream_transfer();
3486 self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3487 }
3488
3489 fn translate_error_context(
3490 &mut self,
3491 src_ty: TypeComponentLocalErrorContextTableIndex,
3492 src: &Source<'_>,
3493 dst_ty: &InterfaceType,
3494 dst: &Destination,
3495 ) {
3496 let dst_ty = match dst_ty {
3497 InterfaceType::ErrorContext(t) => *t,
3498 _ => panic!("expected an `ErrorContext`"),
3499 };
3500 let transfer = self.module.import_error_context_transfer();
3501 self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3502 }
3503
3504 fn translate_own(
3505 &mut self,
3506 src_ty: TypeResourceTableIndex,
3507 src: &Source<'_>,
3508 dst_ty: &InterfaceType,
3509 dst: &Destination,
3510 ) {
3511 let dst_ty = match dst_ty {
3512 InterfaceType::Own(t) => *t,
3513 _ => panic!("expected an `Own`"),
3514 };
3515 let transfer = self.module.import_resource_transfer_own();
3516 self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3517 }
3518
3519 fn translate_borrow(
3520 &mut self,
3521 src_ty: TypeResourceTableIndex,
3522 src: &Source<'_>,
3523 dst_ty: &InterfaceType,
3524 dst: &Destination,
3525 ) {
3526 let dst_ty = match dst_ty {
3527 InterfaceType::Borrow(t) => *t,
3528 _ => panic!("expected an `Borrow`"),
3529 };
3530
3531 let transfer = self.module.import_resource_transfer_borrow();
3532 self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3533 }
3534
3535 fn translate_handle(
3543 &mut self,
3544 src_ty: u32,
3545 src: &Source<'_>,
3546 dst_ty: u32,
3547 dst: &Destination,
3548 transfer: FuncIndex,
3549 ) {
3550 self.push_dst_addr(dst);
3551 match src {
3552 Source::Memory(mem) => self.i32_load(mem),
3553 Source::Stack(stack) => self.stack_get(stack, ValType::I32),
3554 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
3555 }
3556 self.instruction(I32Const(src_ty as i32));
3557 self.instruction(I32Const(dst_ty as i32));
3558 self.instruction(Call(transfer.as_u32()));
3559 match dst {
3560 Destination::Memory(mem) => self.i32_store(mem),
3561 Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
3562 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3563 }
3564 }
3565
3566 fn trap_if_not_flag(&mut self, flags_global: GlobalIndex, flag_to_test: i32, trap: Trap) {
3567 self.instruction(GlobalGet(flags_global.as_u32()));
3568 self.instruction(I32Const(flag_to_test));
3569 self.instruction(I32And);
3570 self.instruction(I32Eqz);
3571 self.instruction(If(BlockType::Empty));
3572 self.trap(trap);
3573 self.instruction(End);
3574 }
3575
3576 fn set_flag(&mut self, flags_global: GlobalIndex, flag_to_set: i32, value: bool) {
3577 self.instruction(GlobalGet(flags_global.as_u32()));
3578 if value {
3579 self.instruction(I32Const(flag_to_set));
3580 self.instruction(I32Or);
3581 } else {
3582 self.instruction(I32Const(!flag_to_set));
3583 self.instruction(I32And);
3584 }
3585 self.instruction(GlobalSet(flags_global.as_u32()));
3586 }
3587
3588 fn assert_aligned(&mut self, ty: &InterfaceType, mem: &Memory) {
3589 let mem_opts = mem.mem_opts();
3590 if !self.module.tunables.debug_adapter_modules {
3591 return;
3592 }
3593 let align = self.types.align(mem_opts, ty);
3594 if align == 1 {
3595 return;
3596 }
3597 assert!(align.is_power_of_two());
3598 self.instruction(LocalGet(mem.addr.idx));
3599 self.ptr_uconst(mem_opts, mem.offset);
3600 self.ptr_add(mem_opts);
3601 self.ptr_uconst(mem_opts, align - 1);
3602 self.ptr_and(mem_opts);
3603 self.ptr_if(mem_opts, BlockType::Empty);
3604 self.trap(Trap::DebugAssertPointerAligned);
3605 self.instruction(End);
3606 }
3607
3608 fn malloc_abi<'c>(
3614 &mut self,
3615 opts: &'c Options,
3616 abi: &CanonicalAbiInfo,
3617 oob_trap: Trap,
3618 ) -> Memory<'c> {
3619 match &opts.data_model {
3620 DataModel::Gc {} => todo!("CM+GC"),
3621 DataModel::LinearMemory(mem_opts) => {
3622 let (size, align) = mem_opts.sizealign(abi);
3623 let size = AllocSize::Const(size);
3624 self.malloc(opts, size, align, oob_trap)
3625 }
3626 }
3627 }
3628
3629 fn malloc<'c>(
3635 &mut self,
3636 opts: &'c Options,
3637 size: AllocSize,
3638 align: u32,
3639 oob_trap: Trap,
3640 ) -> Memory<'c> {
3641 match &opts.data_model {
3642 DataModel::Gc {} => todo!("CM+GC"),
3643 DataModel::LinearMemory(mem_opts) => {
3644 let realloc = mem_opts.realloc.unwrap();
3645 self.ptr_uconst(mem_opts, 0);
3646 self.ptr_uconst(mem_opts, 0);
3647 self.ptr_uconst(mem_opts, align);
3648 self.alloc_size(mem_opts, &size);
3649 self.instruction(Call(realloc.as_u32()));
3650 let addr = self.local_set_new_tmp(mem_opts.ptr());
3651 self.memory_operand(opts, addr, size, align, oob_trap)
3652 }
3653 }
3654 }
3655
3656 fn realloc(
3662 &mut self,
3663 opts: &Options,
3664 ptr: &TempLocal,
3665 prev_size: AllocSize,
3666 size: AllocSize,
3667 align: u32,
3668 oob_trap: Trap,
3669 ) {
3670 match &opts.data_model {
3671 DataModel::Gc {} => todo!("CM+GC"),
3672 DataModel::LinearMemory(mem_opts) => {
3673 let realloc = mem_opts.realloc.unwrap();
3674 self.instruction(LocalGet(ptr.idx));
3675 self.alloc_size(mem_opts, &prev_size);
3676 self.ptr_uconst(mem_opts, align);
3677 self.alloc_size(mem_opts, &size);
3678 self.instruction(Call(realloc.as_u32()));
3679 self.instruction(LocalSet(ptr.idx));
3680 self.validate_guest_pointer(opts, &ptr, &size, align, oob_trap)
3681 }
3682 }
3683 }
3684
3685 fn memory_operand_abi<'c>(
3688 &mut self,
3689 opts: &'c Options,
3690 addr: TempLocal,
3691 abi: &CanonicalAbiInfo,
3692 oob_trap: Trap,
3693 ) -> Memory<'c> {
3694 match &opts.data_model {
3695 DataModel::Gc {} => todo!("CM+GC"),
3696 DataModel::LinearMemory(mem_opts) => {
3697 let (size, align) = mem_opts.sizealign(abi);
3698 self.memory_operand(opts, addr, AllocSize::Const(size), align, oob_trap)
3699 }
3700 }
3701 }
3702
3703 fn memory_operand<'c>(
3706 &mut self,
3707 opts: &'c Options,
3708 addr: TempLocal,
3709 size: AllocSize,
3710 align: u32,
3711 oob_trap: Trap,
3712 ) -> Memory<'c> {
3713 self.validate_guest_pointer(opts, &addr, &size, align, oob_trap);
3714 Memory {
3715 addr,
3716 opts,
3717 offset: 0,
3718 }
3719 }
3720
3721 fn validate_guest_pointer(
3729 &mut self,
3730 opts: &Options,
3731 addr: &TempLocal,
3732 size: &AllocSize,
3733 align: u32,
3734 oob_trap: Trap,
3735 ) {
3736 let mem_opts = match &opts.data_model {
3737 DataModel::Gc {} => todo!("CM+GC"),
3738 DataModel::LinearMemory(mem_opts) => mem_opts,
3739 };
3740
3741 if align != 1 {
3744 self.instruction(LocalGet(addr.idx));
3745 assert!(align.is_power_of_two());
3746 self.ptr_uconst(mem_opts, align - 1);
3747 self.ptr_and(mem_opts);
3748 self.ptr_if(mem_opts, BlockType::Empty);
3749 self.trap(Trap::UnalignedPointer);
3750 self.instruction(End);
3751 }
3752
3753 let extend_to_64 = |me: &mut Self| {
3754 if !mem_opts.memory64() {
3755 me.instruction(I64ExtendI32U);
3756 }
3757 };
3758
3759 self.instruction(Block(BlockType::Empty));
3760 self.instruction(Block(BlockType::Empty));
3761 let (memory, ty) = mem_opts.memory.unwrap();
3762
3763 self.instruction(MemorySize(memory.as_u32()));
3768 extend_to_64(self);
3769 self.instruction(I64Const(ty.page_size_log2.into()));
3770 self.instruction(I64Shl);
3771
3772 self.instruction(LocalGet(addr.idx));
3777 extend_to_64(self);
3778 self.alloc_size(mem_opts, size);
3779 extend_to_64(self);
3780 self.instruction(I64Add);
3781 if mem_opts.memory64() {
3782 let tmp = self.local_tee_new_tmp(ValType::I64);
3783 self.instruction(LocalGet(addr.idx));
3784 self.ptr_lt_u(mem_opts);
3785 self.instruction(BrIf(0));
3786 self.instruction(LocalGet(tmp.idx));
3787 self.free_temp_local(tmp);
3788 }
3789
3790 self.instruction(I64GeU);
3794 self.instruction(BrIf(1));
3795
3796 self.instruction(End);
3797 self.trap(oob_trap);
3798 self.instruction(End);
3799 }
3800
3801 fn local_tee_new_tmp(&mut self, ty: ValType) -> TempLocal {
3807 self.gen_temp_local(ty, LocalTee)
3808 }
3809
3810 fn local_set_new_tmp(&mut self, ty: ValType) -> TempLocal {
3813 self.gen_temp_local(ty, LocalSet)
3814 }
3815
3816 fn local_get_tmp(&mut self, local: &TempLocal) {
3817 self.instruction(LocalGet(local.idx));
3818 }
3819
3820 fn gen_temp_local(&mut self, ty: ValType, insn: fn(u32) -> Instruction<'static>) -> TempLocal {
3821 if let Some(idx) = self.free_locals.get_mut(&ty).and_then(|v| v.pop()) {
3824 self.instruction(insn(idx));
3825 return TempLocal {
3826 ty,
3827 idx,
3828 needs_free: true,
3829 };
3830 }
3831
3832 let locals = &mut self.module.funcs[self.result].locals;
3834 match locals.last_mut() {
3835 Some((cnt, prev_ty)) if ty == *prev_ty => *cnt += 1,
3836 _ => locals.push((1, ty)),
3837 }
3838 self.nlocals += 1;
3839 let idx = self.nlocals - 1;
3840 self.instruction(insn(idx));
3841 TempLocal {
3842 ty,
3843 idx,
3844 needs_free: true,
3845 }
3846 }
3847
3848 fn free_temp_local(&mut self, mut local: TempLocal) {
3851 assert!(local.needs_free);
3852 self.free_locals
3853 .entry(local.ty)
3854 .or_insert(Vec::new())
3855 .push(local.idx);
3856 local.needs_free = false;
3857 }
3858
3859 fn instruction(&mut self, instr: Instruction) {
3860 instr.encode(&mut self.code);
3861 }
3862
3863 fn trap(&mut self, trap: Trap) {
3864 let trap_func = self.module.import_trap();
3865 self.instruction(I32Const(trap as i32));
3866 self.instruction(Call(trap_func.as_u32()));
3867 self.instruction(Unreachable);
3868 }
3869
3870 fn flush_code(&mut self) {
3875 if self.code.is_empty() {
3876 return;
3877 }
3878 self.module.funcs[self.result]
3879 .body
3880 .push(Body::Raw(mem::take(&mut self.code)));
3881 }
3882
3883 fn finish(mut self) {
3884 self.instruction(End);
3887 self.flush_code();
3888
3889 self.module.funcs[self.result].filled_in = true;
3892 }
3893
3894 fn stack_get(&mut self, stack: &Stack<'_>, dst_ty: ValType) {
3902 assert_eq!(stack.locals.len(), 1);
3903 let (idx, src_ty) = stack.locals[0];
3904 self.instruction(LocalGet(idx));
3905 match (src_ty, dst_ty) {
3906 (ValType::I32, ValType::I32)
3907 | (ValType::I64, ValType::I64)
3908 | (ValType::F32, ValType::F32)
3909 | (ValType::F64, ValType::F64) => {}
3910
3911 (ValType::I32, ValType::F32) => self.instruction(F32ReinterpretI32),
3912 (ValType::I64, ValType::I32) => {
3913 self.assert_i64_upper_bits_not_set(idx);
3914 self.instruction(I32WrapI64);
3915 }
3916 (ValType::I64, ValType::F64) => self.instruction(F64ReinterpretI64),
3917 (ValType::I64, ValType::F32) => {
3918 self.assert_i64_upper_bits_not_set(idx);
3919 self.instruction(I32WrapI64);
3920 self.instruction(F32ReinterpretI32);
3921 }
3922
3923 (ValType::I32, ValType::I64)
3925 | (ValType::I32, ValType::F64)
3926 | (ValType::F32, ValType::I32)
3927 | (ValType::F32, ValType::I64)
3928 | (ValType::F32, ValType::F64)
3929 | (ValType::F64, ValType::I32)
3930 | (ValType::F64, ValType::I64)
3931 | (ValType::F64, ValType::F32)
3932
3933 | (ValType::Ref(_), _)
3935 | (_, ValType::Ref(_))
3936 | (ValType::V128, _)
3937 | (_, ValType::V128) => {
3938 panic!("cannot get {dst_ty:?} from {src_ty:?} local");
3939 }
3940 }
3941 }
3942
3943 fn assert_i64_upper_bits_not_set(&mut self, local: u32) {
3944 if !self.module.tunables.debug_adapter_modules {
3945 return;
3946 }
3947 self.instruction(LocalGet(local));
3948 self.instruction(I64Const(32));
3949 self.instruction(I64ShrU);
3950 self.instruction(I32WrapI64);
3951 self.instruction(If(BlockType::Empty));
3952 self.trap(Trap::DebugAssertUpperBitsUnset);
3953 self.instruction(End);
3954 }
3955
3956 fn stack_set(&mut self, dst_tys: &[ValType], src_ty: ValType) {
3962 assert_eq!(dst_tys.len(), 1);
3963 let dst_ty = dst_tys[0];
3964 match (src_ty, dst_ty) {
3965 (ValType::I32, ValType::I32)
3966 | (ValType::I64, ValType::I64)
3967 | (ValType::F32, ValType::F32)
3968 | (ValType::F64, ValType::F64) => {}
3969
3970 (ValType::F32, ValType::I32) => self.instruction(I32ReinterpretF32),
3971 (ValType::I32, ValType::I64) => self.instruction(I64ExtendI32U),
3972 (ValType::F64, ValType::I64) => self.instruction(I64ReinterpretF64),
3973 (ValType::F32, ValType::I64) => {
3974 self.instruction(I32ReinterpretF32);
3975 self.instruction(I64ExtendI32U);
3976 }
3977
3978 (ValType::I64, ValType::I32)
3980 | (ValType::F64, ValType::I32)
3981 | (ValType::I32, ValType::F32)
3982 | (ValType::I64, ValType::F32)
3983 | (ValType::F64, ValType::F32)
3984 | (ValType::I32, ValType::F64)
3985 | (ValType::I64, ValType::F64)
3986 | (ValType::F32, ValType::F64)
3987
3988 | (ValType::Ref(_), _)
3990 | (_, ValType::Ref(_))
3991 | (ValType::V128, _)
3992 | (_, ValType::V128) => {
3993 panic!("cannot get {dst_ty:?} from {src_ty:?} local");
3994 }
3995 }
3996 }
3997
3998 fn i32_load8u(&mut self, mem: &Memory) {
3999 self.instruction(LocalGet(mem.addr.idx));
4000 self.instruction(I32Load8U(mem.memarg(0)));
4001 }
4002
4003 fn i32_load8s(&mut self, mem: &Memory) {
4004 self.instruction(LocalGet(mem.addr.idx));
4005 self.instruction(I32Load8S(mem.memarg(0)));
4006 }
4007
4008 fn i32_load16u(&mut self, mem: &Memory) {
4009 self.instruction(LocalGet(mem.addr.idx));
4010 self.instruction(I32Load16U(mem.memarg(1)));
4011 }
4012
4013 fn i32_load16s(&mut self, mem: &Memory) {
4014 self.instruction(LocalGet(mem.addr.idx));
4015 self.instruction(I32Load16S(mem.memarg(1)));
4016 }
4017
4018 fn i32_load(&mut self, mem: &Memory) {
4019 self.instruction(LocalGet(mem.addr.idx));
4020 self.instruction(I32Load(mem.memarg(2)));
4021 }
4022
4023 fn i64_load(&mut self, mem: &Memory) {
4024 self.instruction(LocalGet(mem.addr.idx));
4025 self.instruction(I64Load(mem.memarg(3)));
4026 }
4027
4028 fn ptr_load(&mut self, mem: &Memory) {
4029 if mem.mem_opts().memory64() {
4030 self.i64_load(mem);
4031 } else {
4032 self.i32_load(mem);
4033 }
4034 }
4035
4036 fn ptr_add(&mut self, opts: &LinearMemoryOptions) {
4037 if opts.memory64() {
4038 self.instruction(I64Add);
4039 } else {
4040 self.instruction(I32Add);
4041 }
4042 }
4043
4044 fn ptr_sub(&mut self, opts: &LinearMemoryOptions) {
4045 if opts.memory64() {
4046 self.instruction(I64Sub);
4047 } else {
4048 self.instruction(I32Sub);
4049 }
4050 }
4051
4052 fn ptr_mul(&mut self, opts: &LinearMemoryOptions) {
4053 if opts.memory64() {
4054 self.instruction(I64Mul);
4055 } else {
4056 self.instruction(I32Mul);
4057 }
4058 }
4059
4060 fn ptr_gt_u(&mut self, opts: &LinearMemoryOptions) {
4061 if opts.memory64() {
4062 self.instruction(I64GtU);
4063 } else {
4064 self.instruction(I32GtU);
4065 }
4066 }
4067
4068 fn ptr_lt_u(&mut self, opts: &LinearMemoryOptions) {
4069 if opts.memory64() {
4070 self.instruction(I64LtU);
4071 } else {
4072 self.instruction(I32LtU);
4073 }
4074 }
4075
4076 fn ptr_shl(&mut self, opts: &LinearMemoryOptions) {
4077 if opts.memory64() {
4078 self.instruction(I64Shl);
4079 } else {
4080 self.instruction(I32Shl);
4081 }
4082 }
4083
4084 fn ptr_eqz(&mut self, opts: &LinearMemoryOptions) {
4085 if opts.memory64() {
4086 self.instruction(I64Eqz);
4087 } else {
4088 self.instruction(I32Eqz);
4089 }
4090 }
4091
4092 fn ptr_uconst(&mut self, opts: &LinearMemoryOptions, val: u32) {
4093 if opts.memory64() {
4094 self.instruction(I64Const(val.into()));
4095 } else {
4096 self.instruction(I32Const(val.cast_signed()));
4097 }
4098 }
4099
4100 fn ptr_iconst(&mut self, opts: &LinearMemoryOptions, val: i32) {
4101 if opts.memory64() {
4102 self.instruction(I64Const(val.into()));
4103 } else {
4104 self.instruction(I32Const(val));
4105 }
4106 }
4107
4108 fn ptr_eq(&mut self, opts: &LinearMemoryOptions) {
4109 if opts.memory64() {
4110 self.instruction(I64Eq);
4111 } else {
4112 self.instruction(I32Eq);
4113 }
4114 }
4115
4116 fn ptr_ne(&mut self, opts: &LinearMemoryOptions) {
4117 if opts.memory64() {
4118 self.instruction(I64Ne);
4119 } else {
4120 self.instruction(I32Ne);
4121 }
4122 }
4123
4124 fn ptr_and(&mut self, opts: &LinearMemoryOptions) {
4125 if opts.memory64() {
4126 self.instruction(I64And);
4127 } else {
4128 self.instruction(I32And);
4129 }
4130 }
4131
4132 fn ptr_or(&mut self, opts: &LinearMemoryOptions) {
4133 if opts.memory64() {
4134 self.instruction(I64Or);
4135 } else {
4136 self.instruction(I32Or);
4137 }
4138 }
4139
4140 fn ptr_xor(&mut self, opts: &LinearMemoryOptions) {
4141 if opts.memory64() {
4142 self.instruction(I64Xor);
4143 } else {
4144 self.instruction(I32Xor);
4145 }
4146 }
4147
4148 fn ptr_if(&mut self, opts: &LinearMemoryOptions, ty: BlockType) {
4149 if opts.memory64() {
4150 self.instruction(I64Const(0));
4151 self.instruction(I64Ne);
4152 }
4153 self.instruction(If(ty));
4154 }
4155
4156 fn ptr_br_if(&mut self, opts: &LinearMemoryOptions, depth: u32) {
4157 if opts.memory64() {
4158 self.instruction(I64Const(0));
4159 self.instruction(I64Ne);
4160 }
4161 self.instruction(BrIf(depth));
4162 }
4163
4164 fn f32_load(&mut self, mem: &Memory) {
4165 self.instruction(LocalGet(mem.addr.idx));
4166 self.instruction(F32Load(mem.memarg(2)));
4167 }
4168
4169 fn f64_load(&mut self, mem: &Memory) {
4170 self.instruction(LocalGet(mem.addr.idx));
4171 self.instruction(F64Load(mem.memarg(3)));
4172 }
4173
4174 fn push_dst_addr(&mut self, dst: &Destination) {
4175 if let Destination::Memory(mem) = dst {
4176 self.instruction(LocalGet(mem.addr.idx));
4177 }
4178 }
4179
4180 fn i32_store8(&mut self, mem: &Memory) {
4181 self.instruction(I32Store8(mem.memarg(0)));
4182 }
4183
4184 fn i32_store16(&mut self, mem: &Memory) {
4185 self.instruction(I32Store16(mem.memarg(1)));
4186 }
4187
4188 fn i32_store(&mut self, mem: &Memory) {
4189 self.instruction(I32Store(mem.memarg(2)));
4190 }
4191
4192 fn i64_store(&mut self, mem: &Memory) {
4193 self.instruction(I64Store(mem.memarg(3)));
4194 }
4195
4196 fn ptr_store(&mut self, mem: &Memory) {
4197 if mem.mem_opts().memory64() {
4198 self.i64_store(mem);
4199 } else {
4200 self.i32_store(mem);
4201 }
4202 }
4203
4204 fn f32_store(&mut self, mem: &Memory) {
4205 self.instruction(F32Store(mem.memarg(2)));
4206 }
4207
4208 fn f64_store(&mut self, mem: &Memory) {
4209 self.instruction(F64Store(mem.memarg(3)));
4210 }
4211
4212 fn alloc_size(&mut self, opts: &LinearMemoryOptions, size: &AllocSize) {
4215 match size {
4216 AllocSize::Const(size) => self.ptr_uconst(opts, *size),
4217 AllocSize::Local(idx) => self.instruction(LocalGet(*idx)),
4218 AllocSize::DoubleLocal(idx) => {
4219 self.instruction(LocalGet(*idx));
4220 self.ptr_uconst(opts, 1);
4221 self.ptr_shl(opts);
4222 }
4223 }
4224 }
4225}
4226
4227impl<'a> Source<'a> {
4228 fn record_field_srcs<'b>(
4235 &'b self,
4236 types: &'b ComponentTypesBuilder,
4237 fields: impl IntoIterator<Item = InterfaceType> + 'b,
4238 ) -> impl Iterator<Item = Source<'a>> + 'b
4239 where
4240 'a: 'b,
4241 {
4242 let mut offset = 0;
4243 fields.into_iter().map(move |ty| match self {
4244 Source::Memory(mem) => {
4245 let mem = next_field_offset(&mut offset, types, &ty, mem);
4246 Source::Memory(mem)
4247 }
4248 Source::Stack(stack) => {
4249 let cnt = types.flat_types(&ty).unwrap().len() as u32;
4250 offset += cnt;
4251 Source::Stack(stack.slice((offset - cnt) as usize..offset as usize))
4252 }
4253 Source::Struct(_) => todo!(),
4254 Source::Array(_) => todo!(),
4255 })
4256 }
4257
4258 fn payload_src(
4260 &self,
4261 types: &ComponentTypesBuilder,
4262 info: &VariantInfo,
4263 case: Option<&InterfaceType>,
4264 ) -> Source<'a> {
4265 match self {
4266 Source::Stack(s) => {
4267 let flat_len = match case {
4268 Some(case) => types.flat_types(case).unwrap().len(),
4269 None => 0,
4270 };
4271 Source::Stack(s.slice(1..s.locals.len()).slice(0..flat_len))
4272 }
4273 Source::Memory(mem) => {
4274 let mem = if mem.mem_opts().memory64() {
4275 mem.bump(info.payload_offset64)
4276 } else {
4277 mem.bump(info.payload_offset32)
4278 };
4279 Source::Memory(mem)
4280 }
4281 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
4282 }
4283 }
4284
4285 fn opts(&self) -> &'a Options {
4286 match self {
4287 Source::Stack(s) => s.opts,
4288 Source::Memory(mem) => mem.opts,
4289 Source::Struct(s) => s.opts,
4290 Source::Array(a) => a.opts,
4291 }
4292 }
4293}
4294
4295impl<'a> Destination<'a> {
4296 fn record_field_dsts<'b, I>(
4298 &'b self,
4299 types: &'b ComponentTypesBuilder,
4300 fields: I,
4301 ) -> impl Iterator<Item = Destination<'b>> + use<'b, I>
4302 where
4303 'a: 'b,
4304 I: IntoIterator<Item = InterfaceType> + 'b,
4305 {
4306 let mut offset = 0;
4307 fields.into_iter().map(move |ty| match self {
4308 Destination::Memory(mem) => {
4309 let mem = next_field_offset(&mut offset, types, &ty, mem);
4310 Destination::Memory(mem)
4311 }
4312 Destination::Stack(s, opts) => {
4313 let cnt = types.flat_types(&ty).unwrap().len() as u32;
4314 offset += cnt;
4315 Destination::Stack(&s[(offset - cnt) as usize..offset as usize], opts)
4316 }
4317 Destination::Struct(_) => todo!(),
4318 Destination::Array(_) => todo!(),
4319 })
4320 }
4321
4322 fn payload_dst(
4324 &self,
4325 types: &ComponentTypesBuilder,
4326 info: &VariantInfo,
4327 case: Option<&InterfaceType>,
4328 ) -> Destination<'_> {
4329 match self {
4330 Destination::Stack(s, opts) => {
4331 let flat_len = match case {
4332 Some(case) => types.flat_types(case).unwrap().len(),
4333 None => 0,
4334 };
4335 Destination::Stack(&s[1..][..flat_len], opts)
4336 }
4337 Destination::Memory(mem) => {
4338 let mem = if mem.mem_opts().memory64() {
4339 mem.bump(info.payload_offset64)
4340 } else {
4341 mem.bump(info.payload_offset32)
4342 };
4343 Destination::Memory(mem)
4344 }
4345 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
4346 }
4347 }
4348
4349 fn opts(&self) -> &'a Options {
4350 match self {
4351 Destination::Stack(_, opts) => opts,
4352 Destination::Memory(mem) => mem.opts,
4353 Destination::Struct(s) => s.opts,
4354 Destination::Array(a) => a.opts,
4355 }
4356 }
4357}
4358
4359fn next_field_offset<'a>(
4360 offset: &mut u32,
4361 types: &ComponentTypesBuilder,
4362 field: &InterfaceType,
4363 mem: &Memory<'a>,
4364) -> Memory<'a> {
4365 let abi = types.canonical_abi(field);
4366 let offset = if mem.mem_opts().memory64() {
4367 abi.next_field64(offset)
4368 } else {
4369 abi.next_field32(offset)
4370 };
4371 mem.bump(offset)
4372}
4373
4374impl<'a> Memory<'a> {
4375 fn memarg(&self, align: u32) -> MemArg {
4376 MemArg {
4377 offset: u64::from(self.offset),
4378 align,
4379 memory_index: self.mem_opts().memory.unwrap().0.as_u32(),
4380 }
4381 }
4382
4383 fn bump(&self, offset: u32) -> Memory<'a> {
4384 Memory {
4385 opts: self.opts,
4386 addr: TempLocal::new(self.addr.idx, self.addr.ty),
4387 offset: self.offset + offset,
4388 }
4389 }
4390}
4391
4392impl<'a> Stack<'a> {
4393 fn slice(&self, range: Range<usize>) -> Stack<'a> {
4394 Stack {
4395 locals: &self.locals[range],
4396 opts: self.opts,
4397 }
4398 }
4399}
4400
4401struct VariantCase<'a> {
4402 src_i: u32,
4403 src_ty: Option<&'a InterfaceType>,
4404 dst_i: u32,
4405 dst_ty: Option<&'a InterfaceType>,
4406}
4407
4408fn variant_info<'a, I>(types: &ComponentTypesBuilder, cases: I) -> VariantInfo
4409where
4410 I: IntoIterator<Item = Option<&'a InterfaceType>>,
4411 I::IntoIter: ExactSizeIterator,
4412{
4413 VariantInfo::new(
4414 cases
4415 .into_iter()
4416 .map(|ty| ty.map(|ty| types.canonical_abi(ty))),
4417 )
4418 .0
4419}
4420
4421struct SequenceLoopState {
4423 remaining: TempLocal,
4424 cur_src_ptr: TempLocal,
4425 cur_dst_ptr: TempLocal,
4426}
4427
4428struct SequenceTranslation<'a> {
4432 src_len: TempLocal,
4433 src_mem: Memory<'a>,
4434 dst_mem: Memory<'a>,
4435 src_opts: &'a Options,
4436 dst_opts: &'a Options,
4437 src_mem_opts: &'a LinearMemoryOptions,
4438 dst_mem_opts: &'a LinearMemoryOptions,
4439 loop_state: Option<SequenceLoopState>,
4440}
4441
4442enum AllocSize {
4443 Const(u32),
4444 Local(u32),
4445 DoubleLocal(u32),
4446}
4447
4448struct WasmString<'a> {
4449 ptr: TempLocal,
4450 len: TempLocal,
4451 opts: &'a Options,
4452}
4453
4454struct TempLocal {
4455 idx: u32,
4456 ty: ValType,
4457 needs_free: bool,
4458}
4459
4460impl TempLocal {
4461 fn new(idx: u32, ty: ValType) -> TempLocal {
4462 TempLocal {
4463 idx,
4464 ty,
4465 needs_free: false,
4466 }
4467 }
4468}
4469
4470impl std::ops::Drop for TempLocal {
4471 fn drop(&mut self) {
4472 if self.needs_free {
4473 panic!("temporary local not free'd");
4474 }
4475 }
4476}
4477
4478impl From<FlatType> for ValType {
4479 fn from(ty: FlatType) -> ValType {
4480 match ty {
4481 FlatType::I32 => ValType::I32,
4482 FlatType::I64 => ValType::I64,
4483 FlatType::F32 => ValType::F32,
4484 FlatType::F64 => ValType::F64,
4485 }
4486 }
4487}