1use crate::{
2 Result,
3 abi::{ABIOperand, ABISig, RetArea, vmctx},
4 bail,
5 codegen::BlockSig,
6 ensure, format_err,
7 isa::reg::{Reg, RegClass, writable},
8 masm::{
9 AtomicWaitKind, Extend, Imm, IntCmpKind, IntScratch, LaneSelector, LoadKind,
10 MacroAssembler, OperandSize, RegImm, RmwOp, SPOffset, ShiftKind, StoreKind, TrapCode,
11 UNTRUSTED_FLAGS, Zero,
12 },
13 stack::{TypedReg, Val},
14};
15use cranelift_codegen::{
16 binemit::CodeOffset,
17 ir::{RelSourceLoc, SourceLoc},
18};
19use smallvec::SmallVec;
20use std::marker::PhantomData;
21use wasmparser::{
22 BinaryReader, FuncValidator, MemArg, Operator, OperatorsReader, ValidatorResources,
23 VisitOperator, VisitSimdOperator,
24};
25use wasmtime_cranelift::{TRAP_BAD_SIGNATURE, TRAP_HEAP_MISALIGNED, TRAP_TABLE_OUT_OF_BOUNDS};
26use wasmtime_environ::{
27 DataIndex, ElemIndex, FUNCREF_INIT_BIT, FUNCREF_MASK, GlobalIndex, IndexType, MemoryIndex,
28 MemoryKind, MemoryTunables, PtrSize, TableIndex, Tunables, TypeIndex, WasmHeapType,
29 WasmValType,
30};
31
32mod context;
33pub(crate) use context::*;
34mod env;
35pub use env::*;
36mod call;
37pub(crate) use call::*;
38mod control;
39pub(crate) use control::*;
40mod builtin;
41pub use builtin::*;
42pub(crate) mod bounds;
43
44use bounds::{Bounds, ImmOffset, Index};
45
46mod phase;
47pub(crate) use phase::*;
48
49mod error;
50pub(crate) use error::*;
51
52pub(crate) trait BranchState {
55 fn unreachable_state_after_emission() -> bool;
58}
59
60pub(crate) struct ConditionalBranch;
62
63impl BranchState for ConditionalBranch {
64 fn unreachable_state_after_emission() -> bool {
65 false
66 }
67}
68
69pub(crate) struct UnconditionalBranch;
71
72impl BranchState for UnconditionalBranch {
73 fn unreachable_state_after_emission() -> bool {
74 true
75 }
76}
77
78#[derive(Default)]
82pub(crate) struct SourceLocation {
83 pub base: Option<SourceLoc>,
85 pub current: (CodeOffset, RelSourceLoc),
88}
89
90pub(crate) struct CodeGen<'a, 'translation: 'a, 'data: 'translation, M, P>
92where
93 M: MacroAssembler,
94 P: CodeGenPhase,
95{
96 pub sig: ABISig,
98
99 pub context: CodeGenContext<'a, P>,
101
102 pub env: FuncEnv<'a, 'translation, 'data, M::Ptr>,
104
105 pub masm: &'a mut M,
107
108 pub control_frames: SmallVec<[ControlStackFrame; 64]>,
112
113 pub source_location: SourceLocation,
115
116 pub tunables: &'a Tunables,
118
119 pub fuel_consumed: i64,
121 phase: PhantomData<P>,
122}
123
124impl<'a, 'translation, 'data, M> CodeGen<'a, 'translation, 'data, M, Prologue>
125where
126 M: MacroAssembler,
127{
128 pub fn new(
129 tunables: &'a Tunables,
130 masm: &'a mut M,
131 context: CodeGenContext<'a, Prologue>,
132 env: FuncEnv<'a, 'translation, 'data, M::Ptr>,
133 sig: ABISig,
134 ) -> CodeGen<'a, 'translation, 'data, M, Prologue> {
135 Self {
136 sig,
137 context,
138 masm,
139 env,
140 tunables,
141 source_location: Default::default(),
142 control_frames: Default::default(),
143 fuel_consumed: 1,
145 phase: PhantomData,
146 }
147 }
148
149 pub fn emit_prologue(mut self) -> Result<CodeGen<'a, 'translation, 'data, M, Emission>> {
151 let vmctx = self
152 .sig
153 .params()
154 .first()
155 .ok_or_else(|| format_err!(CodeGenError::vmcontext_arg_expected()))?
156 .unwrap_reg();
157
158 self.masm.start_source_loc(Default::default())?;
159 self.masm.prologue(vmctx)?;
161
162 self.masm.mov(
164 writable!(vmctx!(M)),
165 vmctx.into(),
166 self.env.ptr_type().try_into()?,
167 )?;
168
169 self.masm.reserve_stack(self.context.frame.locals_size)?;
170 self.spill_register_arguments()?;
171
172 let defined_locals_range = &self.context.frame.defined_locals_range;
173 self.masm.zero_mem_range(defined_locals_range.as_range())?;
174
175 if self.sig.params.has_retptr() {
178 match self.sig.params.unwrap_results_area_operand() {
179 ABIOperand::Reg { ty, reg, .. } => {
180 let results_base_slot = self.context.frame.results_base_slot.as_ref().unwrap();
181 ensure!(
182 results_base_slot.addressed_from_sp(),
183 CodeGenError::sp_addressing_expected(),
184 );
185 let addr = self.masm.local_address(results_base_slot)?;
186 self.masm.store((*reg).into(), addr, (*ty).try_into()?)?;
187 }
188 _ => {}
191 }
192 }
193
194 self.masm.end_source_loc()?;
195
196 Ok(CodeGen {
197 sig: self.sig,
198 context: self.context.for_emission(),
199 masm: self.masm,
200 env: self.env,
201 tunables: self.tunables,
202 source_location: self.source_location,
203 control_frames: self.control_frames,
204 fuel_consumed: self.fuel_consumed,
205 phase: PhantomData,
206 })
207 }
208
209 fn spill_register_arguments(&mut self) -> Result<()> {
210 use WasmValType::*;
211 for (operand, slot) in self
212 .sig
213 .params_without_retptr()
214 .iter()
215 .zip(self.context.frame.locals())
216 {
217 match (operand, slot) {
218 (ABIOperand::Reg { ty, reg, .. }, slot) => {
219 let addr = self.masm.local_address(slot)?;
220 match &ty {
221 I32 | I64 | F32 | F64 | V128 => {
222 self.masm.store((*reg).into(), addr, (*ty).try_into()?)?;
223 }
224 Ref(rt) => match rt.heap_type {
225 WasmHeapType::Func | WasmHeapType::Extern => {
226 self.masm.store_ptr(*reg, addr)?;
227 }
228 _ => bail!(CodeGenError::unsupported_wasm_type()),
229 },
230 }
231 }
232 _ => {}
234 }
235 }
236 Ok(())
237 }
238}
239
240impl<'a, 'translation, 'data, M> CodeGen<'a, 'translation, 'data, M, Emission>
241where
242 M: MacroAssembler,
243{
244 pub fn emit(
246 &mut self,
247 body: BinaryReader<'a>,
248 validator: &mut FuncValidator<ValidatorResources>,
249 ) -> Result<()> {
250 self.emit_body(body, validator)
251 .and_then(|_| self.emit_end())?;
252
253 Ok(())
254 }
255
256 pub fn pop_control_frame(&mut self) -> Result<ControlStackFrame> {
258 self.control_frames
259 .pop()
260 .ok_or_else(|| format_err!(CodeGenError::control_frame_expected()))
261 }
262
263 pub fn source_loc_from(&mut self, loc: SourceLoc) -> RelSourceLoc {
265 if self.source_location.base.is_none() && !loc.is_default() {
266 self.source_location.base = Some(loc);
267 }
268
269 RelSourceLoc::from_base_offset(self.source_location.base.unwrap_or_default(), loc)
270 }
271
272 pub fn handle_unreachable_else(&mut self) -> Result<()> {
280 let frame = self
281 .control_frames
282 .last_mut()
283 .ok_or_else(|| CodeGenError::control_frame_expected())?;
284 ensure!(frame.is_if(), CodeGenError::if_control_frame_expected());
285 if frame.is_next_sequence_reachable() {
286 self.context.reachable = true;
290 frame.ensure_stack_state(self.masm, &mut self.context)?;
291 frame.bind_else(self.masm, &mut self.context)?;
292 }
293 Ok(())
294 }
295
296 pub fn handle_unreachable_end(&mut self) -> Result<()> {
297 let mut frame = self.pop_control_frame()?;
298 let is_outermost = self.control_frames.len() == 0;
300
301 if frame.is_next_sequence_reachable() {
302 self.context.reachable = true;
303 frame.ensure_stack_state(self.masm, &mut self.context)?;
304 frame.bind_end(self.masm, &mut self.context)
305 } else if is_outermost {
306 frame.ensure_stack_state(self.masm, &mut self.context)
311 } else {
312 Ok(())
313 }
314 }
315
316 fn emit_body(
317 &mut self,
318 body: BinaryReader<'a>,
319 validator: &mut FuncValidator<ValidatorResources>,
320 ) -> Result<()> {
321 self.maybe_emit_fuel_check()?;
322
323 self.maybe_emit_epoch_check()?;
324
325 self.control_frames.push(ControlStackFrame::block(
328 BlockSig::from_sig(self.sig.clone()),
329 self.masm,
330 &mut self.context,
331 )?);
332
333 if self.sig.params.has_retptr() {
338 self.sig
339 .results
340 .set_ret_area(RetArea::slot(self.context.frame.results_base_slot.unwrap()));
341 }
342
343 let mut ops = OperatorsReader::new(body);
344 while !ops.eof() {
345 let offset = ops.original_position();
346 ops.visit_operator(&mut ValidateThenVisit(
347 validator.simd_visitor(offset),
348 self,
349 offset,
350 ))??;
351 }
352 ops.finish()?;
353 return Ok(());
354
355 struct ValidateThenVisit<'a, T, U>(T, &'a mut U, usize);
356
357 macro_rules! validate_then_visit {
358 ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $ann:tt)*) => {
359 $(
360 fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
361 self.0.$visit($($($arg.clone()),*)?)?;
362 let op = Operator::$op $({ $($arg: $arg.clone()),* })?;
363 if self.1.visit(&op) {
364 self.1.before_visit_op(&op, self.2)?;
365 let res = self.1.$visit($($($arg),*)?)?;
366 self.1.after_visit_op()?;
367 Ok(res)
368 } else {
369 Ok(())
370 }
371 }
372 )*
373 };
374 }
375
376 fn visit_op_when_unreachable(op: &Operator) -> bool {
377 use Operator::*;
378 match op {
379 If { .. } | Block { .. } | Loop { .. } | Else | End => true,
380 _ => false,
381 }
382 }
383
384 trait VisitorHooks {
387 fn before_visit_op(&mut self, operator: &Operator, offset: usize) -> Result<()>;
389 fn after_visit_op(&mut self) -> Result<()>;
391
392 fn visit(&self, op: &Operator) -> bool;
401 }
402
403 impl<'a, 'translation, 'data, M: MacroAssembler> VisitorHooks
404 for CodeGen<'a, 'translation, 'data, M, Emission>
405 {
406 fn visit(&self, op: &Operator) -> bool {
407 self.context.reachable || visit_op_when_unreachable(op)
408 }
409
410 fn before_visit_op(&mut self, operator: &Operator, offset: usize) -> Result<()> {
411 self.source_location_before_visit_op(offset)?;
413
414 if self.tunables.consume_fuel {
416 self.fuel_before_visit_op(operator)?;
417 }
418 Ok(())
419 }
420
421 fn after_visit_op(&mut self) -> Result<()> {
422 self.source_location_after_visit_op()
424 }
425 }
426
427 impl<'a, T, U> VisitOperator<'a> for ValidateThenVisit<'_, T, U>
428 where
429 T: VisitSimdOperator<'a, Output = wasmparser::Result<()>>,
430 U: VisitSimdOperator<'a, Output = Result<()>> + VisitorHooks,
431 {
432 type Output = U::Output;
433
434 fn simd_visitor(
435 &mut self,
436 ) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>>
437 where
438 T:,
439 {
440 Some(self)
441 }
442
443 wasmparser::for_each_visit_operator!(validate_then_visit);
444 }
445
446 impl<'a, T, U> VisitSimdOperator<'a> for ValidateThenVisit<'_, T, U>
447 where
448 T: VisitSimdOperator<'a, Output = wasmparser::Result<()>>,
449 U: VisitSimdOperator<'a, Output = Result<()>> + VisitorHooks,
450 {
451 wasmparser::for_each_visit_simd_operator!(validate_then_visit);
452 }
453 }
454
455 pub fn emit_typecheck_funcref(
457 &mut self,
458 funcref_ptr: Reg,
459 type_index: TypeIndex,
460 ) -> Result<()> {
461 let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
462 let sig_index_bytes = self.env.vmoffsets.size_of_vmshared_type_index();
463 let sig_size = OperandSize::from_bytes(sig_index_bytes);
464 let sig_index = self.env.translation.module.types[type_index].unwrap_module_type_index();
465 let sig_offset = sig_index
466 .as_u32()
467 .checked_mul(sig_index_bytes.into())
468 .unwrap();
469 let signatures_base_offset = self.env.vmoffsets.ptr.vmctx_type_ids_array();
470 let funcref_sig_offset = self.env.vmoffsets.ptr.vm_func_ref_type_index();
471 let caller_id = self.context.any_gpr(self.masm)?;
473
474 self.masm.with_scratch::<IntScratch, _>(|masm, scratch| {
475 masm.load(
477 masm.address_at_vmctx(signatures_base_offset.into())?,
478 scratch.writable(),
479 ptr_size,
480 )?;
481
482 masm.load(
483 masm.address_at_reg(scratch.inner(), sig_offset)?,
484 writable!(caller_id),
485 sig_size,
486 )
487 })?;
488
489 let callee_id = self.context.any_gpr(self.masm)?;
490 self.masm.load(
491 self.masm
492 .address_at_reg(funcref_ptr, funcref_sig_offset.into())?,
493 writable!(callee_id),
494 sig_size,
495 )?;
496
497 self.masm
499 .cmp(caller_id, callee_id.into(), OperandSize::S32)?;
500 self.masm.trapif(IntCmpKind::Ne, TRAP_BAD_SIGNATURE)?;
501 self.context.free_reg(callee_id);
502 self.context.free_reg(caller_id);
503 wasmtime_environ::error::Ok(())
504 }
505
506 fn emit_end(&mut self) -> Result<()> {
508 let base = SPOffset::from_u32(self.context.frame.locals_size);
512 self.masm.start_source_loc(Default::default())?;
513 if self.context.reachable {
514 ControlStackFrame::pop_abi_results_impl(
515 &mut self.sig.results,
516 &mut self.context,
517 self.masm,
518 |results, _, _| Ok(results.ret_area().copied()),
519 )?;
520 } else {
521 self.context.truncate_stack_to(0)?;
525 self.masm.reset_stack_pointer(base)?;
526 }
527 ensure!(
528 self.context.stack.len() == 0,
529 CodeGenError::unexpected_value_in_value_stack()
530 );
531 self.masm.free_stack(self.context.frame.locals_size)?;
532 self.masm.epilogue()?;
533 self.masm.end_source_loc()?;
534 Ok(())
535 }
536
537 pub fn emit_set_local(&mut self, index: u32) -> Result<TypedReg> {
541 if self.context.stack.contains_latent_local(index) {
544 self.context.spill(self.masm)?;
545 }
546 let src = self.context.pop_to_reg(self.masm, None)?;
547 let (ty, addr) = self.context.frame.get_local_address(index, self.masm)?;
551 self.masm
552 .store(RegImm::reg(src.reg), addr, ty.try_into()?)?;
553
554 Ok(src)
555 }
556
557 pub fn emit_get_global_addr(&mut self, index: GlobalIndex) -> Result<(WasmValType, Reg, u32)> {
559 let data = self.env.resolve_global(index);
560
561 if data.imported {
562 let global_base = self.masm.address_at_reg(vmctx!(M), data.offset)?;
563 let dst = self.context.any_gpr(self.masm)?;
564 self.masm.load_ptr(global_base, writable!(dst))?;
565 Ok((data.ty, dst, 0))
566 } else {
567 Ok((data.ty, vmctx!(M), data.offset))
568 }
569 }
570
571 pub fn emit_table_get(&mut self, table_index: TableIndex) -> Result<()> {
572 let table = self.env.table(table_index);
573 let heap_type = table.ref_type.heap_type;
574 ensure!(
575 heap_type == WasmHeapType::Func,
576 CodeGenError::unsupported_wasm_type()
577 );
578 ensure!(
579 self.tunables.table_lazy_init,
580 CodeGenError::unsupported_table_eager_init()
581 );
582 let table_data = self.env.resolve_table_data(table_index);
583 let ptr_type = self.env.ptr_type();
584 let builtin = self.env.builtins.table_get_lazy_init_func_ref::<M::ABI>()?;
585
586 self.context.spill(self.masm)?;
591 let elem_value: Reg = self.context.reg(
592 builtin.sig().results.unwrap_singleton().unwrap_reg(),
593 self.masm,
594 )?;
595
596 let index = self.context.pop_to_reg(self.masm, None)?;
597 let base = self.context.any_gpr(self.masm)?;
598
599 let elem_addr = self.emit_compute_table_elem_addr(index.into(), base, &table_data)?;
600 self.masm.load_ptr(elem_addr, writable!(elem_value))?;
601 self.context.free_reg(base);
604
605 let (defined, cont) = (self.masm.get_label()?, self.masm.get_label()?);
606
607 self.context
609 .stack
610 .extend([table_index.as_u32().try_into().unwrap(), index.into()]);
611
612 self.masm.branch(
613 IntCmpKind::Ne,
614 elem_value,
615 elem_value.into(),
616 defined,
617 ptr_type.try_into()?,
618 )?;
619 self.context.free_reg(elem_value);
623 FnCall::emit::<M>(
624 &mut self.env,
625 self.masm,
626 &mut self.context,
627 Callee::Builtin(builtin.clone()),
628 )?;
629
630 let top = self
633 .context
634 .stack
635 .peek()
636 .ok_or_else(|| CodeGenError::missing_values_in_stack())?;
637 let top = top.unwrap_reg();
638 ensure!(
639 top.reg == elem_value,
640 CodeGenError::table_element_value_expected()
641 );
642 self.masm.jmp(cont)?;
643
644 self.masm.bind(defined)?;
651 assert_eq!(FUNCREF_MASK as isize, -2);
652 let imm = RegImm::i64(-2);
653 let dst = top.into();
654 self.masm
655 .and(writable!(dst), dst, imm, top.ty.try_into()?)?;
656
657 self.masm.bind(cont)
658 }
659
660 pub fn emit_table_set(&mut self, table_index: TableIndex) -> Result<()> {
665 let table = self.env.table(table_index);
666 ensure!(
667 table.ref_type.heap_type == WasmHeapType::Func,
668 CodeGenError::unsupported_wasm_type()
669 );
670 ensure!(
671 self.tunables.table_lazy_init,
672 CodeGenError::unsupported_table_eager_init()
673 );
674 let ptr_type = self.env.ptr_type();
675 let table_data = self.env.resolve_table_data(table_index);
676 let value = self.context.pop_to_reg(self.masm, None)?;
677 let index = self.context.pop_to_reg(self.masm, None)?;
678 let base = self.context.any_gpr(self.masm)?;
679 let elem_addr = self.emit_compute_table_elem_addr(index.into(), base, &table_data)?;
680 self.masm.or(
682 writable!(value.into()),
683 value.into(),
684 RegImm::i64(FUNCREF_INIT_BIT as i64),
685 ptr_type.try_into()?,
686 )?;
687
688 self.masm.store_ptr(value.into(), elem_addr)?;
689
690 self.context.free_reg(value);
691 self.context.free_reg(index);
692 self.context.free_reg(base);
693 Ok(())
694 }
695
696 pub fn emit_table_grow(&mut self, table_index: TableIndex) -> Result<()> {
698 let ptr_type = self.env.ptr_type();
699 let idx_type = self.env.table(table_index).idx_type;
700
701 let delta = self.context.pop_to_reg(self.masm, None)?;
704 let tmp = self.context.any_gpr(self.masm)?;
705 self.masm
706 .mov(writable!(tmp), delta.reg.into(), delta.ty.try_into()?)?;
707 self.context.stack.push(TypedReg::new(delta.ty, tmp).into());
708 self.context.stack.push(delta.into());
709
710 let at = self.context.stack.ensure_index_at(1)?;
713 let builtin = self.env.builtins.table_grow::<M::ABI>()?;
714 let builtin = self.prepare_builtin_defined_table_arg(table_index, at, builtin)?;
715 FnCall::emit::<M>(&mut self.env, self.masm, &mut self.context, builtin)?;
716
717 let result = self.context.pop_to_reg(self.masm, None)?;
721 let len = self.context.pop_to_reg(self.masm, None)?;
722 let init = self.context.pop_to_reg(self.masm, None)?;
723
724 let tmp_result = self.context.any_gpr(self.masm)?;
727 self.masm.mov(
728 writable!(tmp_result),
729 result.reg.into(),
730 result.ty.try_into()?,
731 )?;
732 self.context
733 .stack
734 .push(TypedReg::new(result.ty, tmp_result).into());
735
736 let done = self.masm.get_label()?;
739 self.masm.branch(
740 IntCmpKind::Eq,
741 result.reg,
742 RegImm::i64(-1),
743 done,
744 OperandSize::S64,
745 )?;
746
747 self.context.stack.push(result.into());
750 self.context.stack.push(init.into());
751 self.context.stack.push(len.into());
752 self.emit_table_fill(table_index)?;
753
754 self.masm.bind(done)?;
755
756 match (ptr_type, idx_type) {
760 (WasmValType::I64, IndexType::I64) => Ok(()),
761 (WasmValType::I64, IndexType::I32) => {
762 let top: Reg = self.context.pop_to_reg(self.masm, None)?.into();
763 self.masm.wrap(writable!(top), top)?;
764 self.context.stack.push(TypedReg::i32(top).into());
765 Ok(())
766 }
767
768 _ => Err(format_err!(CodeGenError::unsupported_32_bit_platform())),
769 }
770 }
771
772 pub fn emit_table_fill(&mut self, table_index: TableIndex) -> Result<()> {
774 let len = self.context.pop_to_reg(self.masm, None)?;
776 let init = self.context.pop_to_reg(self.masm, None)?;
777 let offset = self.context.pop_to_reg(self.masm, None)?;
778
779 let table_data = self.env.resolve_table_data(table_index);
781 self.emit_compute_table_size(&table_data)?;
782 let table_size = self.context.pop_to_reg(self.masm, None)?;
783 let tmp = self.context.any_gpr(self.masm)?;
784 let idx_size = table_data.index_type().try_into()?;
785 self.masm.mov(writable!(tmp), offset.reg.into(), idx_size)?;
786 self.masm.checked_uadd(
787 writable!(tmp),
788 tmp,
789 len.reg.into(),
790 idx_size,
791 TRAP_TABLE_OUT_OF_BOUNDS,
792 )?;
793 self.masm.cmp(tmp, table_size.reg.into(), idx_size)?;
794 self.masm
795 .trapif(IntCmpKind::GtU, TRAP_TABLE_OUT_OF_BOUNDS)?;
796 self.context.free_reg(tmp);
797 self.context.free_reg(table_size);
798
799 let header = self.masm.get_label()?;
800 let exit = self.masm.get_label()?;
801
802 self.masm.bind(header)?;
803
804 self.masm.branch(
806 IntCmpKind::Eq,
807 len.reg,
808 RegImm::i64(0),
809 exit,
810 OperandSize::S64,
811 )?;
812
813 let tmp_index = self.context.any_gpr(self.masm)?;
817 let tmp_init = self.context.any_gpr(self.masm)?;
818 self.masm
819 .mov(writable!(tmp_index), offset.reg.into(), OperandSize::S64)?;
820 self.masm
821 .mov(writable!(tmp_init), init.reg.into(), OperandSize::S64)?;
822
823 self.context.stack.push(TypedReg::i64(len.reg).into());
825 self.context.stack.push(TypedReg::i64(init.reg).into());
826 self.context.stack.push(TypedReg::i64(offset.reg).into());
827
828 self.context.stack.push(TypedReg::i64(tmp_index).into());
830 self.context.stack.push(TypedReg::i64(tmp_init).into());
831 self.emit_table_set(table_index)?;
832
833 self.context.pop_to_reg(self.masm, Some(offset.reg))?;
836 self.context.pop_to_reg(self.masm, Some(init.reg))?;
837 self.context.pop_to_reg(self.masm, Some(len.reg))?;
838
839 self.masm.add(
842 writable!(offset.reg),
843 offset.reg,
844 RegImm::i64(1),
845 OperandSize::S64,
846 )?;
847 self.masm.sub(
848 writable!(len.reg),
849 len.reg,
850 RegImm::i64(1),
851 OperandSize::S64,
852 )?;
853 self.masm.jmp(header)?;
854
855 self.masm.bind(exit)?;
856
857 self.context.free_reg(offset);
858 self.context.free_reg(init);
859 self.context.free_reg(len);
860 Ok(())
861 }
862
863 fn emit_table_range_bounds_check(
869 &mut self,
870 table_data: &TableData,
871 idx: Reg,
872 len: Reg,
873 ) -> Result<()> {
874 self.emit_compute_table_size(table_data)?;
875 let size = self.context.pop_to_reg(self.masm, None)?;
876
877 let end = self.context.any_gpr(self.masm)?;
880 self.masm
881 .mov(writable!(end), idx.into(), OperandSize::S64)?;
882 self.masm.checked_uadd(
883 writable!(end),
884 end,
885 len.into(),
886 OperandSize::S64,
887 TRAP_TABLE_OUT_OF_BOUNDS,
888 )?;
889 self.masm.cmp(end, size.reg.into(), OperandSize::S64)?;
890 self.masm
891 .trapif(IntCmpKind::GtU, TRAP_TABLE_OUT_OF_BOUNDS)?;
892
893 self.context.free_reg(size);
894 self.context.free_reg(end);
895 Ok(())
896 }
897
898 pub fn emit_table_copy(&mut self, dst_table: TableIndex, src_table: TableIndex) -> Result<()> {
900 let dst_data = self.env.resolve_table_data(dst_table);
901 let src_data = self.env.resolve_table_data(src_table);
902
903 let len = self.context.pop_to_reg(self.masm, None)?;
905 let src = self.context.pop_to_reg(self.masm, None)?;
906 let dst = self.context.pop_to_reg(self.masm, None)?;
907
908 for op in [&len, &src, &dst] {
912 if op.ty == WasmValType::I32 {
913 self.masm.extend(
914 writable!(op.reg),
915 op.reg,
916 Extend::<Zero>::I64Extend32.into(),
917 )?;
918 }
919 }
920
921 self.emit_table_range_bounds_check(&src_data, src.reg, len.reg)?;
924 self.emit_table_range_bounds_check(&dst_data, dst.reg, len.reg)?;
925
926 let step = self.context.any_gpr(self.masm)?;
929 let forward = self.masm.get_label()?;
930 let setup_done = self.masm.get_label()?;
931 self.masm.branch(
932 IntCmpKind::LeU,
933 dst.reg,
934 src.reg.into(),
935 forward,
936 OperandSize::S64,
937 )?;
938 {
940 self.masm
941 .mov(writable!(step), RegImm::i64(-1), OperandSize::S64)?;
942 self.masm.add(
943 writable!(src.reg),
944 src.reg,
945 len.reg.into(),
946 OperandSize::S64,
947 )?;
948 self.masm.sub(
949 writable!(src.reg),
950 src.reg,
951 RegImm::i64(1),
952 OperandSize::S64,
953 )?;
954 self.masm.add(
955 writable!(dst.reg),
956 dst.reg,
957 len.reg.into(),
958 OperandSize::S64,
959 )?;
960 self.masm.sub(
961 writable!(dst.reg),
962 dst.reg,
963 RegImm::i64(1),
964 OperandSize::S64,
965 )?;
966 }
967 self.masm.jmp(setup_done)?;
968 self.masm.bind(forward)?;
970 {
971 self.masm
972 .mov(writable!(step), RegImm::i64(1), OperandSize::S64)?;
973 }
974
975 self.masm.bind(setup_done)?;
976
977 let header = self.masm.get_label()?;
978 let exit = self.masm.get_label()?;
979
980 self.masm.bind(header)?;
981
982 self.masm.branch(
984 IntCmpKind::Eq,
985 len.reg,
986 RegImm::i64(0),
987 exit,
988 OperandSize::S64,
989 )?;
990
991 self.context.stack.push(TypedReg::i64(step).into());
995 self.context.stack.push(TypedReg::i64(len.reg).into());
996 self.context.stack.push(TypedReg::i64(dst.reg).into());
997 self.context.stack.push(TypedReg::i64(src.reg).into());
998
999 {
1008 let tmp_src = self.context.pop_to_reg(self.masm, None)?;
1009 let s = self.context.any_gpr(self.masm)?;
1010 self.masm
1011 .mov(writable!(s), tmp_src.reg.into(), OperandSize::S64)?;
1012 self.context.stack.push(tmp_src.into());
1013 self.context.stack.push(TypedReg::i64(s).into());
1014 self.emit_table_get(src_table)?;
1015 let funcref = self.context.pop_to_reg(self.masm, None)?;
1016
1017 let tmp_src = self.context.pop_to_reg(self.masm, None)?;
1018 let tmp_dst = self.context.pop_to_reg(self.masm, None)?;
1019
1020 let d = self.context.any_gpr(self.masm)?;
1021 self.masm
1022 .mov(writable!(d), tmp_dst.reg.into(), OperandSize::S64)?;
1023 self.context.stack.push(tmp_dst.into());
1024 self.context.stack.push(tmp_src.into());
1025 self.context.stack.push(TypedReg::i64(d).into());
1026 self.context.stack.push(funcref.into());
1027 self.emit_table_set(dst_table)?;
1028 }
1029
1030 self.context.pop_to_reg(self.masm, Some(src.reg))?;
1033 self.context.pop_to_reg(self.masm, Some(dst.reg))?;
1034 self.context.pop_to_reg(self.masm, Some(len.reg))?;
1035 self.context.pop_to_reg(self.masm, Some(step))?;
1036
1037 self.masm
1039 .add(writable!(dst.reg), dst.reg, step.into(), OperandSize::S64)?;
1040 self.masm
1041 .add(writable!(src.reg), src.reg, step.into(), OperandSize::S64)?;
1042 self.masm.sub(
1043 writable!(len.reg),
1044 len.reg,
1045 RegImm::i64(1),
1046 OperandSize::S64,
1047 )?;
1048
1049 self.masm.jmp(header)?;
1050
1051 self.masm.bind(exit)?;
1052
1053 self.context.free_reg(src);
1054 self.context.free_reg(dst);
1055 self.context.free_reg(len);
1056 self.context.free_reg(step);
1057 Ok(())
1058 }
1059
1060 pub fn emit_compute_heap_address(
1081 &mut self,
1082 heap: &HeapData,
1083 memarg: &MemArg,
1084 access_size: OperandSize,
1085 ) -> Result<Option<Reg>> {
1086 let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
1087 let enable_spectre_mitigation = self.env.heap_access_spectre_mitigation();
1088 let add_offset_and_access_size = |offset: ImmOffset, access_size: OperandSize| {
1089 (access_size.bytes() as u64) + (offset.as_u32() as u64)
1090 };
1091
1092 let index = Index::from_typed_reg(self.context.pop_to_reg(self.masm, None)?);
1093
1094 let offset = bounds::ensure_index_and_offset(
1095 self.masm,
1096 index,
1097 memarg.offset,
1098 heap.index_type().try_into()?,
1099 )?;
1100 let offset_with_access_size = add_offset_and_access_size(offset, access_size);
1101
1102 let memory_tunables = MemoryTunables::new(self.tunables, MemoryKind::LinearMemory);
1103 let can_elide_bounds_check = heap
1104 .memory
1105 .can_elide_bounds_check(&memory_tunables, self.env.page_size_log2);
1106
1107 let addr = if offset_with_access_size > heap.memory.maximum_byte_size().unwrap_or(u64::MAX)
1108 || (!self.tunables.memory_may_move
1109 && offset_with_access_size > self.tunables.memory_reservation)
1110 {
1111 self.emit_fuel_increment()?;
1118 self.masm.trap(TrapCode::HEAP_OUT_OF_BOUNDS)?;
1119 self.context.reachable = false;
1120 None
1121
1122 } else if can_elide_bounds_check
1149 && u64::from(u32::MAX)
1150 <= self.tunables.memory_reservation + self.tunables.memory_guard_size
1151 - offset_with_access_size
1152 {
1153 assert!(can_elide_bounds_check);
1154 assert!(heap.index_type() == WasmValType::I32);
1155 let addr = self.context.any_gpr(self.masm)?;
1156 bounds::load_heap_addr_unchecked(self.masm, &heap, index, offset, addr, ptr_size)?;
1157 Some(addr)
1158
1159 } else if let Some(static_size) = heap.memory.static_heap_size() {
1167 let bounds = Bounds::from_u64(static_size);
1168 let addr = bounds::load_heap_addr_checked(
1169 self.masm,
1170 &mut self.context,
1171 ptr_size,
1172 &heap,
1173 enable_spectre_mitigation,
1174 bounds,
1175 index,
1176 offset,
1177 |masm, bounds, index| {
1178 let adjusted_bounds = bounds.as_u64() - offset_with_access_size;
1179 let index_reg = index.as_typed_reg().reg;
1180 masm.cmp(
1181 index_reg,
1182 RegImm::i64(adjusted_bounds as i64),
1183 ptr_size,
1189 )?;
1190 Ok(IntCmpKind::GtU)
1191 },
1192 )?;
1193 Some(addr)
1194 } else {
1195 let bounds = bounds::load_dynamic_heap_bounds::<_>(
1201 &mut self.context,
1202 self.masm,
1203 &heap,
1204 ptr_size,
1205 )?;
1206
1207 let index_reg = index.as_typed_reg().reg;
1208 let index_offset_and_access_size = self.context.any_gpr(self.masm)?;
1212
1213 self.masm.mov(
1225 writable!(index_offset_and_access_size),
1226 index_reg.into(),
1227 heap.index_type().try_into()?,
1228 )?;
1229 self.masm.checked_uadd(
1240 writable!(index_offset_and_access_size),
1241 index_offset_and_access_size,
1242 RegImm::i64(offset_with_access_size as i64),
1243 ptr_size,
1244 TrapCode::HEAP_OUT_OF_BOUNDS,
1245 )?;
1246
1247 let addr = bounds::load_heap_addr_checked(
1248 self.masm,
1249 &mut self.context,
1250 ptr_size,
1251 &heap,
1252 enable_spectre_mitigation,
1253 bounds,
1254 index,
1255 offset,
1256 |masm, bounds, _| {
1257 let bounds_reg = bounds.as_typed_reg().reg;
1258 masm.cmp(
1259 index_offset_and_access_size,
1260 bounds_reg.into(),
1261 ptr_size,
1265 )?;
1266 Ok(IntCmpKind::GtU)
1267 },
1268 )?;
1269 self.context.free_reg(bounds.as_typed_reg().reg);
1270 self.context.free_reg(index_offset_and_access_size);
1271 Some(addr)
1272 };
1273
1274 self.context.free_reg(index.as_typed_reg().reg);
1275 Ok(addr)
1276 }
1277
1278 fn emit_check_align(
1281 &mut self,
1282 heap: &HeapData,
1283 memarg: &MemArg,
1284 access_size: OperandSize,
1285 ) -> Result<()> {
1286 if access_size.bytes() > 1 {
1287 let heap_ty_size: OperandSize = heap.index_type().try_into()?;
1288 let addr = *self
1289 .context
1290 .stack
1291 .peek()
1292 .ok_or_else(|| CodeGenError::missing_values_in_stack())?;
1293 let tmp = self.context.any_gpr(self.masm)?;
1294 self.context.move_val_to_reg(&addr, tmp, self.masm)?;
1295
1296 if memarg.offset != 0 {
1297 self.masm.add(
1298 writable!(tmp),
1299 tmp,
1300 RegImm::Imm(Imm::I64(memarg.offset)),
1301 heap_ty_size,
1302 )?;
1303 }
1304
1305 self.masm.and(
1306 writable!(tmp),
1307 tmp,
1308 RegImm::Imm(Imm::I32(access_size.bytes() - 1)),
1309 heap_ty_size,
1310 )?;
1311
1312 self.masm.cmp(tmp, RegImm::Imm(Imm::i64(0)), heap_ty_size)?;
1313 self.masm.trapif(IntCmpKind::Ne, TRAP_HEAP_MISALIGNED)?;
1314 self.context.free_reg(tmp);
1315 }
1316
1317 Ok(())
1318 }
1319
1320 pub fn emit_compute_heap_address_align_checked(
1321 &mut self,
1322 heap: &HeapData,
1323 memarg: &MemArg,
1324 access_size: OperandSize,
1325 ) -> Result<Option<Reg>> {
1326 self.emit_check_align(heap, memarg, access_size)?;
1327 self.emit_compute_heap_address(heap, memarg, access_size)
1328 }
1329
1330 pub fn emit_wasm_load(
1332 &mut self,
1333 arg: &MemArg,
1334 target_type: WasmValType,
1335 kind: LoadKind,
1336 ) -> Result<()> {
1337 let emit_load = |this: &mut Self, dst, addr, kind| -> Result<()> {
1338 let src = this.masm.address_at_reg(addr, 0)?;
1339 this.masm.wasm_load(src, writable!(dst), kind)?;
1340 this.context
1341 .stack
1342 .push(TypedReg::new(target_type, dst).into());
1343 this.context.free_reg(addr);
1344 Ok(())
1345 };
1346
1347 let memory_index = MemoryIndex::from_u32(arg.memory);
1348 let heap = self.env.resolve_heap(memory_index);
1349
1350 match kind {
1353 LoadKind::VectorLane(_) => {
1354 let dst = self.context.pop_to_reg(self.masm, None)?;
1358 let addr =
1359 self.emit_compute_heap_address(&heap, &arg, kind.derive_operand_size())?;
1360 if let Some(addr) = addr {
1361 emit_load(self, dst.reg, addr, kind)?;
1362 } else {
1363 self.context.free_reg(dst);
1364 }
1365 }
1366 _ => {
1367 let maybe_addr = match kind {
1368 LoadKind::Atomic(_, _) => self.emit_compute_heap_address_align_checked(
1369 &heap,
1370 &arg,
1371 kind.derive_operand_size(),
1372 )?,
1373 _ => self.emit_compute_heap_address(&heap, &arg, kind.derive_operand_size())?,
1374 };
1375
1376 if let Some(addr) = maybe_addr {
1377 let dst = match target_type {
1378 WasmValType::I32 | WasmValType::I64 => self.context.any_gpr(self.masm)?,
1379 WasmValType::F32 | WasmValType::F64 => self.context.any_fpr(self.masm)?,
1380 WasmValType::V128 => self.context.reg_for_type(target_type, self.masm)?,
1381 _ => bail!(CodeGenError::unsupported_wasm_type()),
1382 };
1383
1384 emit_load(self, dst, addr, kind)?;
1385 }
1386 }
1387 }
1388
1389 Ok(())
1390 }
1391
1392 pub fn emit_wasm_store(&mut self, arg: &MemArg, kind: StoreKind) -> Result<()> {
1394 let memory_index = MemoryIndex::from_u32(arg.memory);
1395 let heap = self.env.resolve_heap(memory_index);
1396 let src = self.context.pop_to_reg(self.masm, None)?;
1397
1398 let maybe_addr = match kind {
1399 StoreKind::Atomic(size) => {
1400 self.emit_compute_heap_address_align_checked(&heap, &arg, size)?
1401 }
1402 StoreKind::Operand(size) | StoreKind::VectorLane(LaneSelector { size, .. }) => {
1403 self.emit_compute_heap_address(&heap, &arg, size)?
1404 }
1405 };
1406
1407 if let Some(addr) = maybe_addr {
1408 self.masm
1409 .wasm_store(src.reg, self.masm.address_at_reg(addr, 0)?, kind)?;
1410
1411 self.context.free_reg(addr);
1412 }
1413 self.context.free_reg(src);
1414
1415 Ok(())
1416 }
1417
1418 pub fn emit_compute_table_elem_addr(
1421 &mut self,
1422 index: Reg,
1423 base: Reg,
1424 table_data: &TableData,
1425 ) -> Result<M::Address> {
1426 let bound = self.context.any_gpr(self.masm)?;
1427 let tmp = self.context.any_gpr(self.masm)?;
1428 let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
1429
1430 if let Some(offset) = table_data.import_from {
1431 self.masm
1435 .load_ptr(self.masm.address_at_vmctx(offset)?, writable!(base))?;
1436 } else {
1437 self.masm.mov(writable!(base), vmctx!(M).into(), ptr_size)?;
1440 };
1441
1442 let bound_addr = self
1444 .masm
1445 .address_at_reg(base, table_data.current_elems_offset)?;
1446 let bound_size = table_data.current_elements_size;
1447 self.masm.load(bound_addr, writable!(bound), bound_size)?;
1448 self.masm.cmp(index, bound.into(), bound_size)?;
1449 self.masm
1450 .trapif(IntCmpKind::GeU, TRAP_TABLE_OUT_OF_BOUNDS)?;
1451
1452 self.masm.with_scratch::<IntScratch, _>(|masm, scratch| {
1457 masm.mov(scratch.writable(), index.into(), bound_size)?;
1458 masm.mul(
1459 scratch.writable(),
1460 scratch.inner(),
1461 RegImm::i32(table_data.element_size.bytes() as i32),
1462 table_data.element_size,
1463 )?;
1464 masm.load_ptr(
1465 masm.address_at_reg(base, table_data.offset)?,
1466 writable!(base),
1467 )?;
1468 masm.mov(writable!(tmp), base.into(), ptr_size)?;
1471 masm.add(writable!(base), base, scratch.inner().into(), ptr_size)
1473 })?;
1474 if self.env.table_access_spectre_mitigation() {
1475 self.masm.cmp(index, bound.into(), bound_size)?;
1478 self.masm
1479 .cmov(writable!(base), tmp, IntCmpKind::GeU, ptr_size)?;
1480 }
1481 self.context.free_reg(bound);
1482 self.context.free_reg(tmp);
1483 self.masm.address_at_reg(base, 0)
1484 }
1485
1486 pub fn emit_compute_table_size(&mut self, table_data: &TableData) -> Result<()> {
1488 let size = self.context.any_gpr(self.masm)?;
1489 let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
1490
1491 self.masm.with_scratch::<IntScratch, _>(|masm, scratch| {
1492 if let Some(offset) = table_data.import_from {
1493 masm.load_ptr(masm.address_at_vmctx(offset)?, scratch.writable())?;
1494 } else {
1495 masm.mov(scratch.writable(), vmctx!(M).into(), ptr_size)?;
1496 };
1497
1498 let size_addr =
1499 masm.address_at_reg(scratch.inner(), table_data.current_elems_offset)?;
1500 masm.load(size_addr, writable!(size), table_data.current_elements_size)
1501 })?;
1502
1503 let dst = TypedReg::new(table_data.index_type(), size);
1504 self.context.stack.push(dst.into());
1505 Ok(())
1506 }
1507
1508 fn load_memory_length(&mut self, heap_data: &HeapData, size_reg: Reg) -> Result<()> {
1510 self.masm.with_scratch::<IntScratch, _>(|masm, scratch| {
1511 let base = if let Some(offset) = heap_data.import_from {
1512 masm.load_ptr(masm.address_at_vmctx(offset)?, scratch.writable())?;
1513 scratch.inner()
1514 } else {
1515 vmctx!(M)
1516 };
1517
1518 let size_addr = masm.address_at_reg(base, heap_data.current_length_offset)?;
1519 masm.load_ptr(size_addr, writable!(size_reg))
1520 })?;
1521 Ok(())
1522 }
1523
1524 pub fn emit_compute_memory_size(&mut self, heap_data: &HeapData) -> Result<()> {
1526 let size_reg = self.context.any_gpr(self.masm)?;
1527 self.load_memory_length(heap_data, size_reg)?;
1528
1529 let dst = TypedReg::new(heap_data.index_type(), size_reg);
1531 let pow = heap_data.memory.page_size_log2;
1532 self.masm.shift_ir(
1533 writable!(dst.reg),
1534 Imm::i32(pow as i32),
1535 dst.into(),
1536 ShiftKind::ShrU,
1537 self.env.ptr_type().try_into()?,
1538 )?;
1539 self.context.stack.push(dst.into());
1540 Ok(())
1541 }
1542
1543 fn emit_bounds_check_and_compute_addr(
1546 &mut self,
1547 heap: &HeapData,
1548 dst: Reg,
1549 ptr: Reg,
1550 len: Reg,
1551 ) -> Result<()> {
1552 let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
1553 let idx_size: OperandSize = heap.index_type().try_into()?;
1554 match idx_size {
1558 OperandSize::S32 => {
1559 self.masm
1560 .extend(writable!(dst), ptr, Extend::<Zero>::I64Extend32.into())?;
1561 self.masm.add_uextend(
1562 writable!(dst),
1563 dst,
1564 len,
1565 OperandSize::S32,
1566 OperandSize::S64,
1567 )?;
1568 }
1569 OperandSize::S64 => {
1570 self.masm
1571 .mov(writable!(dst), ptr.into(), OperandSize::S64)?;
1572 self.masm.checked_uadd(
1573 writable!(dst),
1574 dst,
1575 len.into(),
1576 OperandSize::S64,
1577 TrapCode::HEAP_OUT_OF_BOUNDS,
1578 )?;
1579 }
1580 _ => unreachable!(),
1581 }
1582
1583 let size_in_bytes = self.context.any_gpr(self.masm)?;
1586 self.load_memory_length(&heap, size_in_bytes)?;
1587 assert!(ptr_size == OperandSize::S64);
1588 self.masm.cmp(dst, size_in_bytes.into(), ptr_size)?;
1589 self.masm
1590 .trapif(IntCmpKind::GtU, TrapCode::HEAP_OUT_OF_BOUNDS)?;
1591 self.context.free_reg(size_in_bytes);
1592
1593 bounds::load_heap_addr_unchecked(
1595 self.masm,
1596 &heap,
1597 Index::from_typed_reg(TypedReg::new(heap.index_type(), ptr)),
1598 ImmOffset::from_u32(0),
1599 dst,
1600 ptr_size,
1601 )?;
1602 Ok(())
1603 }
1604
1605 pub fn emit_memory_copy(&mut self, dst_mem: MemoryIndex, src_mem: MemoryIndex) -> Result<()> {
1607 let dst_heap = self.env.resolve_heap(dst_mem);
1608 let src_heap = self.env.resolve_heap(src_mem);
1609 let dst_idx_size: OperandSize = dst_heap.index_type().try_into()?;
1610 let src_idx_size: OperandSize = src_heap.index_type().try_into()?;
1611
1612 let len = self.context.pop_to_reg(self.masm, None)?;
1613 let src = self.context.pop_to_reg(self.masm, None)?;
1614 let dst = self.context.pop_to_reg(self.masm, None)?;
1615
1616 if dst_idx_size == OperandSize::S32 || src_idx_size == OperandSize::S32 {
1622 self.masm.extend(
1623 writable!(len.reg),
1624 len.reg,
1625 Extend::<Zero>::I64Extend32.into(),
1626 )?;
1627 }
1628
1629 let dst_raw_addr = self.context.any_gpr(self.masm)?;
1630 self.emit_bounds_check_and_compute_addr(&dst_heap, dst_raw_addr, dst.reg, len.reg)?;
1631 self.context.free_reg(dst);
1632
1633 let src_raw_addr = self.context.any_gpr(self.masm)?;
1634 self.emit_bounds_check_and_compute_addr(&src_heap, src_raw_addr, src.reg, len.reg)?;
1635 self.context.free_reg(src);
1636
1637 self.context
1638 .stack
1639 .push(TypedReg::new(self.env.ptr_type(), dst_raw_addr).into());
1640 self.context
1641 .stack
1642 .push(TypedReg::new(self.env.ptr_type(), src_raw_addr).into());
1643 self.context
1644 .stack
1645 .push(TypedReg::new(self.env.ptr_type(), len.reg).into());
1646
1647 let builtin = self.env.builtins.memory_copy::<M::ABI>()?;
1648 FnCall::emit::<M>(
1649 &mut self.env,
1650 self.masm,
1651 &mut self.context,
1652 Callee::Builtin(builtin),
1653 )?;
1654 Ok(())
1655 }
1656
1657 pub fn emit_memory_fill(&mut self, mem: MemoryIndex) -> Result<()> {
1659 let heap = self.env.resolve_heap(mem);
1660 let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
1661 let idx_size: OperandSize = heap.index_type().try_into()?;
1662
1663 let len = self.context.pop_to_reg(self.masm, None)?;
1665 let val = self.context.pop_to_reg(self.masm, None)?;
1666 let dst = self.context.pop_to_reg(self.masm, None)?;
1667
1668 let raw_addr = self.context.any_gpr(self.masm)?;
1669 self.emit_bounds_check_and_compute_addr(&heap, raw_addr, dst.reg, len.reg)?;
1670 self.context.free_reg(dst);
1671
1672 let len_reg = len.reg;
1675 if idx_size == OperandSize::S32 && ptr_size == OperandSize::S64 {
1676 self.masm.extend(
1677 writable!(len_reg),
1678 len_reg,
1679 Extend::<Zero>::I64Extend32.into(),
1680 )?;
1681 }
1682
1683 self.context
1685 .stack
1686 .push(TypedReg::new(self.env.ptr_type(), raw_addr).into());
1687 self.context.stack.push(val.into());
1688 self.context
1689 .stack
1690 .push(TypedReg::new(self.env.ptr_type(), len_reg).into());
1691
1692 let builtin = self.env.builtins.memory_fill::<M::ABI>()?;
1693 FnCall::emit::<M>(
1694 &mut self.env,
1695 self.masm,
1696 &mut self.context,
1697 Callee::Builtin(builtin),
1698 )?;
1699 Ok(())
1700 }
1701
1702 pub fn emit_memory_init(&mut self, segment: DataIndex, mem: MemoryIndex) -> Result<()> {
1704 let dst_heap = self.env.resolve_heap(mem);
1705
1706 let len = self.context.pop_to_reg(self.masm, None)?;
1707 let src = self.context.pop_to_reg(self.masm, None)?;
1708 let dst = self.context.pop_to_reg(self.masm, None)?;
1709
1710 self.masm.extend(
1716 writable!(len.reg),
1717 len.reg,
1718 Extend::<Zero>::I64Extend32.into(),
1719 )?;
1720
1721 let dst_raw_addr = self.context.any_gpr(self.masm)?;
1722 self.emit_bounds_check_and_compute_addr(&dst_heap, dst_raw_addr, dst.reg, len.reg)?;
1723 self.context.free_reg(dst);
1724
1725 let runtime_data_index = match self.env.translation.runtime_data_map[segment] {
1726 Some(i) => i,
1727
1728 None => {
1731 self.masm.cmp(src.reg, RegImm::i32(0), OperandSize::S32)?;
1732 self.masm
1733 .trapif(IntCmpKind::Ne, TrapCode::HEAP_OUT_OF_BOUNDS)?;
1734 self.masm.cmp(len.reg, RegImm::i32(0), OperandSize::S32)?;
1735 self.masm
1736 .trapif(IntCmpKind::Ne, TrapCode::HEAP_OUT_OF_BOUNDS)?;
1737 self.context.free_reg(dst_raw_addr);
1738 self.context.free_reg(src);
1739 self.context.free_reg(len);
1740 return Ok(());
1741 }
1742 };
1743
1744 let data_segment_length_offset = self
1748 .env
1749 .vmoffsets
1750 .vmctx_runtime_data_length(runtime_data_index);
1751 let tmp1 = self.context.any_gpr(self.masm)?;
1752 let tmp2 = self.context.any_gpr(self.masm)?;
1753 self.masm.load(
1754 self.masm.address_at_vmctx(data_segment_length_offset)?,
1755 writable!(tmp1),
1756 OperandSize::S32,
1757 )?;
1758 self.masm
1759 .mov(writable!(tmp2), src.reg.into(), OperandSize::S32)?;
1760 self.masm.checked_uadd(
1761 writable!(tmp2),
1762 tmp2,
1763 len.reg.into(),
1764 OperandSize::S32,
1765 TrapCode::HEAP_OUT_OF_BOUNDS,
1766 )?;
1767 self.masm.cmp(tmp2, tmp1.into(), OperandSize::S32)?;
1768 self.masm
1769 .trapif(IntCmpKind::GtU, TrapCode::HEAP_OUT_OF_BOUNDS)?;
1770 self.context.free_reg(tmp2);
1771
1772 let data_segment_base_offset = self
1775 .env
1776 .vmoffsets
1777 .vmctx_runtime_data_base(runtime_data_index);
1778 self.masm.load(
1779 self.masm.address_at_vmctx(data_segment_base_offset)?,
1780 writable!(tmp1),
1781 OperandSize::S64,
1782 )?;
1783 self.masm.add_uextend(
1784 writable!(tmp1),
1785 tmp1,
1786 src.reg,
1787 OperandSize::S32,
1788 OperandSize::S64,
1789 )?;
1790 self.context.free_reg(src);
1791
1792 self.context.stack.push(TypedReg::i64(dst_raw_addr).into());
1794 self.context.stack.push(TypedReg::i64(tmp1).into());
1795 self.context.stack.push(len.into());
1796 let builtin = self.env.builtins.memory_copy::<M::ABI>()?;
1797 FnCall::emit::<M>(
1798 &mut self.env,
1799 self.masm,
1800 &mut self.context,
1801 Callee::Builtin(builtin),
1802 )?;
1803 Ok(())
1804 }
1805
1806 pub fn emit_data_drop(&mut self, data_index: DataIndex) -> Result<()> {
1807 let runtime_data_index = match self.env.translation.runtime_data_map[data_index] {
1808 Some(idx) => idx,
1809 None => return Ok(()),
1811 };
1812 let data_segment_offset = self
1813 .env
1814 .vmoffsets
1815 .vmctx_runtime_data_length(runtime_data_index);
1816 let len_addr = self.masm.address_at_vmctx(data_segment_offset)?;
1817 self.masm.store(RegImm::i32(0), len_addr, OperandSize::S32)
1818 }
1819
1820 pub fn emit_table_init(
1822 &mut self,
1823 elem_index: ElemIndex,
1824 table_index: TableIndex,
1825 ) -> Result<()> {
1826 let builtin_base = self.env.builtins.passive_elem_segment_base::<M::ABI>()?;
1827 let builtin_len = self.env.builtins.passive_elem_segment_len::<M::ABI>()?;
1828
1829 match self.env.translation.passive_elem_map[elem_index] {
1831 Some(idx) => {
1832 self.context.stack.extend([idx.as_u32().try_into()?]);
1833 FnCall::emit::<M>(
1834 &mut self.env,
1835 self.masm,
1836 &mut self.context,
1837 Callee::Builtin(builtin_len),
1838 )?;
1839 self.context.stack.extend([idx.as_u32().try_into()?]);
1840 FnCall::emit::<M>(
1841 &mut self.env,
1842 self.masm,
1843 &mut self.context,
1844 Callee::Builtin(builtin_base),
1845 )?;
1846 }
1847 None => {
1849 let tmp = self.context.any_gpr(self.masm)?;
1850 self.masm
1851 .mov(writable!(tmp), RegImm::i64(0), OperandSize::S64)?;
1852 self.context
1853 .stack
1854 .push(TypedReg::new(WasmValType::I64, tmp).into());
1855
1856 let tmp = self.context.any_gpr(self.masm)?;
1857 self.masm
1858 .mov(writable!(tmp), RegImm::i64(0), OperandSize::S64)?;
1859 self.context
1860 .stack
1861 .push(TypedReg::new(WasmValType::I64, tmp).into());
1862 }
1863 };
1864
1865 let table_data = self.env.resolve_table_data(table_index);
1867 let idx_size = table_data.index_type().try_into()?;
1868 self.emit_compute_table_size(&table_data)?;
1869
1870 let table_size = self.context.pop_to_reg(self.masm, None)?;
1873 let segment_base = self.context.pop_to_reg(self.masm, None)?;
1874 let segment_len = self.context.pop_to_reg(self.masm, None)?;
1875 let len = self.context.pop_to_reg(self.masm, None)?;
1876 let segment_off = self.context.pop_to_reg(self.masm, None)?;
1877 let table_off = self.context.pop_to_reg(self.masm, None)?;
1878
1879 if len.ty == WasmValType::I32 {
1882 self.masm.extend(
1883 writable!(len.reg),
1884 len.reg,
1885 Extend::<Zero>::I64Extend32.into(),
1886 )?;
1887 }
1888
1889 let tmp = self.context.any_gpr(self.masm)?;
1891 {
1892 self.masm
1893 .mov(writable!(tmp), segment_off.reg.into(), OperandSize::S32)?;
1894 self.masm.checked_uadd(
1895 writable!(tmp),
1896 tmp,
1897 len.reg.into(),
1898 OperandSize::S32,
1899 TRAP_TABLE_OUT_OF_BOUNDS,
1900 )?;
1901 self.masm
1902 .cmp(tmp, segment_len.reg.into(), OperandSize::S32)?;
1903 self.masm
1904 .trapif(IntCmpKind::GtU, TRAP_TABLE_OUT_OF_BOUNDS)?;
1905 self.context.free_reg(segment_len);
1906 }
1907
1908 {
1910 self.masm
1911 .mov(writable!(tmp), table_off.reg.into(), idx_size)?;
1912 self.masm.checked_uadd(
1913 writable!(tmp),
1914 tmp,
1915 len.reg.into(),
1916 idx_size,
1917 TRAP_TABLE_OUT_OF_BOUNDS,
1918 )?;
1919 self.masm.cmp(tmp, table_size.reg.into(), idx_size)?;
1920 self.masm
1921 .trapif(IntCmpKind::GtU, TRAP_TABLE_OUT_OF_BOUNDS)?;
1922 self.context.free_reg(table_size);
1923 }
1924 self.context.free_reg(tmp);
1925
1926 {
1928 self.masm.extend(
1929 writable!(segment_off.reg),
1930 segment_off.reg,
1931 Extend::<Zero>::I64Extend32.into(),
1932 )?;
1933 self.masm.mul(
1934 writable!(segment_off.reg),
1935 segment_off.reg,
1936 RegImm::i64(16),
1937 OperandSize::S64,
1938 )?;
1939 self.masm.add(
1940 writable!(segment_base.reg),
1941 segment_base.reg,
1942 segment_off.reg.into(),
1943 OperandSize::S64,
1944 )?;
1945 self.context.free_reg(segment_off);
1946 }
1947
1948 let header = self.masm.get_label()?;
1951 let exit = self.masm.get_label()?;
1952
1953 self.masm.bind(header)?;
1954 {
1955 self.masm.branch(
1956 IntCmpKind::Eq,
1957 len.reg,
1958 RegImm::i64(0),
1959 exit,
1960 OperandSize::S64,
1961 )?;
1962
1963 let funcref = self.context.any_gpr(self.masm)?;
1966 self.masm.load_ptr(
1967 self.masm.address_at_reg(segment_base.reg, 0)?,
1968 writable!(funcref),
1969 )?;
1970 self.masm.add(
1971 writable!(segment_base.reg),
1972 segment_base.reg,
1973 RegImm::i64(16),
1974 OperandSize::S64,
1975 )?;
1976
1977 self.context.stack.push(segment_base.into());
1981 self.context.stack.push(len.into());
1982 let table_off_copy = self.context.any_gpr(self.masm)?;
1983 self.masm.mov(
1984 writable!(table_off_copy),
1985 table_off.reg.into(),
1986 table_off.ty.try_into()?,
1987 )?;
1988 self.context.stack.push(table_off.into());
1989 self.context
1990 .stack
1991 .push(TypedReg::new(table_off.ty, table_off_copy).into());
1992 self.context
1993 .stack
1994 .push(TypedReg::new(WasmValType::FUNCREF, funcref).into());
1995 self.emit_table_set(table_index)?;
1996
1997 self.context.pop_to_reg(self.masm, Some(table_off.reg))?;
1999 self.context.pop_to_reg(self.masm, Some(len.reg))?;
2000 self.context.pop_to_reg(self.masm, Some(segment_base.reg))?;
2001
2002 self.masm.add(
2004 writable!(table_off.reg),
2005 table_off.reg,
2006 RegImm::i64(1),
2007 table_off.ty.try_into()?,
2008 )?;
2009
2010 self.masm.sub(
2013 writable!(len.reg),
2014 len.reg,
2015 RegImm::i64(1),
2016 OperandSize::S64,
2017 )?;
2018 }
2019 self.masm.jmp(header)?;
2020
2021 self.masm.bind(exit)?;
2022
2023 self.context.free_reg(segment_base);
2024 self.context.free_reg(len);
2025 self.context.free_reg(table_off);
2026 Ok(())
2027 }
2028
2029 pub fn emit_elem_drop(&mut self, elem_index: ElemIndex) -> Result<()> {
2031 let passive_elem_index = match self.env.translation.passive_elem_map[elem_index] {
2032 Some(idx) => idx,
2033 None => return Ok(()),
2035 };
2036 let builtin = self.env.builtins.passive_elem_segment_drop::<M::ABI>()?;
2037 self.context
2038 .stack
2039 .extend([passive_elem_index.as_u32().try_into()?]);
2040 FnCall::emit::<M>(
2041 &mut self.env,
2042 self.masm,
2043 &mut self.context,
2044 Callee::Builtin(builtin),
2045 )?;
2046 self.context.pop_and_free(self.masm)
2047 }
2048
2049 pub fn maybe_emit_fuel_check(&mut self) -> Result<()> {
2053 if !self.tunables.consume_fuel {
2054 return Ok(());
2055 }
2056
2057 self.emit_fuel_increment()?;
2058 let out_of_fuel = self.env.builtins.out_of_gas::<M::ABI>()?;
2059 let fuel_reg = self.context.without::<Result<Reg>, M, _>(
2060 &out_of_fuel.sig().regs,
2061 self.masm,
2062 |cx, masm| cx.any_gpr(masm),
2063 )??;
2064
2065 self.emit_load_fuel_consumed(fuel_reg)?;
2066
2067 let continuation = self.masm.get_label()?;
2069
2070 self.context.spill(self.masm)?;
2073 self.masm.branch(
2076 IntCmpKind::LtS,
2077 fuel_reg,
2078 RegImm::i64(0),
2079 continuation,
2080 OperandSize::S64,
2081 )?;
2082 FnCall::emit::<M>(
2084 &mut self.env,
2085 self.masm,
2086 &mut self.context,
2087 Callee::Builtin(out_of_fuel.clone()),
2088 )?;
2089 self.context.pop_and_free(self.masm)?;
2090
2091 self.masm.bind(continuation)?;
2093 self.context.free_reg(fuel_reg);
2094
2095 Ok(())
2096 }
2097
2098 fn emit_load_fuel_consumed(&mut self, fuel_reg: Reg) -> Result<()> {
2101 let store_context_offset = self.env.vmoffsets.ptr.vmctx_store_context();
2102 let fuel_offset = self.env.vmoffsets.ptr.vmstore_context_fuel_consumed();
2103 self.masm.load_ptr(
2104 self.masm
2105 .address_at_vmctx(u32::from(store_context_offset))?,
2106 writable!(fuel_reg),
2107 )?;
2108
2109 self.masm.load(
2110 self.masm.address_at_reg(fuel_reg, u32::from(fuel_offset))?,
2111 writable!(fuel_reg),
2112 OperandSize::S64,
2114 )
2115 }
2116
2117 pub fn maybe_emit_epoch_check(&mut self) -> Result<()> {
2120 if !self.tunables.epoch_interruption {
2121 return Ok(());
2122 }
2123
2124 let cont = self.masm.get_label()?;
2127 let new_epoch = self.env.builtins.new_epoch::<M::ABI>()?;
2128
2129 let (epoch_deadline_reg, epoch_counter_reg) =
2137 self.context.without::<Result<(Reg, Reg)>, M, _>(
2138 &new_epoch.sig().regs,
2139 self.masm,
2140 |cx, masm| Ok((cx.any_gpr(masm)?, cx.any_gpr(masm)?)),
2141 )??;
2142
2143 self.emit_load_epoch_deadline_and_counter(epoch_deadline_reg, epoch_counter_reg)?;
2144
2145 self.context.spill(self.masm)?;
2148 self.masm.branch(
2149 IntCmpKind::LtU,
2150 epoch_counter_reg,
2151 RegImm::reg(epoch_deadline_reg),
2152 cont,
2153 OperandSize::S64,
2154 )?;
2155 FnCall::emit::<M>(
2157 &mut self.env,
2158 self.masm,
2159 &mut self.context,
2160 Callee::Builtin(new_epoch.clone()),
2161 )?;
2162 self.visit_drop()?;
2165
2166 self.masm.bind(cont)?;
2168
2169 self.context.free_reg(epoch_deadline_reg);
2170 self.context.free_reg(epoch_counter_reg);
2171 Ok(())
2172 }
2173
2174 fn emit_load_epoch_deadline_and_counter(
2175 &mut self,
2176 epoch_deadline_reg: Reg,
2177 epoch_counter_reg: Reg,
2178 ) -> Result<()> {
2179 let epoch_ptr_offset = self.env.vmoffsets.ptr.vmctx_epoch_ptr();
2180 let store_context_offset = self.env.vmoffsets.ptr.vmctx_store_context();
2181 let epoch_deadline_offset = self.env.vmoffsets.ptr.vmstore_context_epoch_deadline();
2182
2183 self.masm.load_ptr(
2185 self.masm.address_at_vmctx(u32::from(epoch_ptr_offset))?,
2186 writable!(epoch_counter_reg),
2187 )?;
2188
2189 self.masm.load(
2192 self.masm.address_at_reg(epoch_counter_reg, 0)?,
2193 writable!(epoch_counter_reg),
2194 OperandSize::S64,
2195 )?;
2196
2197 self.masm.load_ptr(
2199 self.masm
2200 .address_at_vmctx(u32::from(store_context_offset))?,
2201 writable!(epoch_deadline_reg),
2202 )?;
2203
2204 self.masm.load(
2205 self.masm
2206 .address_at_reg(epoch_deadline_reg, u32::from(epoch_deadline_offset))?,
2207 writable!(epoch_deadline_reg),
2208 OperandSize::S64,
2210 )
2211 }
2212
2213 fn emit_fuel_increment(&mut self) -> Result<()> {
2216 let fuel_at_point = std::mem::replace(&mut self.fuel_consumed, 0);
2217 if fuel_at_point == 0 {
2218 return Ok(());
2219 }
2220
2221 let store_context_offset = self.env.vmoffsets.ptr.vmctx_store_context();
2222 let fuel_offset = self.env.vmoffsets.ptr.vmstore_context_fuel_consumed();
2223 let limits_reg = self.context.any_gpr(self.masm)?;
2224
2225 self.masm.load_ptr(
2227 self.masm
2228 .address_at_vmctx(u32::from(store_context_offset))?,
2229 writable!(limits_reg),
2230 )?;
2231
2232 self.masm.with_scratch::<IntScratch, _>(|masm, scratch| {
2233 masm.load(
2235 masm.address_at_reg(limits_reg, u32::from(fuel_offset))?,
2236 scratch.writable(),
2237 OperandSize::S64,
2238 )?;
2239
2240 masm.add(
2243 scratch.writable(),
2244 scratch.inner(),
2245 RegImm::i64(fuel_at_point),
2246 OperandSize::S64,
2247 )?;
2248
2249 masm.store(
2251 scratch.inner().into(),
2252 masm.address_at_reg(limits_reg, u32::from(fuel_offset))?,
2253 OperandSize::S64,
2254 )
2255 })?;
2256
2257 self.context.free_reg(limits_reg);
2258
2259 Ok(())
2260 }
2261
2262 fn fuel_before_visit_op(&mut self, op: &Operator) -> Result<()> {
2264 if !self.context.reachable {
2265 ensure!(self.fuel_consumed == 0, CodeGenError::illegal_fuel_state())
2268 }
2269
2270 self.fuel_consumed += self.tunables.operator_cost.cost(op);
2287
2288 match op {
2289 Operator::Unreachable
2290 | Operator::Loop { .. }
2291 | Operator::If { .. }
2292 | Operator::Else { .. }
2293 | Operator::Br { .. }
2294 | Operator::BrIf { .. }
2295 | Operator::BrTable { .. }
2296 | Operator::End
2297 | Operator::Return
2298 | Operator::CallIndirect { .. }
2299 | Operator::Call { .. }
2300 | Operator::ReturnCall { .. }
2301 | Operator::ReturnCallIndirect { .. } => self.emit_fuel_increment(),
2302 _ => Ok(()),
2303 }
2304 }
2305
2306 fn source_location_before_visit_op(&mut self, offset: usize) -> Result<()> {
2308 let loc = SourceLoc::new(offset as u32);
2309 let rel = self.source_loc_from(loc);
2310 self.source_location.current = self.masm.start_source_loc(rel)?;
2311 Ok(())
2312 }
2313
2314 fn source_location_after_visit_op(&mut self) -> Result<()> {
2316 if self.masm.current_code_offset()? >= self.source_location.current.0 {
2323 self.masm.end_source_loc()?;
2324 }
2325
2326 Ok(())
2327 }
2328
2329 pub(crate) fn emit_atomic_rmw(
2330 &mut self,
2331 arg: &MemArg,
2332 op: RmwOp,
2333 size: OperandSize,
2334 extend: Option<Extend<Zero>>,
2335 ) -> Result<()> {
2336 let memory_index = MemoryIndex::from_u32(arg.memory);
2337 let heap = self.env.resolve_heap(memory_index);
2338 let operand = self.context.pop_to_reg(self.masm, None)?;
2342 if let Some(addr) = self.emit_compute_heap_address_align_checked(&heap, arg, size)? {
2343 let src = self.masm.address_at_reg(addr, 0)?;
2344 self.context.stack.push(operand.into());
2345 self.masm
2346 .atomic_rmw(&mut self.context, src, size, op, UNTRUSTED_FLAGS, extend)?;
2347 self.context.free_reg(addr);
2348 }
2349
2350 Ok(())
2351 }
2352
2353 pub(crate) fn emit_atomic_cmpxchg(
2354 &mut self,
2355 arg: &MemArg,
2356 size: OperandSize,
2357 extend: Option<Extend<Zero>>,
2358 ) -> Result<()> {
2359 let replacement = self.context.pop_to_reg(self.masm, None)?;
2377 let expected = self.context.pop_to_reg(self.masm, None)?;
2378
2379 let memory_index = MemoryIndex::from_u32(arg.memory);
2380 let heap = self.env.resolve_heap(memory_index);
2381 if let Some(addr) = self.emit_compute_heap_address_align_checked(&heap, arg, size)? {
2382 self.context.stack.push(expected.into());
2383 self.context.stack.push(replacement.into());
2384
2385 let src = self.masm.address_at_reg(addr, 0)?;
2386 self.masm
2387 .atomic_cas(&mut self.context, src, size, UNTRUSTED_FLAGS, extend)?;
2388
2389 self.context.free_reg(addr);
2390 }
2391 Ok(())
2392 }
2393
2394 pub fn emit_atomic_wait(&mut self, arg: &MemArg, kind: AtomicWaitKind) -> Result<()> {
2396 let timeout = self.context.pop_to_reg(self.masm, None)?;
2409 let expected = self.context.pop_to_reg(self.masm, None)?;
2410 let addr = self.context.pop_to_reg(self.masm, None)?;
2411
2412 let stack_len = self.context.stack.len();
2414 let builtin = match kind {
2415 AtomicWaitKind::Wait32 => self.env.builtins.memory_atomic_wait32::<M::ABI>()?,
2416 AtomicWaitKind::Wait64 => self.env.builtins.memory_atomic_wait64::<M::ABI>()?,
2417 };
2418 let builtin = self.prepare_builtin_defined_memory_arg(
2419 MemoryIndex::from_u32(arg.memory),
2420 stack_len,
2421 builtin,
2422 )?;
2423
2424 if arg.offset != 0 {
2425 self.masm.checked_uadd(
2426 writable!(addr.reg),
2427 addr.reg,
2428 RegImm::i64(arg.offset as i64),
2429 OperandSize::S64,
2430 TrapCode::HEAP_OUT_OF_BOUNDS,
2431 )?;
2432 }
2433
2434 self.context
2435 .stack
2436 .push(TypedReg::new(WasmValType::I64, addr.reg).into());
2437 self.context.stack.push(expected.into());
2438 self.context.stack.push(timeout.into());
2439
2440 FnCall::emit::<M>(&mut self.env, self.masm, &mut self.context, builtin)?;
2441
2442 Ok(())
2443 }
2444
2445 pub fn emit_atomic_notify(&mut self, arg: &MemArg) -> Result<()> {
2446 let count = self.context.pop_to_reg(self.masm, None)?;
2458 let addr = self.context.pop_to_reg(self.masm, None)?;
2459
2460 let builtin = self.env.builtins.memory_atomic_notify::<M::ABI>()?;
2462 let stack_len = self.context.stack.len();
2463 let builtin = self.prepare_builtin_defined_memory_arg(
2464 MemoryIndex::from_u32(arg.memory),
2465 stack_len,
2466 builtin,
2467 )?;
2468
2469 if arg.offset != 0 {
2470 self.masm.checked_uadd(
2471 writable!(addr.reg),
2472 addr.reg,
2473 RegImm::i64(arg.offset as i64),
2474 OperandSize::S64,
2475 TrapCode::HEAP_OUT_OF_BOUNDS,
2476 )?;
2477 }
2478
2479 self.context
2481 .stack
2482 .push(TypedReg::new(WasmValType::I64, addr.reg).into());
2483 self.context.stack.push(count.into());
2484
2485 FnCall::emit::<M>(&mut self.env, self.masm, &mut self.context, builtin)?;
2486
2487 Ok(())
2488 }
2489
2490 pub fn prepare_builtin_defined_memory_arg(
2491 &mut self,
2492 mem: MemoryIndex,
2493 defined_index_at: usize,
2494 builtin: BuiltinFunction,
2495 ) -> Result<Callee> {
2496 match self.env.translation.module.defined_memory_index(mem) {
2497 Some(defined) => {
2500 self.context
2501 .stack
2502 .insert_many(defined_index_at, &[defined.as_u32().try_into()?]);
2503 Ok(Callee::Builtin(builtin))
2504 }
2505
2506 None => {
2510 let vmimport = self.env.vmoffsets.vmctx_vmmemory_import(mem);
2511 let vmctx_offset = vmimport + u32::from(self.env.vmoffsets.vmmemory_import_vmctx());
2512 let index_offset = vmimport + u32::from(self.env.vmoffsets.vmmemory_import_index());
2513 let index_addr = self.masm.address_at_vmctx(index_offset)?;
2514 let index_dst = self.context.reg_for_class(RegClass::Int, self.masm)?;
2515 self.masm
2516 .load(index_addr, writable!(index_dst), OperandSize::S32)?;
2517 self.context
2518 .stack
2519 .insert_many(defined_index_at, &[Val::reg(index_dst, WasmValType::I32)]);
2520 Ok(Callee::BuiltinWithDifferentVmctx(builtin, vmctx_offset))
2521 }
2522 }
2523 }
2524
2525 pub fn prepare_builtin_defined_table_arg(
2527 &mut self,
2528 table: TableIndex,
2529 defined_index_at: usize,
2530 builtin: BuiltinFunction,
2531 ) -> Result<Callee> {
2532 match self.env.translation.module.defined_table_index(table) {
2533 Some(defined) => {
2534 self.context
2535 .stack
2536 .insert_many(defined_index_at, &[defined.as_u32().try_into()?]);
2537 Ok(Callee::Builtin(builtin))
2538 }
2539 None => {
2540 let vmimport = self.env.vmoffsets.vmctx_vmtable_import(table);
2541 let vmctx_offset = vmimport + u32::from(self.env.vmoffsets.vmtable_import_vmctx());
2542 let index_offset = vmimport + u32::from(self.env.vmoffsets.vmtable_import_index());
2543 let index_addr = self.masm.address_at_vmctx(index_offset)?;
2544 let index_dst = self.context.reg_for_class(RegClass::Int, self.masm)?;
2545 self.masm
2546 .load(index_addr, writable!(index_dst), OperandSize::S32)?;
2547 self.context
2548 .stack
2549 .insert_many(defined_index_at, &[Val::reg(index_dst, WasmValType::I32)]);
2550 Ok(Callee::BuiltinWithDifferentVmctx(builtin, vmctx_offset))
2551 }
2552 }
2553 }
2554}
2555
2556pub fn control_index(depth: u32, control_length: usize) -> Result<usize> {
2559 (control_length - 1)
2560 .checked_sub(depth as usize)
2561 .ok_or_else(|| format_err!(CodeGenError::control_frame_expected()))
2562}