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 let enter_sync_call = self.module.import_enter_sync_call();
802 self.instruction(Call(enter_sync_call.as_u32()));
803 None
804 } else {
805 None
806 };
807
808 self.set_flag(adapter.lift.flags, FLAG_MAY_LEAVE, false);
821 let param_locals = lower_sig
822 .params
823 .iter()
824 .enumerate()
825 .map(|(i, ty)| (i as u32, *ty))
826 .collect::<Vec<_>>();
827 self.translate_params(adapter, ¶m_locals);
828 self.set_flag(adapter.lift.flags, FLAG_MAY_LEAVE, true);
829
830 self.instruction(Call(adapter.callee.as_u32()));
834 let mut result_locals = Vec::with_capacity(lift_sig.results.len());
835 let mut temps = Vec::new();
836 for ty in lift_sig.results.iter().rev() {
837 let local = self.local_set_new_tmp(*ty);
838 result_locals.push((local.idx, *ty));
839 temps.push(local);
840 }
841 result_locals.reverse();
842
843 if self.emit_resource_call || self.module.tunables.concurrency_support {
858 let exit_sync_call = self.module.import_exit_sync_call();
859 self.instruction(Call(exit_sync_call.as_u32()));
860 }
861
862 self.set_flag(adapter.lower.flags, FLAG_MAY_LEAVE, false);
871 self.translate_results(adapter, ¶m_locals, &result_locals);
872 self.set_flag(adapter.lower.flags, FLAG_MAY_LEAVE, true);
873
874 if let Some(func) = adapter.lift.post_return {
877 for (result, _) in result_locals.iter() {
878 self.instruction(LocalGet(*result));
879 }
880 self.instruction(Call(func.as_u32()));
881 }
882
883 for tmp in temps {
884 self.free_temp_local(tmp);
885 }
886
887 if self.module.tunables.concurrency_support {
888 if let Some(old_task_may_block) = old_task_may_block {
890 let task_may_block = self.module.import_task_may_block();
891 self.instruction(LocalGet(old_task_may_block.idx));
892 self.instruction(GlobalSet(task_may_block.as_u32()));
893 self.free_temp_local(old_task_may_block);
894 }
895 }
896
897 self.finish()
898 }
899
900 fn translate_params(&mut self, adapter: &AdapterData, param_locals: &[(u32, ValType)]) {
901 let src_tys = self.types[adapter.lower.ty].params;
902 let src_tys = self.types[src_tys]
903 .types
904 .iter()
905 .copied()
906 .collect::<Vec<_>>();
907 let dst_tys = self.types[adapter.lift.ty].params;
908 let dst_tys = self.types[dst_tys]
909 .types
910 .iter()
911 .copied()
912 .collect::<Vec<_>>();
913 let lift_opts = &adapter.lift.options;
914 let lower_opts = &adapter.lower.options;
915
916 assert_eq!(src_tys.len(), dst_tys.len());
918
919 let max_flat_params = if adapter.lower.options.async_ {
923 MAX_FLAT_ASYNC_PARAMS
924 } else {
925 MAX_FLAT_PARAMS
926 };
927 let src_flat =
928 self.types
929 .flatten_types(lower_opts, max_flat_params, src_tys.iter().copied());
930 let dst_flat =
931 self.types
932 .flatten_types(lift_opts, MAX_FLAT_PARAMS, dst_tys.iter().copied());
933
934 let src = if let Some(flat) = &src_flat {
935 Source::Stack(Stack {
936 locals: ¶m_locals[..flat.len()],
937 opts: lower_opts,
938 })
939 } else {
940 let lower_mem_opts = lower_opts.data_model.unwrap_memory();
944 let (addr, ty) = param_locals[0];
945 assert_eq!(ty, lower_mem_opts.ptr());
946 let abi = CanonicalAbiInfo::record(src_tys.iter().map(|t| self.types.canonical_abi(t)));
947 Source::Memory(self.memory_operand_abi(
948 lower_opts,
949 TempLocal::new(addr, ty),
950 &abi,
951 Trap::MemoryOutOfBounds,
952 ))
953 };
954
955 let dst = if let Some(flat) = &dst_flat {
956 Destination::Stack(flat, lift_opts)
957 } else {
958 let abi = CanonicalAbiInfo::record(dst_tys.iter().map(|t| self.types.canonical_abi(t)));
961 Destination::Memory(self.malloc_abi(lift_opts, &abi, Trap::MemoryOutOfBounds))
962 };
963
964 let srcs = src
965 .record_field_srcs(self.types, src_tys.iter().copied())
966 .zip(src_tys.iter());
967 let dsts = dst
968 .record_field_dsts(self.types, dst_tys.iter().copied())
969 .zip(dst_tys.iter());
970 for ((src, src_ty), (dst, dst_ty)) in srcs.zip(dsts) {
971 self.translate(&src_ty, &src, &dst_ty, &dst);
972 }
973
974 if let Destination::Memory(mem) = dst {
978 self.instruction(LocalGet(mem.addr.idx));
979 self.free_temp_local(mem.addr);
980 }
981 }
982
983 fn translate_results(
984 &mut self,
985 adapter: &AdapterData,
986 param_locals: &[(u32, ValType)],
987 result_locals: &[(u32, ValType)],
988 ) {
989 let src_tys = self.types[adapter.lift.ty].results;
990 let src_tys = self.types[src_tys]
991 .types
992 .iter()
993 .copied()
994 .collect::<Vec<_>>();
995 let dst_tys = self.types[adapter.lower.ty].results;
996 let dst_tys = self.types[dst_tys]
997 .types
998 .iter()
999 .copied()
1000 .collect::<Vec<_>>();
1001 let lift_opts = &adapter.lift.options;
1002 let lower_opts = &adapter.lower.options;
1003
1004 let src_flat = self
1005 .types
1006 .flatten_lifting_types(lift_opts, src_tys.iter().copied());
1007 let dst_flat = self
1008 .types
1009 .flatten_lowering_types(lower_opts, dst_tys.iter().copied());
1010
1011 let src = if src_flat.is_some() {
1012 Source::Stack(Stack {
1013 locals: result_locals,
1014 opts: lift_opts,
1015 })
1016 } else {
1017 let abi = CanonicalAbiInfo::record(src_tys.iter().map(|t| self.types.canonical_abi(t)));
1022 assert_eq!(
1023 result_locals.len(),
1024 if lower_opts.async_ || lift_opts.async_ {
1025 2
1026 } else {
1027 1
1028 }
1029 );
1030 let (addr, ty) = result_locals[0];
1031 assert_eq!(ty, lift_opts.data_model.unwrap_memory().ptr());
1032 Source::Memory(self.memory_operand_abi(
1033 lift_opts,
1034 TempLocal::new(addr, ty),
1035 &abi,
1036 Trap::MemoryOutOfBounds,
1037 ))
1038 };
1039
1040 let dst = if let Some(flat) = &dst_flat {
1041 Destination::Stack(flat, lower_opts)
1042 } else {
1043 let abi = CanonicalAbiInfo::record(dst_tys.iter().map(|t| self.types.canonical_abi(t)));
1047 let (addr, ty) = *param_locals.last().expect("no retptr");
1048 assert_eq!(ty, lower_opts.data_model.unwrap_memory().ptr());
1049 Destination::Memory(self.memory_operand_abi(
1050 lower_opts,
1051 TempLocal::new(addr, ty),
1052 &abi,
1053 Trap::MemoryOutOfBounds,
1054 ))
1055 };
1056
1057 let srcs = src
1058 .record_field_srcs(self.types, src_tys.iter().copied())
1059 .zip(src_tys.iter());
1060 let dsts = dst
1061 .record_field_dsts(self.types, dst_tys.iter().copied())
1062 .zip(dst_tys.iter());
1063 for ((src, src_ty), (dst, dst_ty)) in srcs.zip(dsts) {
1064 self.translate(&src_ty, &src, &dst_ty, &dst);
1065 }
1066 }
1067
1068 fn translate(
1069 &mut self,
1070 src_ty: &InterfaceType,
1071 src: &Source<'_>,
1072 dst_ty: &InterfaceType,
1073 dst: &Destination,
1074 ) {
1075 if let Source::Memory(mem) = src {
1076 self.assert_aligned(src_ty, mem);
1077 }
1078 if let Destination::Memory(mem) = dst {
1079 self.assert_aligned(dst_ty, mem);
1080 }
1081
1082 let cost = match src_ty {
1112 InterfaceType::Bool
1116 | InterfaceType::U8
1117 | InterfaceType::S8
1118 | InterfaceType::U16
1119 | InterfaceType::S16
1120 | InterfaceType::U32
1121 | InterfaceType::S32
1122 | InterfaceType::U64
1123 | InterfaceType::S64
1124 | InterfaceType::Float32
1125 | InterfaceType::Float64 => 0,
1126
1127 InterfaceType::Char => 1,
1130
1131 InterfaceType::String => 40,
1134
1135 InterfaceType::List(_) => 40,
1138 InterfaceType::Map(_) => 40,
1140
1141 InterfaceType::Flags(i) => {
1142 let count = self.module.types[*i].names.len();
1143 match FlagsSize::from_count(count) {
1144 FlagsSize::Size0 => 0,
1145 FlagsSize::Size1 | FlagsSize::Size2 => 1,
1146 FlagsSize::Size4Plus(n) => n.into(),
1147 }
1148 }
1149
1150 InterfaceType::Record(i) => self.types[*i].fields.len(),
1151 InterfaceType::Tuple(i) => self.types[*i].types.len(),
1152 InterfaceType::Variant(i) => self.types[*i].cases.len(),
1153 InterfaceType::Enum(i) => self.types[*i].names.len(),
1154
1155 InterfaceType::Option(_) | InterfaceType::Result(_) => 2,
1157
1158 InterfaceType::Own(_)
1160 | InterfaceType::Borrow(_)
1161 | InterfaceType::Future(_)
1162 | InterfaceType::Stream(_)
1163 | InterfaceType::ErrorContext(_) => 1,
1164 InterfaceType::FixedLengthList(i) => self.types[*i].size as usize,
1165 };
1166
1167 match self.fuel.checked_sub(cost) {
1168 Some(n) => {
1174 self.fuel = n;
1175 match src_ty {
1176 InterfaceType::Bool => self.translate_bool(src, dst_ty, dst),
1177 InterfaceType::U8 => self.translate_u8(src, dst_ty, dst),
1178 InterfaceType::S8 => self.translate_s8(src, dst_ty, dst),
1179 InterfaceType::U16 => self.translate_u16(src, dst_ty, dst),
1180 InterfaceType::S16 => self.translate_s16(src, dst_ty, dst),
1181 InterfaceType::U32 => self.translate_u32(src, dst_ty, dst),
1182 InterfaceType::S32 => self.translate_s32(src, dst_ty, dst),
1183 InterfaceType::U64 => self.translate_u64(src, dst_ty, dst),
1184 InterfaceType::S64 => self.translate_s64(src, dst_ty, dst),
1185 InterfaceType::Float32 => self.translate_f32(src, dst_ty, dst),
1186 InterfaceType::Float64 => self.translate_f64(src, dst_ty, dst),
1187 InterfaceType::Char => self.translate_char(src, dst_ty, dst),
1188 InterfaceType::String => self.translate_string(src, dst_ty, dst),
1189 InterfaceType::List(t) => self.translate_list(*t, src, dst_ty, dst),
1190 InterfaceType::Map(t) => self.translate_map(*t, src, dst_ty, dst),
1191 InterfaceType::Record(t) => self.translate_record(*t, src, dst_ty, dst),
1192 InterfaceType::Flags(f) => self.translate_flags(*f, src, dst_ty, dst),
1193 InterfaceType::Tuple(t) => self.translate_tuple(*t, src, dst_ty, dst),
1194 InterfaceType::Variant(v) => self.translate_variant(*v, src, dst_ty, dst),
1195 InterfaceType::Enum(t) => self.translate_enum(*t, src, dst_ty, dst),
1196 InterfaceType::Option(t) => self.translate_option(*t, src, dst_ty, dst),
1197 InterfaceType::Result(t) => self.translate_result(*t, src, dst_ty, dst),
1198 InterfaceType::Own(t) => self.translate_own(*t, src, dst_ty, dst),
1199 InterfaceType::Borrow(t) => self.translate_borrow(*t, src, dst_ty, dst),
1200 InterfaceType::Future(t) => self.translate_future(*t, src, dst_ty, dst),
1201 InterfaceType::Stream(t) => self.translate_stream(*t, src, dst_ty, dst),
1202 InterfaceType::ErrorContext(t) => {
1203 self.translate_error_context(*t, src, dst_ty, dst)
1204 }
1205 InterfaceType::FixedLengthList(t) => {
1206 self.translate_fixed_length_list(*t, src, dst_ty, dst);
1207 }
1208 }
1209 }
1210
1211 None => {
1217 let src_loc = match src {
1218 Source::Stack(stack) => {
1222 for (i, ty) in stack
1223 .opts
1224 .flat_types(src_ty, self.types)
1225 .unwrap()
1226 .iter()
1227 .enumerate()
1228 {
1229 let stack = stack.slice(i..i + 1);
1230 self.stack_get(&stack, (*ty).into());
1231 }
1232 HelperLocation::Stack
1233 }
1234 Source::Memory(mem) => {
1239 self.push_mem_addr(mem);
1240 HelperLocation::Memory
1241 }
1242 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1243 };
1244 let dst_loc = match dst {
1245 Destination::Stack(..) => HelperLocation::Stack,
1246 Destination::Memory(mem) => {
1247 self.push_mem_addr(mem);
1248 HelperLocation::Memory
1249 }
1250 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1251 };
1252 let helper = self.module.translate_helper(Helper {
1258 src: HelperType {
1259 ty: *src_ty,
1260 opts: *src.opts(),
1261 loc: src_loc,
1262 },
1263 dst: HelperType {
1264 ty: *dst_ty,
1265 opts: *dst.opts(),
1266 loc: dst_loc,
1267 },
1268 });
1269 self.flush_code();
1272 self.module.funcs[self.result].body.push(Body::Call(helper));
1273
1274 if let Destination::Stack(tys, opts) = dst {
1283 let flat = self
1284 .types
1285 .flatten_types(opts, usize::MAX, [*dst_ty])
1286 .unwrap();
1287 assert_eq!(flat.len(), tys.len());
1288 let locals = flat
1289 .iter()
1290 .rev()
1291 .map(|ty| self.local_set_new_tmp(*ty))
1292 .collect::<Vec<_>>();
1293 for (ty, local) in tys.iter().zip(locals.into_iter().rev()) {
1294 self.instruction(LocalGet(local.idx));
1295 self.stack_set(std::slice::from_ref(ty), local.ty);
1296 self.free_temp_local(local);
1297 }
1298 }
1299 }
1300 }
1301 }
1302
1303 fn push_mem_addr(&mut self, mem: &Memory<'_>) {
1304 self.instruction(LocalGet(mem.addr.idx));
1305 if mem.offset != 0 {
1306 self.ptr_uconst(mem.mem_opts(), mem.offset);
1307 self.ptr_add(mem.mem_opts());
1308 }
1309 }
1310
1311 fn translate_bool(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1312 assert!(matches!(dst_ty, InterfaceType::Bool));
1314 self.push_dst_addr(dst);
1315
1316 self.instruction(I32Const(1));
1319 self.instruction(I32Const(0));
1320 match src {
1321 Source::Memory(mem) => self.i32_load8u(mem),
1322 Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1323 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1324 }
1325 self.instruction(Select);
1326
1327 match dst {
1328 Destination::Memory(mem) => self.i32_store8(mem),
1329 Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1330 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1331 }
1332 }
1333
1334 fn translate_u8(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1335 assert!(matches!(dst_ty, InterfaceType::U8));
1337 self.convert_u8_mask(src, dst, 0xff);
1338 }
1339
1340 fn convert_u8_mask(&mut self, src: &Source<'_>, dst: &Destination<'_>, mask: u8) {
1341 self.push_dst_addr(dst);
1342 let mut needs_mask = true;
1343 match src {
1344 Source::Memory(mem) => {
1345 self.i32_load8u(mem);
1346 needs_mask = mask != 0xff;
1347 }
1348 Source::Stack(stack) => {
1349 self.stack_get(stack, ValType::I32);
1350 }
1351 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1352 }
1353 if needs_mask {
1354 self.instruction(I32Const(i32::from(mask)));
1355 self.instruction(I32And);
1356 }
1357 match dst {
1358 Destination::Memory(mem) => self.i32_store8(mem),
1359 Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1360 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1361 }
1362 }
1363
1364 fn translate_s8(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1365 assert!(matches!(dst_ty, InterfaceType::S8));
1367 self.push_dst_addr(dst);
1368 match src {
1369 Source::Memory(mem) => self.i32_load8s(mem),
1370 Source::Stack(stack) => {
1371 self.stack_get(stack, ValType::I32);
1372 self.instruction(I32Extend8S);
1373 }
1374 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1375 }
1376 match dst {
1377 Destination::Memory(mem) => self.i32_store8(mem),
1378 Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1379 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1380 }
1381 }
1382
1383 fn translate_u16(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1384 assert!(matches!(dst_ty, InterfaceType::U16));
1386 self.convert_u16_mask(src, dst, 0xffff);
1387 }
1388
1389 fn convert_u16_mask(&mut self, src: &Source<'_>, dst: &Destination<'_>, mask: u16) {
1390 self.push_dst_addr(dst);
1391 let mut needs_mask = true;
1392 match src {
1393 Source::Memory(mem) => {
1394 self.i32_load16u(mem);
1395 needs_mask = mask != 0xffff;
1396 }
1397 Source::Stack(stack) => {
1398 self.stack_get(stack, ValType::I32);
1399 }
1400 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1401 }
1402 if needs_mask {
1403 self.instruction(I32Const(i32::from(mask)));
1404 self.instruction(I32And);
1405 }
1406 match dst {
1407 Destination::Memory(mem) => self.i32_store16(mem),
1408 Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1409 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1410 }
1411 }
1412
1413 fn translate_s16(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1414 assert!(matches!(dst_ty, InterfaceType::S16));
1416 self.push_dst_addr(dst);
1417 match src {
1418 Source::Memory(mem) => self.i32_load16s(mem),
1419 Source::Stack(stack) => {
1420 self.stack_get(stack, ValType::I32);
1421 self.instruction(I32Extend16S);
1422 }
1423 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1424 }
1425 match dst {
1426 Destination::Memory(mem) => self.i32_store16(mem),
1427 Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1428 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1429 }
1430 }
1431
1432 fn translate_u32(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1433 assert!(matches!(dst_ty, InterfaceType::U32));
1435 self.convert_u32_mask(src, dst, 0xffffffff)
1436 }
1437
1438 fn convert_u32_mask(&mut self, src: &Source<'_>, dst: &Destination<'_>, mask: u32) {
1439 self.push_dst_addr(dst);
1440 match src {
1441 Source::Memory(mem) => self.i32_load(mem),
1442 Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1443 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1444 }
1445 if mask != 0xffffffff {
1446 self.instruction(I32Const(mask as i32));
1447 self.instruction(I32And);
1448 }
1449 match dst {
1450 Destination::Memory(mem) => self.i32_store(mem),
1451 Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1452 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1453 }
1454 }
1455
1456 fn translate_s32(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1457 assert!(matches!(dst_ty, InterfaceType::S32));
1459 self.push_dst_addr(dst);
1460 match src {
1461 Source::Memory(mem) => self.i32_load(mem),
1462 Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1463 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1464 }
1465 match dst {
1466 Destination::Memory(mem) => self.i32_store(mem),
1467 Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1468 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1469 }
1470 }
1471
1472 fn translate_u64(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1473 assert!(matches!(dst_ty, InterfaceType::U64));
1475 self.push_dst_addr(dst);
1476 match src {
1477 Source::Memory(mem) => self.i64_load(mem),
1478 Source::Stack(stack) => self.stack_get(stack, ValType::I64),
1479 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1480 }
1481 match dst {
1482 Destination::Memory(mem) => self.i64_store(mem),
1483 Destination::Stack(stack, _) => self.stack_set(stack, ValType::I64),
1484 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1485 }
1486 }
1487
1488 fn translate_s64(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1489 assert!(matches!(dst_ty, InterfaceType::S64));
1491 self.push_dst_addr(dst);
1492 match src {
1493 Source::Memory(mem) => self.i64_load(mem),
1494 Source::Stack(stack) => self.stack_get(stack, ValType::I64),
1495 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1496 }
1497 match dst {
1498 Destination::Memory(mem) => self.i64_store(mem),
1499 Destination::Stack(stack, _) => self.stack_set(stack, ValType::I64),
1500 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1501 }
1502 }
1503
1504 fn translate_f32(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1505 assert!(matches!(dst_ty, InterfaceType::Float32));
1507 self.push_dst_addr(dst);
1508 match src {
1509 Source::Memory(mem) => self.f32_load(mem),
1510 Source::Stack(stack) => self.stack_get(stack, ValType::F32),
1511 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1512 }
1513 match dst {
1514 Destination::Memory(mem) => self.f32_store(mem),
1515 Destination::Stack(stack, _) => self.stack_set(stack, ValType::F32),
1516 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1517 }
1518 }
1519
1520 fn translate_f64(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1521 assert!(matches!(dst_ty, InterfaceType::Float64));
1523 self.push_dst_addr(dst);
1524 match src {
1525 Source::Memory(mem) => self.f64_load(mem),
1526 Source::Stack(stack) => self.stack_get(stack, ValType::F64),
1527 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1528 }
1529 match dst {
1530 Destination::Memory(mem) => self.f64_store(mem),
1531 Destination::Stack(stack, _) => self.stack_set(stack, ValType::F64),
1532 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1533 }
1534 }
1535
1536 fn translate_char(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1537 assert!(matches!(dst_ty, InterfaceType::Char));
1538 match src {
1539 Source::Memory(mem) => self.i32_load(mem),
1540 Source::Stack(stack) => self.stack_get(stack, ValType::I32),
1541 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1542 }
1543 let local = self.local_set_new_tmp(ValType::I32);
1544
1545 self.instruction(Block(BlockType::Empty));
1561 self.instruction(Block(BlockType::Empty));
1562 self.instruction(LocalGet(local.idx));
1563 self.instruction(I32Const(0xd800));
1564 self.instruction(I32Xor);
1565 self.instruction(I32Const(-0x110000));
1566 self.instruction(I32Add);
1567 self.instruction(I32Const(-0x10f800));
1568 self.instruction(I32LtU);
1569 self.instruction(BrIf(0));
1570 self.instruction(LocalGet(local.idx));
1571 self.instruction(I32Const(0x110000));
1572 self.instruction(I32Ne);
1573 self.instruction(BrIf(1));
1574 self.instruction(End);
1575 self.trap(Trap::InvalidChar);
1576 self.instruction(End);
1577
1578 self.push_dst_addr(dst);
1579 self.instruction(LocalGet(local.idx));
1580 match dst {
1581 Destination::Memory(mem) => {
1582 self.i32_store(mem);
1583 }
1584 Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
1585 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1586 }
1587
1588 self.free_temp_local(local);
1589 }
1590
1591 fn translate_string(&mut self, src: &Source<'_>, dst_ty: &InterfaceType, dst: &Destination) {
1592 assert!(matches!(dst_ty, InterfaceType::String));
1593 let src_opts = src.opts();
1594 let dst_opts = dst.opts();
1595
1596 let src_mem_opts = match &src_opts.data_model {
1597 DataModel::Gc {} => todo!("CM+GC"),
1598 DataModel::LinearMemory(opts) => opts,
1599 };
1600 let dst_mem_opts = match &dst_opts.data_model {
1601 DataModel::Gc {} => todo!("CM+GC"),
1602 DataModel::LinearMemory(opts) => opts,
1603 };
1604
1605 match src {
1610 Source::Stack(s) => {
1611 assert_eq!(s.locals.len(), 2);
1612 self.stack_get(&s.slice(0..1), src_mem_opts.ptr());
1613 self.stack_get(&s.slice(1..2), src_mem_opts.ptr());
1614 }
1615 Source::Memory(mem) => {
1616 self.ptr_load(mem);
1617 self.ptr_load(&mem.bump(src_mem_opts.ptr_size().into()));
1618 }
1619 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
1620 }
1621 let src_len = self.local_set_new_tmp(src_mem_opts.ptr());
1622 let src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
1623 let src_str = WasmString {
1624 ptr: src_ptr,
1625 len: src_len,
1626 opts: src_opts,
1627 };
1628
1629 let dst_str = match src_opts.string_encoding {
1630 StringEncoding::Utf8 => {
1631 self.validate_guest_pointer(
1632 src_opts,
1633 &src_str.ptr,
1634 &AllocSize::Local(src_str.len.idx),
1635 1,
1636 Trap::StringOutOfBounds,
1637 );
1638 match dst_opts.string_encoding {
1639 StringEncoding::Utf8 => {
1640 self.string_copy(&src_str, FE::Utf8, dst_opts, FE::Utf8)
1641 }
1642 StringEncoding::Utf16 => self.string_utf8_to_utf16(&src_str, dst_opts),
1643 StringEncoding::CompactUtf16 => {
1644 self.string_to_compact(&src_str, FE::Utf8, dst_opts)
1645 }
1646 }
1647 }
1648
1649 StringEncoding::Utf16 => {
1650 self.validate_guest_pointer(
1651 src_opts,
1652 &src_str.ptr,
1653 &AllocSize::DoubleLocal(src_str.len.idx),
1654 2,
1655 Trap::StringOutOfBounds,
1656 );
1657 match dst_opts.string_encoding {
1658 StringEncoding::Utf8 => {
1659 self.string_deflate_to_utf8(&src_str, FE::Utf16, dst_opts)
1660 }
1661 StringEncoding::Utf16 => {
1662 self.string_copy(&src_str, FE::Utf16, dst_opts, FE::Utf16)
1663 }
1664 StringEncoding::CompactUtf16 => {
1665 self.string_to_compact(&src_str, FE::Utf16, dst_opts)
1666 }
1667 }
1668 }
1669
1670 StringEncoding::CompactUtf16 => {
1671 self.instruction(LocalGet(src_str.len.idx));
1674 self.ptr_uconst(src_mem_opts, UTF16_TAG);
1675 self.ptr_and(src_mem_opts);
1676 self.ptr_if(src_mem_opts, BlockType::Empty);
1677
1678 self.instruction(LocalGet(src_str.len.idx));
1682 self.ptr_uconst(src_mem_opts, UTF16_TAG);
1683 self.ptr_xor(src_mem_opts);
1684 self.instruction(LocalSet(src_str.len.idx));
1685
1686 self.validate_guest_pointer(
1690 src_opts,
1691 &src_str.ptr,
1692 &AllocSize::DoubleLocal(src_str.len.idx),
1693 2,
1694 Trap::StringOutOfBounds,
1695 );
1696
1697 let s1 = match dst_opts.string_encoding {
1698 StringEncoding::Utf8 => {
1699 self.string_deflate_to_utf8(&src_str, FE::Utf16, dst_opts)
1700 }
1701 StringEncoding::Utf16 => {
1702 self.string_copy(&src_str, FE::Utf16, dst_opts, FE::Utf16)
1703 }
1704 StringEncoding::CompactUtf16 => {
1705 self.string_compact_utf16_to_compact(&src_str, dst_opts)
1706 }
1707 };
1708
1709 self.instruction(Else);
1710
1711 self.validate_guest_pointer(
1714 src_opts,
1715 &src_str.ptr,
1716 &AllocSize::Local(src_str.len.idx),
1717 2,
1718 Trap::StringOutOfBounds,
1719 );
1720
1721 let s2 = match dst_opts.string_encoding {
1725 StringEncoding::Utf16 => {
1726 self.string_copy(&src_str, FE::Latin1, dst_opts, FE::Utf16)
1727 }
1728 StringEncoding::Utf8 => {
1729 self.string_deflate_to_utf8(&src_str, FE::Latin1, dst_opts)
1730 }
1731 StringEncoding::CompactUtf16 => {
1732 self.string_copy(&src_str, FE::Latin1, dst_opts, FE::Latin1)
1733 }
1734 };
1735 self.instruction(LocalGet(s2.ptr.idx));
1738 self.instruction(LocalSet(s1.ptr.idx));
1739 self.instruction(LocalGet(s2.len.idx));
1740 self.instruction(LocalSet(s1.len.idx));
1741 self.instruction(End);
1742 self.free_temp_local(s2.ptr);
1743 self.free_temp_local(s2.len);
1744 s1
1745 }
1746 };
1747
1748 match dst {
1750 Destination::Stack(s, _) => {
1751 self.instruction(LocalGet(dst_str.ptr.idx));
1752 self.stack_set(&s[..1], dst_mem_opts.ptr());
1753 self.instruction(LocalGet(dst_str.len.idx));
1754 self.stack_set(&s[1..], dst_mem_opts.ptr());
1755 }
1756 Destination::Memory(mem) => {
1757 self.instruction(LocalGet(mem.addr.idx));
1758 self.instruction(LocalGet(dst_str.ptr.idx));
1759 self.ptr_store(mem);
1760 self.instruction(LocalGet(mem.addr.idx));
1761 self.instruction(LocalGet(dst_str.len.idx));
1762 self.ptr_store(&mem.bump(dst_mem_opts.ptr_size().into()));
1763 }
1764 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
1765 }
1766
1767 self.free_temp_local(src_str.ptr);
1768 self.free_temp_local(src_str.len);
1769 self.free_temp_local(dst_str.ptr);
1770 self.free_temp_local(dst_str.len);
1771 }
1772
1773 fn string_copy<'c>(
1786 &mut self,
1787 src: &WasmString<'_>,
1788 src_enc: FE,
1789 dst_opts: &'c Options,
1790 dst_enc: FE,
1791 ) -> WasmString<'c> {
1792 assert!(dst_enc.width() >= src_enc.width());
1793
1794 self.validate_string_length(src, dst_enc);
1799
1800 let src_mem_opts = {
1801 match &src.opts.data_model {
1802 DataModel::Gc {} => todo!("CM+GC"),
1803 DataModel::LinearMemory(opts) => opts,
1804 }
1805 };
1806 let dst_mem_opts = {
1807 match &dst_opts.data_model {
1808 DataModel::Gc {} => todo!("CM+GC"),
1809 DataModel::LinearMemory(opts) => opts,
1810 }
1811 };
1812
1813 self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
1816 let dst_len = self.local_tee_new_tmp(dst_mem_opts.ptr());
1817 if dst_enc.width() > 1 {
1818 assert_eq!(dst_enc.width(), 2);
1819 self.ptr_uconst(dst_mem_opts, 1);
1820 self.ptr_shl(dst_mem_opts);
1821 }
1822 let dst_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
1823
1824 let dst = {
1827 let dst_mem = self.malloc(
1828 dst_opts,
1829 AllocSize::Local(dst_byte_len.idx),
1830 dst_enc.align().into(),
1831 Trap::StringOutOfBounds,
1832 );
1833 WasmString {
1834 ptr: dst_mem.addr,
1835 len: dst_len,
1836 opts: dst_opts,
1837 }
1838 };
1839
1840 let op = if src_enc == dst_enc {
1844 Transcode::Copy(src_enc)
1845 } else {
1846 assert_eq!(src_enc, FE::Latin1);
1847 assert_eq!(dst_enc, FE::Utf16);
1848 Transcode::Latin1ToUtf16
1849 };
1850 let transcode = self.transcoder(src, &dst, op);
1851 self.instruction(LocalGet(src.ptr.idx));
1852 self.instruction(LocalGet(src.len.idx));
1853 self.instruction(LocalGet(dst.ptr.idx));
1854 self.instruction(Call(transcode.as_u32()));
1855
1856 self.free_temp_local(dst_byte_len);
1857
1858 dst
1859 }
1860
1861 fn string_deflate_to_utf8<'c>(
1874 &mut self,
1875 src: &WasmString<'_>,
1876 src_enc: FE,
1877 dst_opts: &'c Options,
1878 ) -> WasmString<'c> {
1879 let src_mem_opts = match &src.opts.data_model {
1880 DataModel::Gc {} => todo!("CM+GC"),
1881 DataModel::LinearMemory(opts) => opts,
1882 };
1883 let dst_mem_opts = match &dst_opts.data_model {
1884 DataModel::Gc {} => todo!("CM+GC"),
1885 DataModel::LinearMemory(opts) => opts,
1886 };
1887
1888 self.validate_string_length(src, src_enc);
1889
1890 self.convert_src_len_to_dst(
1894 src.len.idx,
1895 src.opts.data_model.unwrap_memory().ptr(),
1896 dst_opts.data_model.unwrap_memory().ptr(),
1897 );
1898 let dst_len = self.local_tee_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
1899 let dst_byte_len = self.local_set_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
1900
1901 let dst = {
1902 let dst_mem = self.malloc(
1903 dst_opts,
1904 AllocSize::Local(dst_byte_len.idx),
1905 1,
1906 Trap::StringOutOfBounds,
1907 );
1908 WasmString {
1909 ptr: dst_mem.addr,
1910 len: dst_len,
1911 opts: dst_opts,
1912 }
1913 };
1914
1915 let op = match src_enc {
1917 FE::Latin1 => Transcode::Latin1ToUtf8,
1918 FE::Utf16 => Transcode::Utf16ToUtf8,
1919 FE::Utf8 => unreachable!(),
1920 };
1921 let transcode = self.transcoder(src, &dst, op);
1922 self.instruction(LocalGet(src.ptr.idx));
1923 self.instruction(LocalGet(src.len.idx));
1924 self.instruction(LocalGet(dst.ptr.idx));
1925 self.instruction(LocalGet(dst_byte_len.idx));
1926 self.instruction(I32Const(1)); self.instruction(Call(transcode.as_u32()));
1928 self.instruction(LocalSet(dst.len.idx));
1929 let src_len_tmp = self.local_set_new_tmp(src.opts.data_model.unwrap_memory().ptr());
1930
1931 self.instruction(LocalGet(src_len_tmp.idx));
1935 self.instruction(LocalGet(src.len.idx));
1936 self.ptr_ne(src_mem_opts);
1937 self.instruction(If(BlockType::Empty));
1938
1939 let factor = match src_enc {
1942 FE::Latin1 => 2,
1943 FE::Utf16 => 3,
1944 _ => unreachable!(),
1945 };
1946 self.validate_string_length_u8(src, factor);
1947 self.convert_src_len_to_dst(
1948 src.len.idx,
1949 src.opts.data_model.unwrap_memory().ptr(),
1950 dst_opts.data_model.unwrap_memory().ptr(),
1951 );
1952 self.ptr_uconst(dst_mem_opts, factor.into());
1953 self.ptr_mul(dst_mem_opts);
1954 let new_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
1955
1956 self.realloc(
1960 dst_opts,
1961 &dst.ptr,
1962 AllocSize::Local(dst_byte_len.idx),
1963 AllocSize::Local(new_byte_len.idx),
1964 1,
1965 Trap::StringOutOfBounds,
1966 );
1967 self.instruction(LocalGet(new_byte_len.idx));
1968 self.instruction(LocalSet(dst_byte_len.idx));
1969 self.free_temp_local(new_byte_len);
1970
1971 self.instruction(LocalGet(src.ptr.idx));
1976 self.instruction(LocalGet(src_len_tmp.idx));
1977 if let FE::Utf16 = src_enc {
1978 self.ptr_uconst(src_mem_opts, 1);
1979 self.ptr_shl(src_mem_opts);
1980 }
1981 self.ptr_add(src_mem_opts);
1982 self.instruction(LocalGet(src.len.idx));
1983 self.instruction(LocalGet(src_len_tmp.idx));
1984 self.ptr_sub(src_mem_opts);
1985 self.instruction(LocalGet(dst.ptr.idx));
1986 self.instruction(LocalGet(dst.len.idx));
1987 self.ptr_add(dst_mem_opts);
1988 self.instruction(LocalGet(dst_byte_len.idx));
1989 self.instruction(LocalGet(dst.len.idx));
1990 self.ptr_sub(dst_mem_opts);
1991 self.instruction(I32Const(0)); self.instruction(Call(transcode.as_u32()));
1993
1994 self.instruction(LocalGet(dst.len.idx));
1998 self.ptr_add(dst_mem_opts);
1999 self.instruction(LocalSet(dst.len.idx));
2000
2001 if self.module.tunables.debug_adapter_modules {
2004 self.instruction(LocalGet(src.len.idx));
2005 self.instruction(LocalGet(src_len_tmp.idx));
2006 self.ptr_sub(src_mem_opts);
2007 self.ptr_ne(src_mem_opts);
2008 self.instruction(If(BlockType::Empty));
2009 self.trap(Trap::DebugAssertStringEncodingFinished);
2010 self.instruction(End);
2011 } else {
2012 self.instruction(Drop);
2013 }
2014
2015 self.instruction(LocalGet(dst.len.idx));
2017 self.instruction(LocalGet(dst_byte_len.idx));
2018 self.ptr_ne(dst_mem_opts);
2019 self.instruction(If(BlockType::Empty));
2020 self.realloc(
2021 dst_opts,
2022 &dst.ptr,
2023 AllocSize::Local(dst_byte_len.idx),
2024 AllocSize::Local(dst.len.idx),
2025 1,
2026 Trap::StringOutOfBounds,
2027 );
2028 self.instruction(End);
2029
2030 if self.module.tunables.debug_adapter_modules {
2033 self.instruction(Else);
2034
2035 self.instruction(LocalGet(dst.len.idx));
2036 self.instruction(LocalGet(dst_byte_len.idx));
2037 self.ptr_ne(dst_mem_opts);
2038 self.instruction(If(BlockType::Empty));
2039 self.trap(Trap::DebugAssertStringEncodingFinished);
2040 self.instruction(End);
2041 }
2042
2043 self.instruction(End); self.free_temp_local(src_len_tmp);
2046 self.free_temp_local(dst_byte_len);
2047
2048 dst
2049 }
2050
2051 fn string_utf8_to_utf16<'c>(
2066 &mut self,
2067 src: &WasmString<'_>,
2068 dst_opts: &'c Options,
2069 ) -> WasmString<'c> {
2070 let src_mem_opts = match &src.opts.data_model {
2071 DataModel::Gc {} => todo!("CM+GC"),
2072 DataModel::LinearMemory(opts) => opts,
2073 };
2074 let dst_mem_opts = match &dst_opts.data_model {
2075 DataModel::Gc {} => todo!("CM+GC"),
2076 DataModel::LinearMemory(opts) => opts,
2077 };
2078
2079 self.validate_string_length(src, FE::Utf16);
2080 self.convert_src_len_to_dst(
2081 src.len.idx,
2082 src_mem_opts.ptr(),
2083 dst_opts.data_model.unwrap_memory().ptr(),
2084 );
2085 let dst_len = self.local_tee_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
2086 self.ptr_uconst(dst_mem_opts, 1);
2087 self.ptr_shl(dst_mem_opts);
2088 let dst_byte_len = self.local_set_new_tmp(dst_opts.data_model.unwrap_memory().ptr());
2089 let dst = {
2090 let dst_mem = self.malloc(
2091 dst_opts,
2092 AllocSize::Local(dst_byte_len.idx),
2093 2,
2094 Trap::StringOutOfBounds,
2095 );
2096 WasmString {
2097 ptr: dst_mem.addr,
2098 len: dst_len,
2099 opts: dst_opts,
2100 }
2101 };
2102
2103 let transcode = self.transcoder(src, &dst, Transcode::Utf8ToUtf16);
2104 self.instruction(LocalGet(src.ptr.idx));
2105 self.instruction(LocalGet(src.len.idx));
2106 self.instruction(LocalGet(dst.ptr.idx));
2107 self.instruction(Call(transcode.as_u32()));
2108 self.instruction(LocalSet(dst.len.idx));
2109
2110 self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2118 self.instruction(LocalGet(dst.len.idx));
2119 self.ptr_ne(dst_mem_opts);
2120 self.instruction(If(BlockType::Empty));
2121 self.realloc(
2122 dst.opts,
2123 &dst.ptr,
2124 AllocSize::Local(dst_byte_len.idx),
2125 AllocSize::DoubleLocal(dst.len.idx),
2126 2,
2127 Trap::StringOutOfBounds,
2128 );
2129 self.instruction(End); self.free_temp_local(dst_byte_len);
2132
2133 dst
2134 }
2135
2136 fn string_compact_utf16_to_compact<'c>(
2150 &mut self,
2151 src: &WasmString<'_>,
2152 dst_opts: &'c Options,
2153 ) -> WasmString<'c> {
2154 let src_mem_opts = match &src.opts.data_model {
2155 DataModel::Gc {} => todo!("CM+GC"),
2156 DataModel::LinearMemory(opts) => opts,
2157 };
2158 let dst_mem_opts = match &dst_opts.data_model {
2159 DataModel::Gc {} => todo!("CM+GC"),
2160 DataModel::LinearMemory(opts) => opts,
2161 };
2162
2163 self.validate_string_length(src, FE::Utf16);
2164 self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2165 let dst_len = self.local_tee_new_tmp(dst_mem_opts.ptr());
2166 self.ptr_uconst(dst_mem_opts, 1);
2167 self.ptr_shl(dst_mem_opts);
2168 let dst_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
2169 let dst = {
2170 let dst_mem = self.malloc(
2171 dst_opts,
2172 AllocSize::Local(dst_byte_len.idx),
2173 2,
2174 Trap::StringOutOfBounds,
2175 );
2176 WasmString {
2177 ptr: dst_mem.addr,
2178 len: dst_len,
2179 opts: dst_opts,
2180 }
2181 };
2182
2183 self.convert_src_len_to_dst(
2184 dst_byte_len.idx,
2185 dst.opts.data_model.unwrap_memory().ptr(),
2186 src_mem_opts.ptr(),
2187 );
2188 let src_byte_len = self.local_set_new_tmp(src_mem_opts.ptr());
2189
2190 let transcode = self.transcoder(src, &dst, Transcode::Utf16ToCompactProbablyUtf16);
2191 self.instruction(LocalGet(src.ptr.idx));
2192 self.instruction(LocalGet(src.len.idx));
2193 self.instruction(LocalGet(dst.ptr.idx));
2194 self.instruction(Call(transcode.as_u32()));
2195 self.instruction(LocalSet(dst.len.idx));
2196
2197 if self.module.tunables.debug_adapter_modules {
2200 self.instruction(LocalGet(dst.len.idx));
2201 self.ptr_uconst(dst_mem_opts, !UTF16_TAG);
2202 self.ptr_and(dst_mem_opts);
2203 self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2204 self.ptr_ne(dst_mem_opts);
2205 self.instruction(If(BlockType::Empty));
2206 self.trap(Trap::DebugAssertEqualCodeUnits);
2207 self.instruction(End);
2208 }
2209
2210 self.instruction(LocalGet(dst.len.idx));
2214 self.ptr_uconst(dst_mem_opts, UTF16_TAG);
2215 self.ptr_and(dst_mem_opts);
2216 self.ptr_br_if(dst_mem_opts, 0);
2217
2218 self.realloc(
2220 dst.opts,
2221 &dst.ptr,
2222 AllocSize::Local(dst_byte_len.idx),
2223 AllocSize::Local(dst.len.idx),
2224 2,
2225 Trap::StringOutOfBounds,
2226 );
2227
2228 self.free_temp_local(dst_byte_len);
2229 self.free_temp_local(src_byte_len);
2230
2231 dst
2232 }
2233
2234 fn string_to_compact<'c>(
2241 &mut self,
2242 src: &WasmString<'_>,
2243 src_enc: FE,
2244 dst_opts: &'c Options,
2245 ) -> WasmString<'c> {
2246 let src_mem_opts = match &src.opts.data_model {
2247 DataModel::Gc {} => todo!("CM+GC"),
2248 DataModel::LinearMemory(opts) => opts,
2249 };
2250 let dst_mem_opts = match &dst_opts.data_model {
2251 DataModel::Gc {} => todo!("CM+GC"),
2252 DataModel::LinearMemory(opts) => opts,
2253 };
2254
2255 self.validate_string_length(src, src_enc);
2256
2257 self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2258 let dst_len = self.local_tee_new_tmp(dst_mem_opts.ptr());
2259 let dst_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
2260 let dst = {
2261 let dst_mem = self.malloc(
2262 dst_opts,
2263 AllocSize::Local(dst_byte_len.idx),
2264 2,
2265 Trap::StringOutOfBounds,
2266 );
2267 WasmString {
2268 ptr: dst_mem.addr,
2269 len: dst_len,
2270 opts: dst_opts,
2271 }
2272 };
2273
2274 let (latin1, utf16) = match src_enc {
2278 FE::Utf8 => (Transcode::Utf8ToLatin1, Transcode::Utf8ToCompactUtf16),
2279 FE::Utf16 => (Transcode::Utf16ToLatin1, Transcode::Utf16ToCompactUtf16),
2280 FE::Latin1 => unreachable!(),
2281 };
2282 let transcode_latin1 = self.transcoder(src, &dst, latin1);
2283 let transcode_utf16 = self.transcoder(src, &dst, utf16);
2284 self.instruction(LocalGet(src.ptr.idx));
2285 self.instruction(LocalGet(src.len.idx));
2286 self.instruction(LocalGet(dst.ptr.idx));
2287 self.instruction(Call(transcode_latin1.as_u32()));
2288 self.instruction(LocalSet(dst.len.idx));
2289 let src_len_tmp = self.local_set_new_tmp(src_mem_opts.ptr());
2290
2291 self.instruction(LocalGet(src_len_tmp.idx));
2294 self.instruction(LocalGet(src.len.idx));
2295 self.ptr_eq(src_mem_opts);
2296 self.instruction(If(BlockType::Empty)); self.instruction(LocalGet(dst_byte_len.idx));
2302 self.instruction(LocalGet(dst.len.idx));
2303 self.ptr_ne(dst_mem_opts);
2304 self.instruction(If(BlockType::Empty));
2305 self.realloc(
2306 dst.opts,
2307 &dst.ptr,
2308 AllocSize::Local(dst_byte_len.idx),
2309 AllocSize::Local(dst.len.idx),
2310 2,
2311 Trap::StringOutOfBounds,
2312 );
2313 self.instruction(End);
2314
2315 self.instruction(Else); if src_enc.width() == 1 {
2324 self.validate_string_length_u8(src, 2);
2325 }
2326
2327 self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2330 self.ptr_uconst(dst_mem_opts, 1);
2331 self.ptr_shl(dst_mem_opts);
2332 let new_byte_len = self.local_set_new_tmp(dst_mem_opts.ptr());
2333 self.realloc(
2334 dst.opts,
2335 &dst.ptr,
2336 AllocSize::Local(dst_byte_len.idx),
2337 AllocSize::Local(new_byte_len.idx),
2338 2,
2339 Trap::StringOutOfBounds,
2340 );
2341 self.instruction(LocalGet(new_byte_len.idx));
2342 self.instruction(LocalSet(dst_byte_len.idx));
2343 self.free_temp_local(new_byte_len);
2344
2345 self.instruction(LocalGet(src.ptr.idx));
2349 self.instruction(LocalGet(src_len_tmp.idx));
2350 if let FE::Utf16 = src_enc {
2351 self.ptr_uconst(src_mem_opts, 1);
2352 self.ptr_shl(src_mem_opts);
2353 }
2354 self.ptr_add(src_mem_opts);
2355 self.instruction(LocalGet(src.len.idx));
2356 self.instruction(LocalGet(src_len_tmp.idx));
2357 self.ptr_sub(src_mem_opts);
2358 self.instruction(LocalGet(dst.ptr.idx));
2359 self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2360 self.instruction(LocalGet(dst.len.idx));
2361 self.instruction(Call(transcode_utf16.as_u32()));
2362 self.instruction(LocalSet(dst.len.idx));
2363
2364 self.instruction(LocalGet(dst.len.idx));
2372 self.convert_src_len_to_dst(src.len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2373 self.ptr_ne(dst_mem_opts);
2374 self.instruction(If(BlockType::Empty));
2375 self.realloc(
2376 dst.opts,
2377 &dst.ptr,
2378 AllocSize::Local(dst_byte_len.idx),
2379 AllocSize::DoubleLocal(dst.len.idx),
2380 2,
2381 Trap::StringOutOfBounds,
2382 );
2383 self.instruction(End);
2384
2385 self.instruction(LocalGet(dst.len.idx));
2387 self.ptr_uconst(dst_mem_opts, UTF16_TAG);
2388 self.ptr_or(dst_mem_opts);
2389 self.instruction(LocalSet(dst.len.idx));
2390
2391 self.instruction(End); self.free_temp_local(src_len_tmp);
2394 self.free_temp_local(dst_byte_len);
2395
2396 dst
2397 }
2398
2399 fn validate_string_length(&mut self, src: &WasmString<'_>, dst: FE) {
2400 self.validate_string_length_u8(src, dst.width())
2401 }
2402
2403 fn validate_string_length_u8(&mut self, s: &WasmString<'_>, dst: u8) {
2404 let mem_opts = match &s.opts.data_model {
2405 DataModel::Gc {} => todo!("CM+GC"),
2406 DataModel::LinearMemory(opts) => opts,
2407 };
2408
2409 self.instruction(LocalGet(s.len.idx));
2412 let max = MAX_STRING_BYTE_LENGTH / u32::from(dst);
2413 self.ptr_uconst(mem_opts, max);
2414 self.ptr_gt_u(mem_opts);
2415 self.instruction(If(BlockType::Empty));
2416 self.trap(Trap::StringOutOfBounds);
2417 self.instruction(End);
2418 }
2419
2420 fn transcoder(
2421 &mut self,
2422 src: &WasmString<'_>,
2423 dst: &WasmString<'_>,
2424 op: Transcode,
2425 ) -> FuncIndex {
2426 match (src.opts.data_model, dst.opts.data_model) {
2427 (DataModel::Gc {}, _) | (_, DataModel::Gc {}) => {
2428 todo!("CM+GC")
2429 }
2430 (
2431 DataModel::LinearMemory(LinearMemoryOptions {
2432 memory: Some((src_mem, src_ty)),
2433 realloc: _,
2434 }),
2435 DataModel::LinearMemory(LinearMemoryOptions {
2436 memory: Some((dst_mem, dst_ty)),
2437 realloc: _,
2438 }),
2439 ) => self.module.import_transcoder(Transcoder {
2440 from_memory: src_mem,
2441 from_memory64: src_ty.idx_type == IndexType::I64,
2442 to_memory: dst_mem,
2443 to_memory64: dst_ty.idx_type == IndexType::I64,
2444 op,
2445 }),
2446 (DataModel::LinearMemory(LinearMemoryOptions { memory: None, .. }), _)
2447 | (_, DataModel::LinearMemory(LinearMemoryOptions { memory: None, .. })) => {
2448 unreachable!()
2449 }
2450 }
2451 }
2452
2453 fn begin_translate_sequence<'c>(
2462 &mut self,
2463 src: &Source<'c>,
2464 dst: &Destination<'c>,
2465 src_element_size: u32,
2466 src_element_align: u32,
2467 dst_element_size: u32,
2468 dst_element_align: u32,
2469 ) -> SequenceTranslation<'c> {
2470 let src_mem_opts = match &src.opts().data_model {
2471 DataModel::Gc {} => todo!("CM+GC"),
2472 DataModel::LinearMemory(opts) => opts,
2473 };
2474 let dst_mem_opts = match &dst.opts().data_model {
2475 DataModel::Gc {} => todo!("CM+GC"),
2476 DataModel::LinearMemory(opts) => opts,
2477 };
2478
2479 let src_opts = src.opts();
2480 let dst_opts = dst.opts();
2481
2482 match src {
2487 Source::Stack(s) => {
2488 assert_eq!(s.locals.len(), 2);
2489 self.stack_get(&s.slice(0..1), src_mem_opts.ptr());
2490 self.stack_get(&s.slice(1..2), src_mem_opts.ptr());
2491 }
2492 Source::Memory(mem) => {
2493 self.ptr_load(mem);
2494 self.ptr_load(&mem.bump(src_mem_opts.ptr_size().into()));
2495 }
2496 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
2497 }
2498 let src_len = self.local_set_new_tmp(src_mem_opts.ptr());
2499 let src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
2500
2501 let src_byte_len =
2503 self.calculate_list_byte_len(src_mem_opts, src_len.idx, src_element_size);
2504 let dst_byte_len = if src_element_size == dst_element_size {
2505 self.convert_src_len_to_dst(src_byte_len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2506 self.local_set_new_tmp(dst_mem_opts.ptr())
2507 } else if src_mem_opts.ptr() == dst_mem_opts.ptr() {
2508 self.calculate_list_byte_len(dst_mem_opts, src_len.idx, dst_element_size)
2509 } else {
2510 self.convert_src_len_to_dst(src_len.idx, src_mem_opts.ptr(), dst_mem_opts.ptr());
2511 let tmp = self.local_set_new_tmp(dst_mem_opts.ptr());
2512 let ret = self.calculate_list_byte_len(dst_mem_opts, tmp.idx, dst_element_size);
2513 self.free_temp_local(tmp);
2514 ret
2515 };
2516
2517 let src_mem = self.memory_operand(
2520 src_opts,
2521 src_ptr,
2522 AllocSize::Local(src_byte_len.idx),
2523 src_element_align,
2524 Trap::ListOutOfBounds,
2525 );
2526
2527 let dst_mem = self.malloc(
2532 dst_opts,
2533 AllocSize::Local(dst_byte_len.idx),
2534 dst_element_align,
2535 Trap::ListOutOfBounds,
2536 );
2537
2538 self.free_temp_local(src_byte_len);
2539 self.free_temp_local(dst_byte_len);
2540
2541 let loop_state = if src_element_size > 0 || dst_element_size > 0 {
2545 self.instruction(Block(BlockType::Empty));
2546
2547 self.instruction(LocalGet(src_len.idx));
2549 let remaining = self.local_tee_new_tmp(src_mem_opts.ptr());
2550 self.ptr_eqz(src_mem_opts);
2551 self.instruction(BrIf(0));
2552
2553 self.instruction(LocalGet(src_mem.addr.idx));
2555 let cur_src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
2556 self.instruction(LocalGet(dst_mem.addr.idx));
2557 let cur_dst_ptr = self.local_set_new_tmp(dst_mem_opts.ptr());
2558
2559 self.instruction(Loop(BlockType::Empty));
2560
2561 Some(SequenceLoopState {
2562 remaining,
2563 cur_src_ptr,
2564 cur_dst_ptr,
2565 })
2566 } else {
2567 None
2568 };
2569
2570 SequenceTranslation {
2571 src_len,
2572 src_mem,
2573 dst_mem,
2574 src_opts,
2575 dst_opts,
2576 src_mem_opts,
2577 dst_mem_opts,
2578 loop_state,
2579 }
2580 }
2581
2582 fn end_translate_sequence(&mut self, seq: SequenceTranslation<'_>, dst: &Destination) {
2588 if let Some(loop_state) = seq.loop_state {
2589 self.instruction(LocalGet(loop_state.remaining.idx));
2592 self.ptr_iconst(seq.src_mem_opts, -1);
2593 self.ptr_add(seq.src_mem_opts);
2594 self.instruction(LocalTee(loop_state.remaining.idx));
2595 self.ptr_br_if(seq.src_mem_opts, 0);
2596 self.instruction(End); self.instruction(End); self.free_temp_local(loop_state.cur_dst_ptr);
2600 self.free_temp_local(loop_state.cur_src_ptr);
2601 self.free_temp_local(loop_state.remaining);
2602 }
2603
2604 match dst {
2606 Destination::Stack(s, _) => {
2607 self.instruction(LocalGet(seq.dst_mem.addr.idx));
2608 self.stack_set(&s[..1], seq.dst_mem_opts.ptr());
2609 self.convert_src_len_to_dst(
2610 seq.src_len.idx,
2611 seq.src_mem_opts.ptr(),
2612 seq.dst_mem_opts.ptr(),
2613 );
2614 self.stack_set(&s[1..], seq.dst_mem_opts.ptr());
2615 }
2616 Destination::Memory(mem) => {
2617 self.instruction(LocalGet(mem.addr.idx));
2618 self.instruction(LocalGet(seq.dst_mem.addr.idx));
2619 self.ptr_store(mem);
2620 self.instruction(LocalGet(mem.addr.idx));
2621 self.convert_src_len_to_dst(
2622 seq.src_len.idx,
2623 seq.src_mem_opts.ptr(),
2624 seq.dst_mem_opts.ptr(),
2625 );
2626 self.ptr_store(&mem.bump(seq.dst_mem_opts.ptr_size().into()));
2627 }
2628 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
2629 }
2630
2631 self.free_temp_local(seq.src_len);
2632 self.free_temp_local(seq.src_mem.addr);
2633 self.free_temp_local(seq.dst_mem.addr);
2634 }
2635
2636 fn translate_list(
2637 &mut self,
2638 src_ty: TypeListIndex,
2639 src: &Source<'_>,
2640 dst_ty: &InterfaceType,
2641 dst: &Destination,
2642 ) {
2643 let src_mem_opts = match &src.opts().data_model {
2644 DataModel::Gc {} => todo!("CM+GC"),
2645 DataModel::LinearMemory(opts) => opts,
2646 };
2647 let dst_mem_opts = match &dst.opts().data_model {
2648 DataModel::Gc {} => todo!("CM+GC"),
2649 DataModel::LinearMemory(opts) => opts,
2650 };
2651
2652 let src_element_ty = &self.types[src_ty].element;
2653 let dst_element_ty = match dst_ty {
2654 InterfaceType::List(r) => &self.types[*r].element,
2655 _ => panic!("expected a list"),
2656 };
2657 let (src_size, src_align) = self.types.size_align(src_mem_opts, src_element_ty);
2658 let (dst_size, dst_align) = self.types.size_align(dst_mem_opts, dst_element_ty);
2659
2660 let seq = self.begin_translate_sequence(src, dst, src_size, src_align, dst_size, dst_align);
2661
2662 if let Some(ref loop_state) = seq.loop_state {
2663 let element_src = Source::Memory(Memory {
2664 opts: seq.src_opts,
2665 offset: 0,
2666 addr: TempLocal::new(loop_state.cur_src_ptr.idx, loop_state.cur_src_ptr.ty),
2667 });
2668 let element_dst = Destination::Memory(Memory {
2669 opts: seq.dst_opts,
2670 offset: 0,
2671 addr: TempLocal::new(loop_state.cur_dst_ptr.idx, loop_state.cur_dst_ptr.ty),
2672 });
2673 self.translate(src_element_ty, &element_src, dst_element_ty, &element_dst);
2674
2675 if src_size > 0 {
2676 self.instruction(LocalGet(loop_state.cur_src_ptr.idx));
2677 self.ptr_uconst(src_mem_opts, src_size);
2678 self.ptr_add(src_mem_opts);
2679 self.instruction(LocalSet(loop_state.cur_src_ptr.idx));
2680 }
2681 if dst_size > 0 {
2682 self.instruction(LocalGet(loop_state.cur_dst_ptr.idx));
2683 self.ptr_uconst(dst_mem_opts, dst_size);
2684 self.ptr_add(dst_mem_opts);
2685 self.instruction(LocalSet(loop_state.cur_dst_ptr.idx));
2686 }
2687 }
2688
2689 self.end_translate_sequence(seq, dst);
2690 }
2691
2692 fn translate_map(
2698 &mut self,
2699 src_ty: TypeMapIndex,
2700 src: &Source<'_>,
2701 dst_ty: &InterfaceType,
2702 dst: &Destination,
2703 ) {
2704 let src_mem_opts = match &src.opts().data_model {
2705 DataModel::Gc {} => todo!("CM+GC"),
2706 DataModel::LinearMemory(opts) => opts,
2707 };
2708 let dst_mem_opts = match &dst.opts().data_model {
2709 DataModel::Gc {} => todo!("CM+GC"),
2710 DataModel::LinearMemory(opts) => opts,
2711 };
2712
2713 let src_map_ty = &self.types[src_ty];
2714 let dst_map_ty = match dst_ty {
2715 InterfaceType::Map(r) => &self.types[*r],
2716 _ => panic!("expected a map"),
2717 };
2718
2719 let src_key_abi = self.types.canonical_abi(&src_map_ty.key);
2721 let src_value_abi = self.types.canonical_abi(&src_map_ty.value);
2722 let src_entry_abi = CanonicalAbiInfo::record([src_key_abi, src_value_abi].into_iter());
2723 let (src_tuple_size, src_entry_align) = src_mem_opts.sizealign(&src_entry_abi);
2724 let src_value_offset = {
2725 let mut offset = 0u32;
2726 if src_mem_opts.memory64() {
2727 src_key_abi.next_field64(&mut offset);
2728 src_value_abi.next_field64(&mut offset)
2729 } else {
2730 src_key_abi.next_field32(&mut offset);
2731 src_value_abi.next_field32(&mut offset)
2732 }
2733 };
2734
2735 let dst_key_abi = self.types.canonical_abi(&dst_map_ty.key);
2736 let dst_value_abi = self.types.canonical_abi(&dst_map_ty.value);
2737 let dst_entry_abi = CanonicalAbiInfo::record([dst_key_abi, dst_value_abi].into_iter());
2738 let (dst_tuple_size, dst_entry_align) = dst_mem_opts.sizealign(&dst_entry_abi);
2739 let dst_value_offset = {
2740 let mut offset = 0u32;
2741 if dst_mem_opts.memory64() {
2742 dst_key_abi.next_field64(&mut offset);
2743 dst_value_abi.next_field64(&mut offset)
2744 } else {
2745 dst_key_abi.next_field32(&mut offset);
2746 dst_value_abi.next_field32(&mut offset)
2747 }
2748 };
2749
2750 let seq = self.begin_translate_sequence(
2751 src,
2752 dst,
2753 src_tuple_size,
2754 src_entry_align,
2755 dst_tuple_size,
2756 dst_entry_align,
2757 );
2758
2759 if let Some(ref loop_state) = seq.loop_state {
2760 let key_src = Source::Memory(Memory {
2761 opts: seq.src_opts,
2762 offset: 0,
2763 addr: TempLocal::new(loop_state.cur_src_ptr.idx, src_mem_opts.ptr()),
2764 });
2765 let key_dst = Destination::Memory(Memory {
2766 opts: seq.dst_opts,
2767 offset: 0,
2768 addr: TempLocal::new(loop_state.cur_dst_ptr.idx, dst_mem_opts.ptr()),
2769 });
2770 self.translate(&src_map_ty.key, &key_src, &dst_map_ty.key, &key_dst);
2771
2772 let value_src = Source::Memory(Memory {
2773 opts: seq.src_opts,
2774 offset: src_value_offset,
2775 addr: TempLocal::new(loop_state.cur_src_ptr.idx, src_mem_opts.ptr()),
2776 });
2777 let value_dst = Destination::Memory(Memory {
2778 opts: seq.dst_opts,
2779 offset: dst_value_offset,
2780 addr: TempLocal::new(loop_state.cur_dst_ptr.idx, dst_mem_opts.ptr()),
2781 });
2782 self.translate(&src_map_ty.value, &value_src, &dst_map_ty.value, &value_dst);
2783
2784 if src_tuple_size > 0 {
2786 self.instruction(LocalGet(loop_state.cur_src_ptr.idx));
2787 self.ptr_uconst(src_mem_opts, src_tuple_size);
2788 self.ptr_add(src_mem_opts);
2789 self.instruction(LocalSet(loop_state.cur_src_ptr.idx));
2790 }
2791 if dst_tuple_size > 0 {
2792 self.instruction(LocalGet(loop_state.cur_dst_ptr.idx));
2793 self.ptr_uconst(dst_mem_opts, dst_tuple_size);
2794 self.ptr_add(dst_mem_opts);
2795 self.instruction(LocalSet(loop_state.cur_dst_ptr.idx));
2796 }
2797 }
2798
2799 self.end_translate_sequence(seq, dst);
2800 }
2801
2802 fn calculate_list_byte_len(
2803 &mut self,
2804 opts: &LinearMemoryOptions,
2805 len_local: u32,
2806 elt_size: u32,
2807 ) -> TempLocal {
2808 if elt_size == 0 {
2811 self.ptr_uconst(opts, 0);
2812 return self.local_set_new_tmp(opts.ptr());
2813 }
2814
2815 if elt_size == 1 {
2823 if let ValType::I64 = opts.ptr() {
2824 self.instruction(LocalGet(len_local));
2825 self.instruction(I64Const(32));
2826 self.instruction(I64ShrU);
2827 self.instruction(I32WrapI64);
2828 self.instruction(If(BlockType::Empty));
2829 self.trap(Trap::ListOutOfBounds);
2830 self.instruction(End);
2831 }
2832 self.instruction(LocalGet(len_local));
2833 return self.local_set_new_tmp(opts.ptr());
2834 }
2835
2836 self.instruction(Block(BlockType::Empty));
2841 self.instruction(Block(BlockType::Empty));
2842 self.instruction(LocalGet(len_local));
2843 match opts.ptr() {
2844 ValType::I32 => self.instruction(I64ExtendI32U),
2848
2849 ValType::I64 => {
2853 self.instruction(I64Const(32));
2854 self.instruction(I64ShrU);
2855 self.instruction(I32WrapI64);
2856 self.instruction(BrIf(0));
2857 self.instruction(LocalGet(len_local));
2858 }
2859
2860 _ => unreachable!(),
2861 }
2862
2863 self.instruction(I64Const(elt_size.into()));
2872 self.instruction(I64Mul);
2873 let tmp = self.local_tee_new_tmp(ValType::I64);
2874 self.instruction(I64Const(32));
2877 self.instruction(I64ShrU);
2878 self.instruction(I64Eqz);
2879 self.instruction(BrIf(1));
2880 self.instruction(End);
2881 self.trap(Trap::ListOutOfBounds);
2882 self.instruction(End);
2883
2884 if opts.ptr() == ValType::I64 {
2888 tmp
2889 } else {
2890 self.instruction(LocalGet(tmp.idx));
2891 self.instruction(I32WrapI64);
2892 self.free_temp_local(tmp);
2893 self.local_set_new_tmp(ValType::I32)
2894 }
2895 }
2896
2897 fn convert_src_len_to_dst(
2898 &mut self,
2899 src_len_local: u32,
2900 src_ptr_ty: ValType,
2901 dst_ptr_ty: ValType,
2902 ) {
2903 self.instruction(LocalGet(src_len_local));
2904 match (src_ptr_ty, dst_ptr_ty) {
2905 (ValType::I32, ValType::I64) => self.instruction(I64ExtendI32U),
2906 (ValType::I64, ValType::I32) => self.instruction(I32WrapI64),
2907 (src, dst) => assert_eq!(src, dst),
2908 }
2909 }
2910
2911 fn translate_record(
2912 &mut self,
2913 src_ty: TypeRecordIndex,
2914 src: &Source<'_>,
2915 dst_ty: &InterfaceType,
2916 dst: &Destination,
2917 ) {
2918 let src_ty = &self.types[src_ty];
2919 let dst_ty = match dst_ty {
2920 InterfaceType::Record(r) => &self.types[*r],
2921 _ => panic!("expected a record"),
2922 };
2923
2924 assert_eq!(src_ty.fields.len(), dst_ty.fields.len());
2926
2927 let mut src_fields = HashMap::new();
2931 for (i, src) in src
2932 .record_field_srcs(self.types, src_ty.fields.iter().map(|f| f.ty))
2933 .enumerate()
2934 {
2935 let field = &src_ty.fields[i];
2936 src_fields.insert(&field.name, (src, &field.ty));
2937 }
2938
2939 for (i, dst) in dst
2948 .record_field_dsts(self.types, dst_ty.fields.iter().map(|f| f.ty))
2949 .enumerate()
2950 {
2951 let field = &dst_ty.fields[i];
2952 let (src, src_ty) = &src_fields[&field.name];
2953 self.translate(src_ty, src, &field.ty, &dst);
2954 }
2955 }
2956
2957 fn translate_flags(
2958 &mut self,
2959 src_ty: TypeFlagsIndex,
2960 src: &Source<'_>,
2961 dst_ty: &InterfaceType,
2962 dst: &Destination,
2963 ) {
2964 let src_ty = &self.types[src_ty];
2965 let dst_ty = match dst_ty {
2966 InterfaceType::Flags(r) => &self.types[*r],
2967 _ => panic!("expected a record"),
2968 };
2969
2970 assert_eq!(src_ty.names, dst_ty.names);
2978 let cnt = src_ty.names.len();
2979 match FlagsSize::from_count(cnt) {
2980 FlagsSize::Size0 => {}
2981 FlagsSize::Size1 => {
2982 let mask = if cnt == 8 { 0xff } else { (1 << cnt) - 1 };
2983 self.convert_u8_mask(src, dst, mask);
2984 }
2985 FlagsSize::Size2 => {
2986 let mask = if cnt == 16 { 0xffff } else { (1 << cnt) - 1 };
2987 self.convert_u16_mask(src, dst, mask);
2988 }
2989 FlagsSize::Size4Plus(n) => {
2990 let srcs = src.record_field_srcs(self.types, (0..n).map(|_| InterfaceType::U32));
2991 let dsts = dst.record_field_dsts(self.types, (0..n).map(|_| InterfaceType::U32));
2992 let n = usize::from(n);
2993 for (i, (src, dst)) in srcs.zip(dsts).enumerate() {
2994 let mask = if i == n - 1 && (cnt % 32 != 0) {
2995 (1 << (cnt % 32)) - 1
2996 } else {
2997 0xffffffff
2998 };
2999 self.convert_u32_mask(&src, &dst, mask);
3000 }
3001 }
3002 }
3003 }
3004
3005 fn translate_tuple(
3006 &mut self,
3007 src_ty: TypeTupleIndex,
3008 src: &Source<'_>,
3009 dst_ty: &InterfaceType,
3010 dst: &Destination,
3011 ) {
3012 let src_ty = &self.types[src_ty];
3013 let dst_ty = match dst_ty {
3014 InterfaceType::Tuple(t) => &self.types[*t],
3015 _ => panic!("expected a tuple"),
3016 };
3017
3018 assert_eq!(src_ty.types.len(), dst_ty.types.len());
3020
3021 let srcs = src
3022 .record_field_srcs(self.types, src_ty.types.iter().copied())
3023 .zip(src_ty.types.iter());
3024 let dsts = dst
3025 .record_field_dsts(self.types, dst_ty.types.iter().copied())
3026 .zip(dst_ty.types.iter());
3027 for ((src, src_ty), (dst, dst_ty)) in srcs.zip(dsts) {
3028 self.translate(src_ty, &src, dst_ty, &dst);
3029 }
3030 }
3031
3032 fn translate_fixed_length_list(
3033 &mut self,
3034 src_ty: TypeFixedLengthListIndex,
3035 src: &Source<'_>,
3036 dst_ty: &InterfaceType,
3037 dst: &Destination,
3038 ) {
3039 let src_ty = &self.types[src_ty];
3040 let dst_ty = match dst_ty {
3041 InterfaceType::FixedLengthList(t) => &self.types[*t],
3042 _ => panic!("expected a fixed size list"),
3043 };
3044
3045 assert_eq!(src_ty.size, dst_ty.size);
3047
3048 match (&src, &dst) {
3049 (Source::Memory(src_mem), Destination::Memory(dst_mem)) => {
3051 let src_mem_opts = match &src_mem.opts.data_model {
3052 DataModel::Gc {} => todo!("CM+GC"),
3053 DataModel::LinearMemory(opts) => opts,
3054 };
3055 let dst_mem_opts = match &dst_mem.opts.data_model {
3056 DataModel::Gc {} => todo!("CM+GC"),
3057 DataModel::LinearMemory(opts) => opts,
3058 };
3059 let src_element_bytes = self.types.size_align(src_mem_opts, &src_ty.element).0;
3060 let dst_element_bytes = self.types.size_align(dst_mem_opts, &dst_ty.element).0;
3061 assert_ne!(src_element_bytes, 0);
3062 assert_ne!(dst_element_bytes, 0);
3063
3064 self.instruction(LocalGet(src_mem.addr.idx));
3067 if src_mem.offset != 0 {
3068 self.ptr_uconst(src_mem_opts, src_mem.offset);
3069 self.ptr_add(src_mem_opts);
3070 }
3071 let cur_src_ptr = self.local_set_new_tmp(src_mem_opts.ptr());
3072 self.instruction(LocalGet(dst_mem.addr.idx));
3073 if dst_mem.offset != 0 {
3074 self.ptr_uconst(dst_mem_opts, dst_mem.offset);
3075 self.ptr_add(dst_mem_opts);
3076 }
3077 let cur_dst_ptr = self.local_set_new_tmp(dst_mem_opts.ptr());
3078
3079 self.instruction(I32Const(src_ty.size as i32));
3080 let remaining = self.local_set_new_tmp(ValType::I32);
3081
3082 self.instruction(Loop(BlockType::Empty));
3083
3084 let element_src = Source::Memory(Memory {
3086 opts: src_mem.opts,
3087 offset: 0,
3088 addr: TempLocal::new(cur_src_ptr.idx, cur_src_ptr.ty),
3089 });
3090 let element_dst = Destination::Memory(Memory {
3091 opts: dst_mem.opts,
3092 offset: 0,
3093 addr: TempLocal::new(cur_dst_ptr.idx, cur_dst_ptr.ty),
3094 });
3095 self.translate(&src_ty.element, &element_src, &dst_ty.element, &element_dst);
3096
3097 self.instruction(LocalGet(cur_src_ptr.idx));
3099 self.ptr_uconst(src_mem_opts, src_element_bytes);
3100 self.ptr_add(src_mem_opts);
3101 self.instruction(LocalSet(cur_src_ptr.idx));
3102 self.instruction(LocalGet(cur_dst_ptr.idx));
3103 self.ptr_uconst(dst_mem_opts, dst_element_bytes);
3104 self.ptr_add(dst_mem_opts);
3105 self.instruction(LocalSet(cur_dst_ptr.idx));
3106
3107 self.instruction(LocalGet(remaining.idx));
3110 self.ptr_iconst(src_mem_opts, -1);
3111 self.ptr_add(src_mem_opts);
3112 self.instruction(LocalTee(remaining.idx));
3113 self.ptr_br_if(src_mem_opts, 0);
3114 self.instruction(End); self.free_temp_local(cur_dst_ptr);
3117 self.free_temp_local(cur_src_ptr);
3118 self.free_temp_local(remaining);
3119 return;
3120 }
3121 (_, _) => {
3123 assert!(
3125 src_ty.size as usize <= MAX_FLAT_PARAMS
3126 && dst_ty.size as usize <= MAX_FLAT_PARAMS
3127 );
3128 let srcs =
3129 src.record_field_srcs(self.types, (0..src_ty.size).map(|_| src_ty.element));
3130 let dsts =
3131 dst.record_field_dsts(self.types, (0..dst_ty.size).map(|_| dst_ty.element));
3132 for (src, dst) in srcs.zip(dsts) {
3133 self.translate(&src_ty.element, &src, &dst_ty.element, &dst);
3134 }
3135 }
3136 }
3137 }
3138
3139 fn translate_variant(
3140 &mut self,
3141 src_ty: TypeVariantIndex,
3142 src: &Source<'_>,
3143 dst_ty: &InterfaceType,
3144 dst: &Destination,
3145 ) {
3146 let src_ty = &self.types[src_ty];
3147 let dst_ty = match dst_ty {
3148 InterfaceType::Variant(t) => &self.types[*t],
3149 _ => panic!("expected a variant"),
3150 };
3151
3152 let src_info = variant_info(self.types, src_ty.cases.iter().map(|(_, c)| c.as_ref()));
3153 let dst_info = variant_info(self.types, dst_ty.cases.iter().map(|(_, c)| c.as_ref()));
3154
3155 let iter = src_ty
3156 .cases
3157 .iter()
3158 .enumerate()
3159 .map(|(src_i, (src_case, src_case_ty))| {
3160 let dst_i = dst_ty
3161 .cases
3162 .iter()
3163 .position(|(c, _)| c == src_case)
3164 .unwrap();
3165 let dst_case_ty = &dst_ty.cases[dst_i];
3166 let src_i = u32::try_from(src_i).unwrap();
3167 let dst_i = u32::try_from(dst_i).unwrap();
3168 VariantCase {
3169 src_i,
3170 src_ty: src_case_ty.as_ref(),
3171 dst_i,
3172 dst_ty: dst_case_ty.as_ref(),
3173 }
3174 });
3175 self.convert_variant(src, &src_info, dst, &dst_info, iter);
3176 }
3177
3178 fn translate_enum(
3179 &mut self,
3180 src_ty: TypeEnumIndex,
3181 src: &Source<'_>,
3182 dst_ty: &InterfaceType,
3183 dst: &Destination,
3184 ) {
3185 let src_ty = &self.types[src_ty];
3186 let dst_ty = match dst_ty {
3187 InterfaceType::Enum(t) => &self.types[*t],
3188 _ => panic!("expected an option"),
3189 };
3190
3191 debug_assert_eq!(src_ty.info.size, dst_ty.info.size);
3192 debug_assert_eq!(src_ty.names.len(), dst_ty.names.len());
3193 debug_assert!(
3194 src_ty
3195 .names
3196 .iter()
3197 .zip(dst_ty.names.iter())
3198 .all(|(a, b)| a == b)
3199 );
3200
3201 match src {
3203 Source::Stack(s) => self.stack_get(&s.slice(0..1), ValType::I32),
3204 Source::Memory(mem) => match src_ty.info.size {
3205 DiscriminantSize::Size1 => self.i32_load8u(mem),
3206 DiscriminantSize::Size2 => self.i32_load16u(mem),
3207 DiscriminantSize::Size4 => self.i32_load(mem),
3208 },
3209 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
3210 }
3211 let tmp = self.local_tee_new_tmp(ValType::I32);
3212
3213 self.instruction(I32Const(i32::try_from(src_ty.names.len()).unwrap()));
3215 self.instruction(I32GeU);
3216 self.instruction(If(BlockType::Empty));
3217 self.trap(Trap::InvalidDiscriminant);
3218 self.instruction(End);
3219
3220 match dst {
3222 Destination::Stack(stack, _) => {
3223 self.local_get_tmp(&tmp);
3224 self.stack_set(&stack[..1], ValType::I32)
3225 }
3226 Destination::Memory(mem) => {
3227 self.push_dst_addr(dst);
3228 self.local_get_tmp(&tmp);
3229 match dst_ty.info.size {
3230 DiscriminantSize::Size1 => self.i32_store8(mem),
3231 DiscriminantSize::Size2 => self.i32_store16(mem),
3232 DiscriminantSize::Size4 => self.i32_store(mem),
3233 }
3234 }
3235 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3236 }
3237 self.free_temp_local(tmp);
3238 }
3239
3240 fn translate_option(
3241 &mut self,
3242 src_ty: TypeOptionIndex,
3243 src: &Source<'_>,
3244 dst_ty: &InterfaceType,
3245 dst: &Destination,
3246 ) {
3247 let src_ty = &self.types[src_ty].ty;
3248 let dst_ty = match dst_ty {
3249 InterfaceType::Option(t) => &self.types[*t].ty,
3250 _ => panic!("expected an option"),
3251 };
3252 let src_ty = Some(src_ty);
3253 let dst_ty = Some(dst_ty);
3254
3255 let src_info = variant_info(self.types, [None, src_ty]);
3256 let dst_info = variant_info(self.types, [None, dst_ty]);
3257
3258 self.convert_variant(
3259 src,
3260 &src_info,
3261 dst,
3262 &dst_info,
3263 [
3264 VariantCase {
3265 src_i: 0,
3266 dst_i: 0,
3267 src_ty: None,
3268 dst_ty: None,
3269 },
3270 VariantCase {
3271 src_i: 1,
3272 dst_i: 1,
3273 src_ty,
3274 dst_ty,
3275 },
3276 ]
3277 .into_iter(),
3278 );
3279 }
3280
3281 fn translate_result(
3282 &mut self,
3283 src_ty: TypeResultIndex,
3284 src: &Source<'_>,
3285 dst_ty: &InterfaceType,
3286 dst: &Destination,
3287 ) {
3288 let src_ty = &self.types[src_ty];
3289 let dst_ty = match dst_ty {
3290 InterfaceType::Result(t) => &self.types[*t],
3291 _ => panic!("expected a result"),
3292 };
3293
3294 let src_info = variant_info(self.types, [src_ty.ok.as_ref(), src_ty.err.as_ref()]);
3295 let dst_info = variant_info(self.types, [dst_ty.ok.as_ref(), dst_ty.err.as_ref()]);
3296
3297 self.convert_variant(
3298 src,
3299 &src_info,
3300 dst,
3301 &dst_info,
3302 [
3303 VariantCase {
3304 src_i: 0,
3305 dst_i: 0,
3306 src_ty: src_ty.ok.as_ref(),
3307 dst_ty: dst_ty.ok.as_ref(),
3308 },
3309 VariantCase {
3310 src_i: 1,
3311 dst_i: 1,
3312 src_ty: src_ty.err.as_ref(),
3313 dst_ty: dst_ty.err.as_ref(),
3314 },
3315 ]
3316 .into_iter(),
3317 );
3318 }
3319
3320 fn convert_variant<'c>(
3321 &mut self,
3322 src: &Source<'_>,
3323 src_info: &VariantInfo,
3324 dst: &Destination,
3325 dst_info: &VariantInfo,
3326 src_cases: impl ExactSizeIterator<Item = VariantCase<'c>>,
3327 ) {
3328 let outer_block_ty = match dst {
3331 Destination::Stack(dst_flat, _) => match dst_flat.len() {
3332 0 => BlockType::Empty,
3333 1 => BlockType::Result(dst_flat[0]),
3334 _ => {
3335 let ty = self.module.core_types.function(&[], &dst_flat);
3336 BlockType::FunctionType(ty)
3337 }
3338 },
3339 Destination::Memory(_) => BlockType::Empty,
3340 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3341 };
3342 self.instruction(Block(outer_block_ty));
3343
3344 let src_cases_len = src_cases.len();
3347 for _ in 0..src_cases_len - 1 {
3348 self.instruction(Block(BlockType::Empty));
3349 }
3350
3351 self.instruction(Block(BlockType::Empty));
3353
3354 self.instruction(Block(BlockType::Empty));
3357
3358 match src {
3360 Source::Stack(s) => self.stack_get(&s.slice(0..1), ValType::I32),
3361 Source::Memory(mem) => match src_info.size {
3362 DiscriminantSize::Size1 => self.i32_load8u(mem),
3363 DiscriminantSize::Size2 => self.i32_load16u(mem),
3364 DiscriminantSize::Size4 => self.i32_load(mem),
3365 },
3366 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
3367 }
3368
3369 let mut targets = Vec::new();
3372 for i in 0..src_cases_len {
3373 targets.push((i + 1) as u32);
3374 }
3375 self.instruction(BrTable(targets[..].into(), 0));
3376 self.instruction(End); self.trap(Trap::InvalidDiscriminant);
3379 self.instruction(End); let src_cases_len = u32::try_from(src_cases_len).unwrap();
3386 for case in src_cases {
3387 let VariantCase {
3388 src_i,
3389 src_ty,
3390 dst_i,
3391 dst_ty,
3392 } = case;
3393
3394 self.push_dst_addr(dst);
3397 self.instruction(I32Const(dst_i as i32));
3398 match dst {
3399 Destination::Stack(stack, _) => self.stack_set(&stack[..1], ValType::I32),
3400 Destination::Memory(mem) => match dst_info.size {
3401 DiscriminantSize::Size1 => self.i32_store8(mem),
3402 DiscriminantSize::Size2 => self.i32_store16(mem),
3403 DiscriminantSize::Size4 => self.i32_store(mem),
3404 },
3405 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3406 }
3407
3408 let src_payload = src.payload_src(self.types, src_info, src_ty);
3409 let dst_payload = dst.payload_dst(self.types, dst_info, dst_ty);
3410
3411 match (src_ty, dst_ty) {
3414 (Some(src_ty), Some(dst_ty)) => {
3415 self.translate(src_ty, &src_payload, dst_ty, &dst_payload);
3416 }
3417 (None, None) => {}
3418 _ => unimplemented!(),
3419 }
3420
3421 if let Destination::Stack(payload_results, _) = dst_payload {
3428 if let Destination::Stack(dst_results, _) = dst {
3429 let remaining = &dst_results[1..][payload_results.len()..];
3430 for ty in remaining {
3431 match ty {
3432 ValType::I32 => self.instruction(I32Const(0)),
3433 ValType::I64 => self.instruction(I64Const(0)),
3434 ValType::F32 => self.instruction(F32Const(0.0.into())),
3435 ValType::F64 => self.instruction(F64Const(0.0.into())),
3436 _ => unreachable!(),
3437 }
3438 }
3439 }
3440 }
3441
3442 if src_i != src_cases_len - 1 {
3445 self.instruction(Br(src_cases_len - src_i - 1));
3446 }
3447 self.instruction(End); }
3449 }
3450
3451 fn translate_future(
3452 &mut self,
3453 src_ty: TypeFutureTableIndex,
3454 src: &Source<'_>,
3455 dst_ty: &InterfaceType,
3456 dst: &Destination,
3457 ) {
3458 let dst_ty = match dst_ty {
3459 InterfaceType::Future(t) => *t,
3460 _ => panic!("expected a `Future`"),
3461 };
3462 let transfer = self.module.import_future_transfer();
3463 self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3464 }
3465
3466 fn translate_stream(
3467 &mut self,
3468 src_ty: TypeStreamTableIndex,
3469 src: &Source<'_>,
3470 dst_ty: &InterfaceType,
3471 dst: &Destination,
3472 ) {
3473 let dst_ty = match dst_ty {
3474 InterfaceType::Stream(t) => *t,
3475 _ => panic!("expected a `Stream`"),
3476 };
3477 let transfer = self.module.import_stream_transfer();
3478 self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3479 }
3480
3481 fn translate_error_context(
3482 &mut self,
3483 src_ty: TypeComponentLocalErrorContextTableIndex,
3484 src: &Source<'_>,
3485 dst_ty: &InterfaceType,
3486 dst: &Destination,
3487 ) {
3488 let dst_ty = match dst_ty {
3489 InterfaceType::ErrorContext(t) => *t,
3490 _ => panic!("expected an `ErrorContext`"),
3491 };
3492 let transfer = self.module.import_error_context_transfer();
3493 self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3494 }
3495
3496 fn translate_own(
3497 &mut self,
3498 src_ty: TypeResourceTableIndex,
3499 src: &Source<'_>,
3500 dst_ty: &InterfaceType,
3501 dst: &Destination,
3502 ) {
3503 let dst_ty = match dst_ty {
3504 InterfaceType::Own(t) => *t,
3505 _ => panic!("expected an `Own`"),
3506 };
3507 let transfer = self.module.import_resource_transfer_own();
3508 self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3509 }
3510
3511 fn translate_borrow(
3512 &mut self,
3513 src_ty: TypeResourceTableIndex,
3514 src: &Source<'_>,
3515 dst_ty: &InterfaceType,
3516 dst: &Destination,
3517 ) {
3518 let dst_ty = match dst_ty {
3519 InterfaceType::Borrow(t) => *t,
3520 _ => panic!("expected an `Borrow`"),
3521 };
3522
3523 let transfer = self.module.import_resource_transfer_borrow();
3524 self.translate_handle(src_ty.as_u32(), src, dst_ty.as_u32(), dst, transfer);
3525 }
3526
3527 fn translate_handle(
3535 &mut self,
3536 src_ty: u32,
3537 src: &Source<'_>,
3538 dst_ty: u32,
3539 dst: &Destination,
3540 transfer: FuncIndex,
3541 ) {
3542 self.push_dst_addr(dst);
3543 match src {
3544 Source::Memory(mem) => self.i32_load(mem),
3545 Source::Stack(stack) => self.stack_get(stack, ValType::I32),
3546 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
3547 }
3548 self.instruction(I32Const(src_ty as i32));
3549 self.instruction(I32Const(dst_ty as i32));
3550 self.instruction(Call(transfer.as_u32()));
3551 match dst {
3552 Destination::Memory(mem) => self.i32_store(mem),
3553 Destination::Stack(stack, _) => self.stack_set(stack, ValType::I32),
3554 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
3555 }
3556 }
3557
3558 fn trap_if_not_flag(&mut self, flags_global: GlobalIndex, flag_to_test: i32, trap: Trap) {
3559 self.instruction(GlobalGet(flags_global.as_u32()));
3560 self.instruction(I32Const(flag_to_test));
3561 self.instruction(I32And);
3562 self.instruction(I32Eqz);
3563 self.instruction(If(BlockType::Empty));
3564 self.trap(trap);
3565 self.instruction(End);
3566 }
3567
3568 fn set_flag(&mut self, flags_global: GlobalIndex, flag_to_set: i32, value: bool) {
3569 self.instruction(GlobalGet(flags_global.as_u32()));
3570 if value {
3571 self.instruction(I32Const(flag_to_set));
3572 self.instruction(I32Or);
3573 } else {
3574 self.instruction(I32Const(!flag_to_set));
3575 self.instruction(I32And);
3576 }
3577 self.instruction(GlobalSet(flags_global.as_u32()));
3578 }
3579
3580 fn assert_aligned(&mut self, ty: &InterfaceType, mem: &Memory) {
3581 let mem_opts = mem.mem_opts();
3582 if !self.module.tunables.debug_adapter_modules {
3583 return;
3584 }
3585 let align = self.types.align(mem_opts, ty);
3586 if align == 1 {
3587 return;
3588 }
3589 assert!(align.is_power_of_two());
3590 self.instruction(LocalGet(mem.addr.idx));
3591 self.ptr_uconst(mem_opts, mem.offset);
3592 self.ptr_add(mem_opts);
3593 self.ptr_uconst(mem_opts, align - 1);
3594 self.ptr_and(mem_opts);
3595 self.ptr_if(mem_opts, BlockType::Empty);
3596 self.trap(Trap::DebugAssertPointerAligned);
3597 self.instruction(End);
3598 }
3599
3600 fn malloc_abi<'c>(
3606 &mut self,
3607 opts: &'c Options,
3608 abi: &CanonicalAbiInfo,
3609 oob_trap: Trap,
3610 ) -> Memory<'c> {
3611 match &opts.data_model {
3612 DataModel::Gc {} => todo!("CM+GC"),
3613 DataModel::LinearMemory(mem_opts) => {
3614 let (size, align) = mem_opts.sizealign(abi);
3615 let size = AllocSize::Const(size);
3616 self.malloc(opts, size, align, oob_trap)
3617 }
3618 }
3619 }
3620
3621 fn malloc<'c>(
3627 &mut self,
3628 opts: &'c Options,
3629 size: AllocSize,
3630 align: u32,
3631 oob_trap: Trap,
3632 ) -> Memory<'c> {
3633 match &opts.data_model {
3634 DataModel::Gc {} => todo!("CM+GC"),
3635 DataModel::LinearMemory(mem_opts) => {
3636 let realloc = mem_opts.realloc.unwrap();
3637 self.ptr_uconst(mem_opts, 0);
3638 self.ptr_uconst(mem_opts, 0);
3639 self.ptr_uconst(mem_opts, align);
3640 self.alloc_size(mem_opts, &size);
3641 self.instruction(Call(realloc.as_u32()));
3642 let addr = self.local_set_new_tmp(mem_opts.ptr());
3643 self.memory_operand(opts, addr, size, align, oob_trap)
3644 }
3645 }
3646 }
3647
3648 fn realloc(
3654 &mut self,
3655 opts: &Options,
3656 ptr: &TempLocal,
3657 prev_size: AllocSize,
3658 size: AllocSize,
3659 align: u32,
3660 oob_trap: Trap,
3661 ) {
3662 match &opts.data_model {
3663 DataModel::Gc {} => todo!("CM+GC"),
3664 DataModel::LinearMemory(mem_opts) => {
3665 let realloc = mem_opts.realloc.unwrap();
3666 self.instruction(LocalGet(ptr.idx));
3667 self.alloc_size(mem_opts, &prev_size);
3668 self.ptr_uconst(mem_opts, align);
3669 self.alloc_size(mem_opts, &size);
3670 self.instruction(Call(realloc.as_u32()));
3671 self.instruction(LocalSet(ptr.idx));
3672 self.validate_guest_pointer(opts, &ptr, &size, align, oob_trap)
3673 }
3674 }
3675 }
3676
3677 fn memory_operand_abi<'c>(
3680 &mut self,
3681 opts: &'c Options,
3682 addr: TempLocal,
3683 abi: &CanonicalAbiInfo,
3684 oob_trap: Trap,
3685 ) -> Memory<'c> {
3686 match &opts.data_model {
3687 DataModel::Gc {} => todo!("CM+GC"),
3688 DataModel::LinearMemory(mem_opts) => {
3689 let (size, align) = mem_opts.sizealign(abi);
3690 self.memory_operand(opts, addr, AllocSize::Const(size), align, oob_trap)
3691 }
3692 }
3693 }
3694
3695 fn memory_operand<'c>(
3698 &mut self,
3699 opts: &'c Options,
3700 addr: TempLocal,
3701 size: AllocSize,
3702 align: u32,
3703 oob_trap: Trap,
3704 ) -> Memory<'c> {
3705 self.validate_guest_pointer(opts, &addr, &size, align, oob_trap);
3706 Memory {
3707 addr,
3708 opts,
3709 offset: 0,
3710 }
3711 }
3712
3713 fn validate_guest_pointer(
3721 &mut self,
3722 opts: &Options,
3723 addr: &TempLocal,
3724 size: &AllocSize,
3725 align: u32,
3726 oob_trap: Trap,
3727 ) {
3728 let mem_opts = match &opts.data_model {
3729 DataModel::Gc {} => todo!("CM+GC"),
3730 DataModel::LinearMemory(mem_opts) => mem_opts,
3731 };
3732
3733 if align != 1 {
3736 self.instruction(LocalGet(addr.idx));
3737 assert!(align.is_power_of_two());
3738 self.ptr_uconst(mem_opts, align - 1);
3739 self.ptr_and(mem_opts);
3740 self.ptr_if(mem_opts, BlockType::Empty);
3741 self.trap(Trap::UnalignedPointer);
3742 self.instruction(End);
3743 }
3744
3745 let extend_to_64 = |me: &mut Self| {
3746 if !mem_opts.memory64() {
3747 me.instruction(I64ExtendI32U);
3748 }
3749 };
3750
3751 self.instruction(Block(BlockType::Empty));
3752 self.instruction(Block(BlockType::Empty));
3753 let (memory, ty) = mem_opts.memory.unwrap();
3754
3755 self.instruction(MemorySize(memory.as_u32()));
3760 extend_to_64(self);
3761 self.instruction(I64Const(ty.page_size_log2.into()));
3762 self.instruction(I64Shl);
3763
3764 self.instruction(LocalGet(addr.idx));
3769 extend_to_64(self);
3770 self.alloc_size(mem_opts, size);
3771 extend_to_64(self);
3772 self.instruction(I64Add);
3773 if mem_opts.memory64() {
3774 let tmp = self.local_tee_new_tmp(ValType::I64);
3775 self.instruction(LocalGet(addr.idx));
3776 self.ptr_lt_u(mem_opts);
3777 self.instruction(BrIf(0));
3778 self.instruction(LocalGet(tmp.idx));
3779 self.free_temp_local(tmp);
3780 }
3781
3782 self.instruction(I64GeU);
3786 self.instruction(BrIf(1));
3787
3788 self.instruction(End);
3789 self.trap(oob_trap);
3790 self.instruction(End);
3791 }
3792
3793 fn local_tee_new_tmp(&mut self, ty: ValType) -> TempLocal {
3799 self.gen_temp_local(ty, LocalTee)
3800 }
3801
3802 fn local_set_new_tmp(&mut self, ty: ValType) -> TempLocal {
3805 self.gen_temp_local(ty, LocalSet)
3806 }
3807
3808 fn local_get_tmp(&mut self, local: &TempLocal) {
3809 self.instruction(LocalGet(local.idx));
3810 }
3811
3812 fn gen_temp_local(&mut self, ty: ValType, insn: fn(u32) -> Instruction<'static>) -> TempLocal {
3813 if let Some(idx) = self.free_locals.get_mut(&ty).and_then(|v| v.pop()) {
3816 self.instruction(insn(idx));
3817 return TempLocal {
3818 ty,
3819 idx,
3820 needs_free: true,
3821 };
3822 }
3823
3824 let locals = &mut self.module.funcs[self.result].locals;
3826 match locals.last_mut() {
3827 Some((cnt, prev_ty)) if ty == *prev_ty => *cnt += 1,
3828 _ => locals.push((1, ty)),
3829 }
3830 self.nlocals += 1;
3831 let idx = self.nlocals - 1;
3832 self.instruction(insn(idx));
3833 TempLocal {
3834 ty,
3835 idx,
3836 needs_free: true,
3837 }
3838 }
3839
3840 fn free_temp_local(&mut self, mut local: TempLocal) {
3843 assert!(local.needs_free);
3844 self.free_locals
3845 .entry(local.ty)
3846 .or_insert(Vec::new())
3847 .push(local.idx);
3848 local.needs_free = false;
3849 }
3850
3851 fn instruction(&mut self, instr: Instruction) {
3852 instr.encode(&mut self.code);
3853 }
3854
3855 fn trap(&mut self, trap: Trap) {
3856 let trap_func = self.module.import_trap();
3857 self.instruction(I32Const(trap as i32));
3858 self.instruction(Call(trap_func.as_u32()));
3859 self.instruction(Unreachable);
3860 }
3861
3862 fn flush_code(&mut self) {
3867 if self.code.is_empty() {
3868 return;
3869 }
3870 self.module.funcs[self.result]
3871 .body
3872 .push(Body::Raw(mem::take(&mut self.code)));
3873 }
3874
3875 fn finish(mut self) {
3876 self.instruction(End);
3879 self.flush_code();
3880
3881 self.module.funcs[self.result].filled_in = true;
3884 }
3885
3886 fn stack_get(&mut self, stack: &Stack<'_>, dst_ty: ValType) {
3894 assert_eq!(stack.locals.len(), 1);
3895 let (idx, src_ty) = stack.locals[0];
3896 self.instruction(LocalGet(idx));
3897 match (src_ty, dst_ty) {
3898 (ValType::I32, ValType::I32)
3899 | (ValType::I64, ValType::I64)
3900 | (ValType::F32, ValType::F32)
3901 | (ValType::F64, ValType::F64) => {}
3902
3903 (ValType::I32, ValType::F32) => self.instruction(F32ReinterpretI32),
3904 (ValType::I64, ValType::I32) => {
3905 self.assert_i64_upper_bits_not_set(idx);
3906 self.instruction(I32WrapI64);
3907 }
3908 (ValType::I64, ValType::F64) => self.instruction(F64ReinterpretI64),
3909 (ValType::I64, ValType::F32) => {
3910 self.assert_i64_upper_bits_not_set(idx);
3911 self.instruction(I32WrapI64);
3912 self.instruction(F32ReinterpretI32);
3913 }
3914
3915 (ValType::I32, ValType::I64)
3917 | (ValType::I32, ValType::F64)
3918 | (ValType::F32, ValType::I32)
3919 | (ValType::F32, ValType::I64)
3920 | (ValType::F32, ValType::F64)
3921 | (ValType::F64, ValType::I32)
3922 | (ValType::F64, ValType::I64)
3923 | (ValType::F64, ValType::F32)
3924
3925 | (ValType::Ref(_), _)
3927 | (_, ValType::Ref(_))
3928 | (ValType::V128, _)
3929 | (_, ValType::V128) => {
3930 panic!("cannot get {dst_ty:?} from {src_ty:?} local");
3931 }
3932 }
3933 }
3934
3935 fn assert_i64_upper_bits_not_set(&mut self, local: u32) {
3936 if !self.module.tunables.debug_adapter_modules {
3937 return;
3938 }
3939 self.instruction(LocalGet(local));
3940 self.instruction(I64Const(32));
3941 self.instruction(I64ShrU);
3942 self.instruction(I32WrapI64);
3943 self.instruction(If(BlockType::Empty));
3944 self.trap(Trap::DebugAssertUpperBitsUnset);
3945 self.instruction(End);
3946 }
3947
3948 fn stack_set(&mut self, dst_tys: &[ValType], src_ty: ValType) {
3954 assert_eq!(dst_tys.len(), 1);
3955 let dst_ty = dst_tys[0];
3956 match (src_ty, dst_ty) {
3957 (ValType::I32, ValType::I32)
3958 | (ValType::I64, ValType::I64)
3959 | (ValType::F32, ValType::F32)
3960 | (ValType::F64, ValType::F64) => {}
3961
3962 (ValType::F32, ValType::I32) => self.instruction(I32ReinterpretF32),
3963 (ValType::I32, ValType::I64) => self.instruction(I64ExtendI32U),
3964 (ValType::F64, ValType::I64) => self.instruction(I64ReinterpretF64),
3965 (ValType::F32, ValType::I64) => {
3966 self.instruction(I32ReinterpretF32);
3967 self.instruction(I64ExtendI32U);
3968 }
3969
3970 (ValType::I64, ValType::I32)
3972 | (ValType::F64, ValType::I32)
3973 | (ValType::I32, ValType::F32)
3974 | (ValType::I64, ValType::F32)
3975 | (ValType::F64, ValType::F32)
3976 | (ValType::I32, ValType::F64)
3977 | (ValType::I64, ValType::F64)
3978 | (ValType::F32, ValType::F64)
3979
3980 | (ValType::Ref(_), _)
3982 | (_, ValType::Ref(_))
3983 | (ValType::V128, _)
3984 | (_, ValType::V128) => {
3985 panic!("cannot get {dst_ty:?} from {src_ty:?} local");
3986 }
3987 }
3988 }
3989
3990 fn i32_load8u(&mut self, mem: &Memory) {
3991 self.instruction(LocalGet(mem.addr.idx));
3992 self.instruction(I32Load8U(mem.memarg(0)));
3993 }
3994
3995 fn i32_load8s(&mut self, mem: &Memory) {
3996 self.instruction(LocalGet(mem.addr.idx));
3997 self.instruction(I32Load8S(mem.memarg(0)));
3998 }
3999
4000 fn i32_load16u(&mut self, mem: &Memory) {
4001 self.instruction(LocalGet(mem.addr.idx));
4002 self.instruction(I32Load16U(mem.memarg(1)));
4003 }
4004
4005 fn i32_load16s(&mut self, mem: &Memory) {
4006 self.instruction(LocalGet(mem.addr.idx));
4007 self.instruction(I32Load16S(mem.memarg(1)));
4008 }
4009
4010 fn i32_load(&mut self, mem: &Memory) {
4011 self.instruction(LocalGet(mem.addr.idx));
4012 self.instruction(I32Load(mem.memarg(2)));
4013 }
4014
4015 fn i64_load(&mut self, mem: &Memory) {
4016 self.instruction(LocalGet(mem.addr.idx));
4017 self.instruction(I64Load(mem.memarg(3)));
4018 }
4019
4020 fn ptr_load(&mut self, mem: &Memory) {
4021 if mem.mem_opts().memory64() {
4022 self.i64_load(mem);
4023 } else {
4024 self.i32_load(mem);
4025 }
4026 }
4027
4028 fn ptr_add(&mut self, opts: &LinearMemoryOptions) {
4029 if opts.memory64() {
4030 self.instruction(I64Add);
4031 } else {
4032 self.instruction(I32Add);
4033 }
4034 }
4035
4036 fn ptr_sub(&mut self, opts: &LinearMemoryOptions) {
4037 if opts.memory64() {
4038 self.instruction(I64Sub);
4039 } else {
4040 self.instruction(I32Sub);
4041 }
4042 }
4043
4044 fn ptr_mul(&mut self, opts: &LinearMemoryOptions) {
4045 if opts.memory64() {
4046 self.instruction(I64Mul);
4047 } else {
4048 self.instruction(I32Mul);
4049 }
4050 }
4051
4052 fn ptr_gt_u(&mut self, opts: &LinearMemoryOptions) {
4053 if opts.memory64() {
4054 self.instruction(I64GtU);
4055 } else {
4056 self.instruction(I32GtU);
4057 }
4058 }
4059
4060 fn ptr_lt_u(&mut self, opts: &LinearMemoryOptions) {
4061 if opts.memory64() {
4062 self.instruction(I64LtU);
4063 } else {
4064 self.instruction(I32LtU);
4065 }
4066 }
4067
4068 fn ptr_shl(&mut self, opts: &LinearMemoryOptions) {
4069 if opts.memory64() {
4070 self.instruction(I64Shl);
4071 } else {
4072 self.instruction(I32Shl);
4073 }
4074 }
4075
4076 fn ptr_eqz(&mut self, opts: &LinearMemoryOptions) {
4077 if opts.memory64() {
4078 self.instruction(I64Eqz);
4079 } else {
4080 self.instruction(I32Eqz);
4081 }
4082 }
4083
4084 fn ptr_uconst(&mut self, opts: &LinearMemoryOptions, val: u32) {
4085 if opts.memory64() {
4086 self.instruction(I64Const(val.into()));
4087 } else {
4088 self.instruction(I32Const(val.cast_signed()));
4089 }
4090 }
4091
4092 fn ptr_iconst(&mut self, opts: &LinearMemoryOptions, val: i32) {
4093 if opts.memory64() {
4094 self.instruction(I64Const(val.into()));
4095 } else {
4096 self.instruction(I32Const(val));
4097 }
4098 }
4099
4100 fn ptr_eq(&mut self, opts: &LinearMemoryOptions) {
4101 if opts.memory64() {
4102 self.instruction(I64Eq);
4103 } else {
4104 self.instruction(I32Eq);
4105 }
4106 }
4107
4108 fn ptr_ne(&mut self, opts: &LinearMemoryOptions) {
4109 if opts.memory64() {
4110 self.instruction(I64Ne);
4111 } else {
4112 self.instruction(I32Ne);
4113 }
4114 }
4115
4116 fn ptr_and(&mut self, opts: &LinearMemoryOptions) {
4117 if opts.memory64() {
4118 self.instruction(I64And);
4119 } else {
4120 self.instruction(I32And);
4121 }
4122 }
4123
4124 fn ptr_or(&mut self, opts: &LinearMemoryOptions) {
4125 if opts.memory64() {
4126 self.instruction(I64Or);
4127 } else {
4128 self.instruction(I32Or);
4129 }
4130 }
4131
4132 fn ptr_xor(&mut self, opts: &LinearMemoryOptions) {
4133 if opts.memory64() {
4134 self.instruction(I64Xor);
4135 } else {
4136 self.instruction(I32Xor);
4137 }
4138 }
4139
4140 fn ptr_if(&mut self, opts: &LinearMemoryOptions, ty: BlockType) {
4141 if opts.memory64() {
4142 self.instruction(I64Const(0));
4143 self.instruction(I64Ne);
4144 }
4145 self.instruction(If(ty));
4146 }
4147
4148 fn ptr_br_if(&mut self, opts: &LinearMemoryOptions, depth: u32) {
4149 if opts.memory64() {
4150 self.instruction(I64Const(0));
4151 self.instruction(I64Ne);
4152 }
4153 self.instruction(BrIf(depth));
4154 }
4155
4156 fn f32_load(&mut self, mem: &Memory) {
4157 self.instruction(LocalGet(mem.addr.idx));
4158 self.instruction(F32Load(mem.memarg(2)));
4159 }
4160
4161 fn f64_load(&mut self, mem: &Memory) {
4162 self.instruction(LocalGet(mem.addr.idx));
4163 self.instruction(F64Load(mem.memarg(3)));
4164 }
4165
4166 fn push_dst_addr(&mut self, dst: &Destination) {
4167 if let Destination::Memory(mem) = dst {
4168 self.instruction(LocalGet(mem.addr.idx));
4169 }
4170 }
4171
4172 fn i32_store8(&mut self, mem: &Memory) {
4173 self.instruction(I32Store8(mem.memarg(0)));
4174 }
4175
4176 fn i32_store16(&mut self, mem: &Memory) {
4177 self.instruction(I32Store16(mem.memarg(1)));
4178 }
4179
4180 fn i32_store(&mut self, mem: &Memory) {
4181 self.instruction(I32Store(mem.memarg(2)));
4182 }
4183
4184 fn i64_store(&mut self, mem: &Memory) {
4185 self.instruction(I64Store(mem.memarg(3)));
4186 }
4187
4188 fn ptr_store(&mut self, mem: &Memory) {
4189 if mem.mem_opts().memory64() {
4190 self.i64_store(mem);
4191 } else {
4192 self.i32_store(mem);
4193 }
4194 }
4195
4196 fn f32_store(&mut self, mem: &Memory) {
4197 self.instruction(F32Store(mem.memarg(2)));
4198 }
4199
4200 fn f64_store(&mut self, mem: &Memory) {
4201 self.instruction(F64Store(mem.memarg(3)));
4202 }
4203
4204 fn alloc_size(&mut self, opts: &LinearMemoryOptions, size: &AllocSize) {
4207 match size {
4208 AllocSize::Const(size) => self.ptr_uconst(opts, *size),
4209 AllocSize::Local(idx) => self.instruction(LocalGet(*idx)),
4210 AllocSize::DoubleLocal(idx) => {
4211 self.instruction(LocalGet(*idx));
4212 self.ptr_uconst(opts, 1);
4213 self.ptr_shl(opts);
4214 }
4215 }
4216 }
4217}
4218
4219impl<'a> Source<'a> {
4220 fn record_field_srcs<'b>(
4227 &'b self,
4228 types: &'b ComponentTypesBuilder,
4229 fields: impl IntoIterator<Item = InterfaceType> + 'b,
4230 ) -> impl Iterator<Item = Source<'a>> + 'b
4231 where
4232 'a: 'b,
4233 {
4234 let mut offset = 0;
4235 fields.into_iter().map(move |ty| match self {
4236 Source::Memory(mem) => {
4237 let mem = next_field_offset(&mut offset, types, &ty, mem);
4238 Source::Memory(mem)
4239 }
4240 Source::Stack(stack) => {
4241 let cnt = types.flat_types(&ty).unwrap().len() as u32;
4242 offset += cnt;
4243 Source::Stack(stack.slice((offset - cnt) as usize..offset as usize))
4244 }
4245 Source::Struct(_) => todo!(),
4246 Source::Array(_) => todo!(),
4247 })
4248 }
4249
4250 fn payload_src(
4252 &self,
4253 types: &ComponentTypesBuilder,
4254 info: &VariantInfo,
4255 case: Option<&InterfaceType>,
4256 ) -> Source<'a> {
4257 match self {
4258 Source::Stack(s) => {
4259 let flat_len = match case {
4260 Some(case) => types.flat_types(case).unwrap().len(),
4261 None => 0,
4262 };
4263 Source::Stack(s.slice(1..s.locals.len()).slice(0..flat_len))
4264 }
4265 Source::Memory(mem) => {
4266 let mem = if mem.mem_opts().memory64() {
4267 mem.bump(info.payload_offset64)
4268 } else {
4269 mem.bump(info.payload_offset32)
4270 };
4271 Source::Memory(mem)
4272 }
4273 Source::Struct(_) | Source::Array(_) => todo!("CM+GC"),
4274 }
4275 }
4276
4277 fn opts(&self) -> &'a Options {
4278 match self {
4279 Source::Stack(s) => s.opts,
4280 Source::Memory(mem) => mem.opts,
4281 Source::Struct(s) => s.opts,
4282 Source::Array(a) => a.opts,
4283 }
4284 }
4285}
4286
4287impl<'a> Destination<'a> {
4288 fn record_field_dsts<'b, I>(
4290 &'b self,
4291 types: &'b ComponentTypesBuilder,
4292 fields: I,
4293 ) -> impl Iterator<Item = Destination<'b>> + use<'b, I>
4294 where
4295 'a: 'b,
4296 I: IntoIterator<Item = InterfaceType> + 'b,
4297 {
4298 let mut offset = 0;
4299 fields.into_iter().map(move |ty| match self {
4300 Destination::Memory(mem) => {
4301 let mem = next_field_offset(&mut offset, types, &ty, mem);
4302 Destination::Memory(mem)
4303 }
4304 Destination::Stack(s, opts) => {
4305 let cnt = types.flat_types(&ty).unwrap().len() as u32;
4306 offset += cnt;
4307 Destination::Stack(&s[(offset - cnt) as usize..offset as usize], opts)
4308 }
4309 Destination::Struct(_) => todo!(),
4310 Destination::Array(_) => todo!(),
4311 })
4312 }
4313
4314 fn payload_dst(
4316 &self,
4317 types: &ComponentTypesBuilder,
4318 info: &VariantInfo,
4319 case: Option<&InterfaceType>,
4320 ) -> Destination<'_> {
4321 match self {
4322 Destination::Stack(s, opts) => {
4323 let flat_len = match case {
4324 Some(case) => types.flat_types(case).unwrap().len(),
4325 None => 0,
4326 };
4327 Destination::Stack(&s[1..][..flat_len], opts)
4328 }
4329 Destination::Memory(mem) => {
4330 let mem = if mem.mem_opts().memory64() {
4331 mem.bump(info.payload_offset64)
4332 } else {
4333 mem.bump(info.payload_offset32)
4334 };
4335 Destination::Memory(mem)
4336 }
4337 Destination::Struct(_) | Destination::Array(_) => todo!("CM+GC"),
4338 }
4339 }
4340
4341 fn opts(&self) -> &'a Options {
4342 match self {
4343 Destination::Stack(_, opts) => opts,
4344 Destination::Memory(mem) => mem.opts,
4345 Destination::Struct(s) => s.opts,
4346 Destination::Array(a) => a.opts,
4347 }
4348 }
4349}
4350
4351fn next_field_offset<'a>(
4352 offset: &mut u32,
4353 types: &ComponentTypesBuilder,
4354 field: &InterfaceType,
4355 mem: &Memory<'a>,
4356) -> Memory<'a> {
4357 let abi = types.canonical_abi(field);
4358 let offset = if mem.mem_opts().memory64() {
4359 abi.next_field64(offset)
4360 } else {
4361 abi.next_field32(offset)
4362 };
4363 mem.bump(offset)
4364}
4365
4366impl<'a> Memory<'a> {
4367 fn memarg(&self, align: u32) -> MemArg {
4368 MemArg {
4369 offset: u64::from(self.offset),
4370 align,
4371 memory_index: self.mem_opts().memory.unwrap().0.as_u32(),
4372 }
4373 }
4374
4375 fn bump(&self, offset: u32) -> Memory<'a> {
4376 Memory {
4377 opts: self.opts,
4378 addr: TempLocal::new(self.addr.idx, self.addr.ty),
4379 offset: self.offset + offset,
4380 }
4381 }
4382}
4383
4384impl<'a> Stack<'a> {
4385 fn slice(&self, range: Range<usize>) -> Stack<'a> {
4386 Stack {
4387 locals: &self.locals[range],
4388 opts: self.opts,
4389 }
4390 }
4391}
4392
4393struct VariantCase<'a> {
4394 src_i: u32,
4395 src_ty: Option<&'a InterfaceType>,
4396 dst_i: u32,
4397 dst_ty: Option<&'a InterfaceType>,
4398}
4399
4400fn variant_info<'a, I>(types: &ComponentTypesBuilder, cases: I) -> VariantInfo
4401where
4402 I: IntoIterator<Item = Option<&'a InterfaceType>>,
4403 I::IntoIter: ExactSizeIterator,
4404{
4405 VariantInfo::new(
4406 cases
4407 .into_iter()
4408 .map(|ty| ty.map(|ty| types.canonical_abi(ty))),
4409 )
4410 .0
4411}
4412
4413struct SequenceLoopState {
4415 remaining: TempLocal,
4416 cur_src_ptr: TempLocal,
4417 cur_dst_ptr: TempLocal,
4418}
4419
4420struct SequenceTranslation<'a> {
4424 src_len: TempLocal,
4425 src_mem: Memory<'a>,
4426 dst_mem: Memory<'a>,
4427 src_opts: &'a Options,
4428 dst_opts: &'a Options,
4429 src_mem_opts: &'a LinearMemoryOptions,
4430 dst_mem_opts: &'a LinearMemoryOptions,
4431 loop_state: Option<SequenceLoopState>,
4432}
4433
4434enum AllocSize {
4435 Const(u32),
4436 Local(u32),
4437 DoubleLocal(u32),
4438}
4439
4440struct WasmString<'a> {
4441 ptr: TempLocal,
4442 len: TempLocal,
4443 opts: &'a Options,
4444}
4445
4446struct TempLocal {
4447 idx: u32,
4448 ty: ValType,
4449 needs_free: bool,
4450}
4451
4452impl TempLocal {
4453 fn new(idx: u32, ty: ValType) -> TempLocal {
4454 TempLocal {
4455 idx,
4456 ty,
4457 needs_free: false,
4458 }
4459 }
4460}
4461
4462impl std::ops::Drop for TempLocal {
4463 fn drop(&mut self) {
4464 if self.needs_free {
4465 panic!("temporary local not free'd");
4466 }
4467 }
4468}
4469
4470impl From<FlatType> for ValType {
4471 fn from(ty: FlatType) -> ValType {
4472 match ty {
4473 FlatType::I32 => ValType::I32,
4474 FlatType::I64 => ValType::I64,
4475 FlatType::F32 => ValType::F32,
4476 FlatType::F64 => ValType::F64,
4477 }
4478 }
4479}