1use crate::{
2 Result,
3 abi::{ABI, ABIOperand, ABISig, LocalSlot, 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 self.copy_stack_gc_refs_to_frame()?;
172
173 let defined_locals_range = &self.context.frame.defined_locals_range;
174 self.masm.zero_mem_range(defined_locals_range.as_range())?;
175
176 if self.sig.params.has_retptr() {
179 match self.sig.params.unwrap_results_area_operand() {
180 ABIOperand::Reg { ty, reg, .. } => {
181 let results_base_slot = self.context.frame.results_base_slot.as_ref().unwrap();
182 ensure!(
183 results_base_slot.addressed_from_sp(),
184 CodeGenError::sp_addressing_expected(),
185 );
186 let addr = self.masm.local_address(results_base_slot)?;
187 self.masm.store((*reg).into(), addr, (*ty).try_into()?)?;
188 }
189 _ => {}
192 }
193 }
194
195 self.masm.end_source_loc()?;
196
197 Ok(CodeGen {
198 sig: self.sig,
199 context: self.context.for_emission(),
200 masm: self.masm,
201 env: self.env,
202 tunables: self.tunables,
203 source_location: self.source_location,
204 control_frames: self.control_frames,
205 fuel_consumed: self.fuel_consumed,
206 phase: PhantomData,
207 })
208 }
209
210 fn spill_register_arguments(&mut self) -> Result<()> {
211 use WasmValType::*;
212 for (operand, slot) in self
213 .sig
214 .params_without_retptr()
215 .iter()
216 .zip(self.context.frame.locals())
217 {
218 match (operand, slot) {
219 (ABIOperand::Reg { ty, reg, .. }, slot) => {
220 let addr = self.masm.local_address(slot)?;
221 match &ty {
222 I32 | I64 | F32 | F64 | V128 => {
223 self.masm.store((*reg).into(), addr, (*ty).try_into()?)?;
224 }
225 Ref(rt) => match rt.heap_type {
226 WasmHeapType::Func => {
227 self.masm.store_ptr(*reg, addr)?;
228 }
229 WasmHeapType::Extern => {
230 self.masm.store((*reg).into(), addr, (*ty).try_into()?)?;
231 }
232 _ => bail!(CodeGenError::unsupported_wasm_type()),
233 },
234 }
235 }
236 _ => {}
238 }
239 }
240 Ok(())
241 }
242
243 fn copy_stack_gc_refs_to_frame(&mut self) -> Result<()> {
248 for (operand, slot) in self
249 .sig
250 .params_without_retptr()
251 .iter()
252 .zip(self.context.frame.locals())
253 {
254 match (operand, slot) {
255 (ABIOperand::Stack { ty, offset, .. }, slot)
256 if ty.is_vmgcref_type_and_not_i31() =>
257 {
258 ensure!(
259 slot.addressed_from_sp(),
260 CodeGenError::sp_addressing_expected(),
261 );
262 let arg_base = u32::from(<M::ABI as ABI>::arg_base_offset());
263 let src = LocalSlot::stack_arg(*ty, offset + arg_base);
264 let src_addr = self.masm.local_address(&src)?;
265 let dst_addr = self.masm.local_address(slot)?;
266 self.masm.with_scratch::<IntScratch, _>(|masm, scratch| {
267 masm.load(src_addr, scratch.writable(), (*ty).try_into()?)?;
268 masm.store(scratch.inner().into(), dst_addr, (*ty).try_into()?)
269 })?;
270 }
271 _ => {}
272 }
273 }
274 Ok(())
275 }
276}
277
278impl<'a, 'translation, 'data, M> CodeGen<'a, 'translation, 'data, M, Emission>
279where
280 M: MacroAssembler,
281{
282 pub fn emit(
284 &mut self,
285 body: BinaryReader<'a>,
286 validator: &mut FuncValidator<ValidatorResources>,
287 ) -> Result<()> {
288 self.emit_body(body, validator)
289 .and_then(|_| self.emit_end())?;
290
291 Ok(())
292 }
293
294 pub fn pop_control_frame(&mut self) -> Result<ControlStackFrame> {
296 self.control_frames
297 .pop()
298 .ok_or_else(|| format_err!(CodeGenError::control_frame_expected()))
299 }
300
301 pub fn source_loc_from(&mut self, loc: SourceLoc) -> RelSourceLoc {
303 if self.source_location.base.is_none() && !loc.is_default() {
304 self.source_location.base = Some(loc);
305 }
306
307 RelSourceLoc::from_base_offset(self.source_location.base.unwrap_or_default(), loc)
308 }
309
310 pub fn handle_unreachable_else(&mut self) -> Result<()> {
318 let frame = self
319 .control_frames
320 .last_mut()
321 .ok_or_else(|| CodeGenError::control_frame_expected())?;
322 ensure!(frame.is_if(), CodeGenError::if_control_frame_expected());
323 if frame.is_next_sequence_reachable() {
324 self.context.reachable = true;
328 frame.ensure_stack_state(self.masm, &mut self.context)?;
329 frame.bind_else(self.masm, &mut self.context)?;
330 }
331 Ok(())
332 }
333
334 pub fn handle_unreachable_end(&mut self) -> Result<()> {
335 let mut frame = self.pop_control_frame()?;
336 let is_outermost = self.control_frames.len() == 0;
338
339 if frame.is_next_sequence_reachable() {
340 self.context.reachable = true;
341 frame.ensure_stack_state(self.masm, &mut self.context)?;
342 frame.bind_end(self.masm, &mut self.context)
343 } else if is_outermost {
344 frame.ensure_stack_state(self.masm, &mut self.context)
349 } else {
350 Ok(())
351 }
352 }
353
354 fn emit_body(
355 &mut self,
356 body: BinaryReader<'a>,
357 validator: &mut FuncValidator<ValidatorResources>,
358 ) -> Result<()> {
359 self.maybe_emit_fuel_check()?;
360
361 self.maybe_emit_epoch_check()?;
362
363 self.control_frames.push(ControlStackFrame::block(
366 BlockSig::from_sig(self.sig.clone()),
367 self.masm,
368 &mut self.context,
369 )?);
370
371 if self.sig.params.has_retptr() {
376 self.sig
377 .results
378 .set_ret_area(RetArea::slot(self.context.frame.results_base_slot.unwrap()));
379 }
380
381 let mut ops = OperatorsReader::new(body);
382 while !ops.eof() {
383 let offset = ops.original_position();
384 ops.visit_operator(&mut ValidateThenVisit(
385 validator.simd_visitor(offset),
386 self,
387 offset,
388 ))??;
389 }
390 ops.finish()?;
391 return Ok(());
392
393 struct ValidateThenVisit<'a, T, U>(T, &'a mut U, usize);
394
395 macro_rules! validate_then_visit {
396 ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $ann:tt)*) => {
397 $(
398 fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
399 self.0.$visit($($($arg.clone()),*)?)?;
400 let op = Operator::$op $({ $($arg: $arg.clone()),* })?;
401 if self.1.visit(&op) {
402 self.1.before_visit_op(&op, self.2)?;
403 let res = self.1.$visit($($($arg),*)?)?;
404 self.1.after_visit_op()?;
405 Ok(res)
406 } else {
407 Ok(())
408 }
409 }
410 )*
411 };
412 }
413
414 fn visit_op_when_unreachable(op: &Operator) -> bool {
415 use Operator::*;
416 match op {
417 If { .. } | Block { .. } | Loop { .. } | Else | End => true,
418 _ => false,
419 }
420 }
421
422 trait VisitorHooks {
425 fn before_visit_op(&mut self, operator: &Operator, offset: usize) -> Result<()>;
427 fn after_visit_op(&mut self) -> Result<()>;
429
430 fn visit(&self, op: &Operator) -> bool;
439 }
440
441 impl<'a, 'translation, 'data, M: MacroAssembler> VisitorHooks
442 for CodeGen<'a, 'translation, 'data, M, Emission>
443 {
444 fn visit(&self, op: &Operator) -> bool {
445 self.context.reachable || visit_op_when_unreachable(op)
446 }
447
448 fn before_visit_op(&mut self, operator: &Operator, offset: usize) -> Result<()> {
449 self.source_location_before_visit_op(offset)?;
451
452 if self.tunables.consume_fuel {
454 self.fuel_before_visit_op(operator)?;
455 }
456 Ok(())
457 }
458
459 fn after_visit_op(&mut self) -> Result<()> {
460 self.source_location_after_visit_op()
462 }
463 }
464
465 impl<'a, T, U> VisitOperator<'a> for ValidateThenVisit<'_, T, U>
466 where
467 T: VisitSimdOperator<'a, Output = wasmparser::Result<()>>,
468 U: VisitSimdOperator<'a, Output = Result<()>> + VisitorHooks,
469 {
470 type Output = U::Output;
471
472 fn simd_visitor(
473 &mut self,
474 ) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>>
475 where
476 T:,
477 {
478 Some(self)
479 }
480
481 wasmparser::for_each_visit_operator!(validate_then_visit);
482 }
483
484 impl<'a, T, U> VisitSimdOperator<'a> for ValidateThenVisit<'_, T, U>
485 where
486 T: VisitSimdOperator<'a, Output = wasmparser::Result<()>>,
487 U: VisitSimdOperator<'a, Output = Result<()>> + VisitorHooks,
488 {
489 wasmparser::for_each_visit_simd_operator!(validate_then_visit);
490 }
491 }
492
493 pub fn emit_typecheck_funcref(
495 &mut self,
496 funcref_ptr: Reg,
497 type_index: TypeIndex,
498 ) -> Result<()> {
499 let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
500 let sig_index_bytes = self.env.vmoffsets.size_of_vmshared_type_index();
501 let sig_size = OperandSize::from_bytes(sig_index_bytes);
502 let sig_index = self.env.translation.module.types[type_index].unwrap_module_type_index();
503 let sig_offset = sig_index
504 .as_u32()
505 .checked_mul(sig_index_bytes.into())
506 .unwrap();
507 let signatures_base_offset = self.env.vmoffsets.ptr.vmctx_type_ids_array();
508 let funcref_sig_offset = self.env.vmoffsets.ptr.vm_func_ref().type_index();
509 let caller_id = self.context.any_gpr(self.masm)?;
511
512 self.masm.with_scratch::<IntScratch, _>(|masm, scratch| {
513 masm.load(
515 masm.address_at_vmctx(signatures_base_offset.into())?,
516 scratch.writable(),
517 ptr_size,
518 )?;
519
520 masm.load(
521 masm.address_at_reg(scratch.inner(), sig_offset)?,
522 writable!(caller_id),
523 sig_size,
524 )
525 })?;
526
527 let callee_id = self.context.any_gpr(self.masm)?;
528 self.masm.load(
529 self.masm
530 .address_at_reg(funcref_ptr, funcref_sig_offset.into())?,
531 writable!(callee_id),
532 sig_size,
533 )?;
534
535 self.masm
537 .cmp(caller_id, callee_id.into(), OperandSize::S32)?;
538 self.masm.trapif(IntCmpKind::Ne, TRAP_BAD_SIGNATURE)?;
539 self.context.free_reg(callee_id);
540 self.context.free_reg(caller_id);
541 wasmtime_environ::error::Ok(())
542 }
543
544 fn emit_end(&mut self) -> Result<()> {
546 let base = SPOffset::from_u32(self.context.frame.locals_size);
550 self.masm.start_source_loc(Default::default())?;
551 if self.context.reachable {
552 ControlStackFrame::pop_abi_results_impl(
553 &mut self.sig.results,
554 &mut self.context,
555 self.masm,
556 |results, _, _| Ok(results.ret_area().copied()),
557 )?;
558 } else {
559 self.context.truncate_stack_to(0)?;
563 self.masm.reset_stack_pointer(base)?;
564 }
565 ensure!(
566 self.context.stack.len() == 0,
567 CodeGenError::unexpected_value_in_value_stack()
568 );
569 self.masm.free_stack(self.context.frame.locals_size)?;
570 self.masm.epilogue()?;
571 self.masm.end_source_loc()?;
572 Ok(())
573 }
574
575 pub fn emit_set_local(&mut self, index: u32) -> Result<TypedReg> {
579 if self.context.stack.contains_latent_local(index) {
582 self.context.spill(self.masm)?;
583 }
584 let src = self.context.pop_to_reg(self.masm, None)?;
585 let (ty, addr) = self.context.frame.get_local_address(index, self.masm)?;
589 self.masm
590 .store(RegImm::reg(src.reg), addr, ty.try_into()?)?;
591
592 Ok(src)
593 }
594
595 pub fn emit_get_global_addr(&mut self, index: GlobalIndex) -> Result<(WasmValType, Reg, u32)> {
597 let data = self.env.resolve_global(index);
598
599 if data.imported {
600 let global_base = self.masm.address_at_reg(vmctx!(M), data.offset)?;
601 let dst = self.context.any_gpr(self.masm)?;
602 self.masm.load_ptr(global_base, writable!(dst))?;
603 Ok((data.ty, dst, 0))
604 } else {
605 Ok((data.ty, vmctx!(M), data.offset))
606 }
607 }
608
609 pub fn emit_table_get(&mut self, table_index: TableIndex) -> Result<()> {
610 let table = self.env.table(table_index);
611 let heap_type = table.ref_type.heap_type;
612 ensure!(
613 heap_type == WasmHeapType::Func,
614 CodeGenError::unsupported_wasm_type()
615 );
616 ensure!(
617 self.tunables.table_lazy_init,
618 CodeGenError::unsupported_table_eager_init()
619 );
620 let table_data = self.env.resolve_table_data(table_index);
621 let ptr_type = self.env.ptr_type();
622 let builtin = self.env.builtins.table_get_lazy_init_func_ref::<M::ABI>()?;
623
624 self.context.spill(self.masm)?;
629 let elem_value: Reg = self.context.reg(
630 builtin.sig().results.unwrap_singleton().unwrap_reg(),
631 self.masm,
632 )?;
633
634 let index = self.context.pop_to_reg(self.masm, None)?;
635 let base = self.context.any_gpr(self.masm)?;
636
637 let elem_addr = self.emit_compute_table_elem_addr(index.into(), base, &table_data)?;
638 self.masm.load_ptr(elem_addr, writable!(elem_value))?;
639 self.context.free_reg(base);
642
643 let (defined, cont) = (self.masm.get_label()?, self.masm.get_label()?);
644
645 self.context
647 .stack
648 .extend([table_index.as_u32().try_into().unwrap(), index.into()]);
649
650 self.masm.branch(
651 IntCmpKind::Ne,
652 elem_value,
653 elem_value.into(),
654 defined,
655 ptr_type.try_into()?,
656 )?;
657 self.context.free_reg(elem_value);
661 FnCall::emit::<M>(
662 &mut self.env,
663 self.masm,
664 &mut self.context,
665 Callee::Builtin(builtin.clone()),
666 )?;
667
668 let top = self
671 .context
672 .stack
673 .peek()
674 .ok_or_else(|| CodeGenError::missing_values_in_stack())?;
675 let top = top.unwrap_reg();
676 ensure!(
677 top.reg == elem_value,
678 CodeGenError::table_element_value_expected()
679 );
680 self.masm.jmp(cont)?;
681
682 self.masm.bind(defined)?;
689 assert_eq!(FUNCREF_MASK as isize, -2);
690 let imm = RegImm::i64(-2);
691 let dst = top.into();
692 self.masm
693 .and(writable!(dst), dst, imm, top.ty.try_into()?)?;
694
695 self.masm.bind(cont)
696 }
697
698 pub fn emit_table_set(&mut self, table_index: TableIndex) -> Result<()> {
703 let table = self.env.table(table_index);
704 ensure!(
705 table.ref_type.heap_type == WasmHeapType::Func,
706 CodeGenError::unsupported_wasm_type()
707 );
708 ensure!(
709 self.tunables.table_lazy_init,
710 CodeGenError::unsupported_table_eager_init()
711 );
712 let ptr_type = self.env.ptr_type();
713 let table_data = self.env.resolve_table_data(table_index);
714 let value = self.context.pop_to_reg(self.masm, None)?;
715 let index = self.context.pop_to_reg(self.masm, None)?;
716 let base = self.context.any_gpr(self.masm)?;
717 let elem_addr = self.emit_compute_table_elem_addr(index.into(), base, &table_data)?;
718 self.masm.or(
720 writable!(value.into()),
721 value.into(),
722 RegImm::i64(FUNCREF_INIT_BIT as i64),
723 ptr_type.try_into()?,
724 )?;
725
726 self.masm.store_ptr(value.into(), elem_addr)?;
727
728 self.context.free_reg(value);
729 self.context.free_reg(index);
730 self.context.free_reg(base);
731 Ok(())
732 }
733
734 pub fn emit_table_grow(&mut self, table_index: TableIndex) -> Result<()> {
736 let ptr_type = self.env.ptr_type();
737 let idx_type = self.env.table(table_index).idx_type;
738
739 let delta = self.context.pop_to_reg(self.masm, None)?;
742 let tmp = self.context.any_gpr(self.masm)?;
743 self.masm
744 .mov(writable!(tmp), delta.reg.into(), delta.ty.try_into()?)?;
745 self.context.stack.push(TypedReg::new(delta.ty, tmp).into());
746 self.context.stack.push(delta.into());
747
748 let at = self.context.stack.ensure_index_at(1)?;
751 let builtin = self.env.builtins.table_grow::<M::ABI>()?;
752 let builtin = self.prepare_builtin_defined_table_arg(table_index, at, builtin)?;
753 FnCall::emit::<M>(&mut self.env, self.masm, &mut self.context, builtin)?;
754
755 let result = self.context.pop_to_reg(self.masm, None)?;
759 let len = self.context.pop_to_reg(self.masm, None)?;
760 let init = self.context.pop_to_reg(self.masm, None)?;
761
762 let tmp_result = self.context.any_gpr(self.masm)?;
765 self.masm.mov(
766 writable!(tmp_result),
767 result.reg.into(),
768 result.ty.try_into()?,
769 )?;
770 self.context
771 .stack
772 .push(TypedReg::new(result.ty, tmp_result).into());
773
774 let done = self.masm.get_label()?;
777 self.masm.branch(
778 IntCmpKind::Eq,
779 result.reg,
780 RegImm::i64(-1),
781 done,
782 OperandSize::S64,
783 )?;
784
785 self.context.stack.push(result.into());
788 self.context.stack.push(init.into());
789 self.context.stack.push(len.into());
790 self.emit_table_fill(table_index)?;
791
792 self.masm.bind(done)?;
793
794 match (ptr_type, idx_type) {
798 (WasmValType::I64, IndexType::I64) => Ok(()),
799 (WasmValType::I64, IndexType::I32) => {
800 let top: Reg = self.context.pop_to_reg(self.masm, None)?.into();
801 self.masm.wrap(writable!(top), top)?;
802 self.context.stack.push(TypedReg::i32(top).into());
803 Ok(())
804 }
805
806 _ => Err(format_err!(CodeGenError::unsupported_32_bit_platform())),
807 }
808 }
809
810 pub fn emit_table_fill(&mut self, table_index: TableIndex) -> Result<()> {
812 let len = self.context.pop_to_reg(self.masm, None)?;
814 let init = self.context.pop_to_reg(self.masm, None)?;
815 let offset = self.context.pop_to_reg(self.masm, None)?;
816
817 let table_data = self.env.resolve_table_data(table_index);
819 self.emit_compute_table_size(&table_data)?;
820 let table_size = self.context.pop_to_reg(self.masm, None)?;
821 let tmp = self.context.any_gpr(self.masm)?;
822 let idx_size = table_data.index_type().try_into()?;
823 self.masm.mov(writable!(tmp), offset.reg.into(), idx_size)?;
824 self.masm.checked_uadd(
825 writable!(tmp),
826 tmp,
827 len.reg.into(),
828 idx_size,
829 TRAP_TABLE_OUT_OF_BOUNDS,
830 )?;
831 self.masm.cmp(tmp, table_size.reg.into(), idx_size)?;
832 self.masm
833 .trapif(IntCmpKind::GtU, TRAP_TABLE_OUT_OF_BOUNDS)?;
834 self.context.free_reg(tmp);
835 self.context.free_reg(table_size);
836
837 let header = self.masm.get_label()?;
838 let exit = self.masm.get_label()?;
839
840 self.masm.bind(header)?;
841
842 self.masm.branch(
844 IntCmpKind::Eq,
845 len.reg,
846 RegImm::i64(0),
847 exit,
848 OperandSize::S64,
849 )?;
850
851 let tmp_index = self.context.any_gpr(self.masm)?;
855 let tmp_init = self.context.any_gpr(self.masm)?;
856 self.masm
857 .mov(writable!(tmp_index), offset.reg.into(), OperandSize::S64)?;
858 self.masm
859 .mov(writable!(tmp_init), init.reg.into(), OperandSize::S64)?;
860
861 self.context.stack.push(TypedReg::i64(len.reg).into());
863 self.context.stack.push(TypedReg::i64(init.reg).into());
864 self.context.stack.push(TypedReg::i64(offset.reg).into());
865
866 self.context.stack.push(TypedReg::i64(tmp_index).into());
868 self.context.stack.push(TypedReg::i64(tmp_init).into());
869 self.emit_table_set(table_index)?;
870
871 self.context.pop_to_reg(self.masm, Some(offset.reg))?;
874 self.context.pop_to_reg(self.masm, Some(init.reg))?;
875 self.context.pop_to_reg(self.masm, Some(len.reg))?;
876
877 self.masm.add(
880 writable!(offset.reg),
881 offset.reg,
882 RegImm::i64(1),
883 OperandSize::S64,
884 )?;
885 self.masm.sub(
886 writable!(len.reg),
887 len.reg,
888 RegImm::i64(1),
889 OperandSize::S64,
890 )?;
891 self.masm.jmp(header)?;
892
893 self.masm.bind(exit)?;
894
895 self.context.free_reg(offset);
896 self.context.free_reg(init);
897 self.context.free_reg(len);
898 Ok(())
899 }
900
901 fn emit_table_range_bounds_check(
907 &mut self,
908 table_data: &TableData,
909 idx: Reg,
910 len: Reg,
911 ) -> Result<()> {
912 self.emit_compute_table_size(table_data)?;
913 let size = self.context.pop_to_reg(self.masm, None)?;
914
915 let end = self.context.any_gpr(self.masm)?;
918 self.masm
919 .mov(writable!(end), idx.into(), OperandSize::S64)?;
920 self.masm.checked_uadd(
921 writable!(end),
922 end,
923 len.into(),
924 OperandSize::S64,
925 TRAP_TABLE_OUT_OF_BOUNDS,
926 )?;
927 self.masm.cmp(end, size.reg.into(), OperandSize::S64)?;
928 self.masm
929 .trapif(IntCmpKind::GtU, TRAP_TABLE_OUT_OF_BOUNDS)?;
930
931 self.context.free_reg(size);
932 self.context.free_reg(end);
933 Ok(())
934 }
935
936 pub fn emit_table_copy(&mut self, dst_table: TableIndex, src_table: TableIndex) -> Result<()> {
938 let dst_data = self.env.resolve_table_data(dst_table);
939 let src_data = self.env.resolve_table_data(src_table);
940
941 let len = self.context.pop_to_reg(self.masm, None)?;
943 let src = self.context.pop_to_reg(self.masm, None)?;
944 let dst = self.context.pop_to_reg(self.masm, None)?;
945
946 for op in [&len, &src, &dst] {
950 if op.ty == WasmValType::I32 {
951 self.masm.extend(
952 writable!(op.reg),
953 op.reg,
954 Extend::<Zero>::I64Extend32.into(),
955 )?;
956 }
957 }
958
959 self.emit_table_range_bounds_check(&src_data, src.reg, len.reg)?;
962 self.emit_table_range_bounds_check(&dst_data, dst.reg, len.reg)?;
963
964 let step = self.context.any_gpr(self.masm)?;
967 let forward = self.masm.get_label()?;
968 let setup_done = self.masm.get_label()?;
969 self.masm.branch(
970 IntCmpKind::LeU,
971 dst.reg,
972 src.reg.into(),
973 forward,
974 OperandSize::S64,
975 )?;
976 {
978 self.masm
979 .mov(writable!(step), RegImm::i64(-1), OperandSize::S64)?;
980 self.masm.add(
981 writable!(src.reg),
982 src.reg,
983 len.reg.into(),
984 OperandSize::S64,
985 )?;
986 self.masm.sub(
987 writable!(src.reg),
988 src.reg,
989 RegImm::i64(1),
990 OperandSize::S64,
991 )?;
992 self.masm.add(
993 writable!(dst.reg),
994 dst.reg,
995 len.reg.into(),
996 OperandSize::S64,
997 )?;
998 self.masm.sub(
999 writable!(dst.reg),
1000 dst.reg,
1001 RegImm::i64(1),
1002 OperandSize::S64,
1003 )?;
1004 }
1005 self.masm.jmp(setup_done)?;
1006 self.masm.bind(forward)?;
1008 {
1009 self.masm
1010 .mov(writable!(step), RegImm::i64(1), OperandSize::S64)?;
1011 }
1012
1013 self.masm.bind(setup_done)?;
1014
1015 let header = self.masm.get_label()?;
1016 let exit = self.masm.get_label()?;
1017
1018 self.masm.bind(header)?;
1019
1020 self.masm.branch(
1022 IntCmpKind::Eq,
1023 len.reg,
1024 RegImm::i64(0),
1025 exit,
1026 OperandSize::S64,
1027 )?;
1028
1029 self.context.stack.push(TypedReg::i64(step).into());
1033 self.context.stack.push(TypedReg::i64(len.reg).into());
1034 self.context.stack.push(TypedReg::i64(dst.reg).into());
1035 self.context.stack.push(TypedReg::i64(src.reg).into());
1036
1037 {
1046 let tmp_src = self.context.pop_to_reg(self.masm, None)?;
1047 let s = self.context.any_gpr(self.masm)?;
1048 self.masm
1049 .mov(writable!(s), tmp_src.reg.into(), OperandSize::S64)?;
1050 self.context.stack.push(tmp_src.into());
1051 self.context.stack.push(TypedReg::i64(s).into());
1052 self.emit_table_get(src_table)?;
1053 let funcref = self.context.pop_to_reg(self.masm, None)?;
1054
1055 let tmp_src = self.context.pop_to_reg(self.masm, None)?;
1056 let tmp_dst = self.context.pop_to_reg(self.masm, None)?;
1057
1058 let d = self.context.any_gpr(self.masm)?;
1059 self.masm
1060 .mov(writable!(d), tmp_dst.reg.into(), OperandSize::S64)?;
1061 self.context.stack.push(tmp_dst.into());
1062 self.context.stack.push(tmp_src.into());
1063 self.context.stack.push(TypedReg::i64(d).into());
1064 self.context.stack.push(funcref.into());
1065 self.emit_table_set(dst_table)?;
1066 }
1067
1068 self.context.pop_to_reg(self.masm, Some(src.reg))?;
1071 self.context.pop_to_reg(self.masm, Some(dst.reg))?;
1072 self.context.pop_to_reg(self.masm, Some(len.reg))?;
1073 self.context.pop_to_reg(self.masm, Some(step))?;
1074
1075 self.masm
1077 .add(writable!(dst.reg), dst.reg, step.into(), OperandSize::S64)?;
1078 self.masm
1079 .add(writable!(src.reg), src.reg, step.into(), OperandSize::S64)?;
1080 self.masm.sub(
1081 writable!(len.reg),
1082 len.reg,
1083 RegImm::i64(1),
1084 OperandSize::S64,
1085 )?;
1086
1087 self.masm.jmp(header)?;
1088
1089 self.masm.bind(exit)?;
1090
1091 self.context.free_reg(src);
1092 self.context.free_reg(dst);
1093 self.context.free_reg(len);
1094 self.context.free_reg(step);
1095 Ok(())
1096 }
1097
1098 pub fn emit_compute_heap_address(
1119 &mut self,
1120 heap: &HeapData,
1121 memarg: &MemArg,
1122 access_size: OperandSize,
1123 ) -> Result<Option<Reg>> {
1124 let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
1125 let enable_spectre_mitigation = self.env.heap_access_spectre_mitigation();
1126 let add_offset_and_access_size = |offset: ImmOffset, access_size: OperandSize| {
1127 (access_size.bytes() as u64) + (offset.as_u32() as u64)
1128 };
1129
1130 let index = Index::from_typed_reg(self.context.pop_to_reg(self.masm, None)?);
1131
1132 let offset = bounds::ensure_index_and_offset(
1133 self.masm,
1134 index,
1135 memarg.offset,
1136 heap.index_type().try_into()?,
1137 )?;
1138 let offset_with_access_size = add_offset_and_access_size(offset, access_size);
1139
1140 let memory_tunables = MemoryTunables::new(self.tunables, MemoryKind::LinearMemory);
1141 let can_elide_bounds_check = heap
1142 .memory
1143 .can_elide_bounds_check(&memory_tunables, self.env.page_size_log2);
1144
1145 let addr = if offset_with_access_size > heap.memory.maximum_byte_size().unwrap_or(u64::MAX)
1146 || (!self.tunables.memory_may_move
1147 && offset_with_access_size > self.tunables.memory_reservation)
1148 {
1149 self.emit_fuel_increment()?;
1156 self.masm.trap(TrapCode::HEAP_OUT_OF_BOUNDS)?;
1157 self.context.reachable = false;
1158 None
1159
1160 } else if can_elide_bounds_check
1187 && u64::from(u32::MAX)
1188 <= self.tunables.memory_reservation + self.tunables.memory_guard_size
1189 - offset_with_access_size
1190 {
1191 assert!(can_elide_bounds_check);
1192 assert!(heap.index_type() == WasmValType::I32);
1193 let addr = self.context.any_gpr(self.masm)?;
1194 bounds::load_heap_addr_unchecked(self.masm, &heap, index, offset, addr, ptr_size)?;
1195 Some(addr)
1196
1197 } else if let Some(static_size) = heap.memory.static_heap_size() {
1205 let bounds = Bounds::from_u64(static_size);
1206 let addr = bounds::load_heap_addr_checked(
1207 self.masm,
1208 &mut self.context,
1209 ptr_size,
1210 &heap,
1211 enable_spectre_mitigation,
1212 bounds,
1213 index,
1214 offset,
1215 |masm, bounds, index| {
1216 let adjusted_bounds = bounds.as_u64() - offset_with_access_size;
1217 let index_reg = index.as_typed_reg().reg;
1218 masm.cmp(
1219 index_reg,
1220 RegImm::i64(adjusted_bounds as i64),
1221 ptr_size,
1227 )?;
1228 Ok(IntCmpKind::GtU)
1229 },
1230 )?;
1231 Some(addr)
1232 } else {
1233 let bounds = bounds::load_dynamic_heap_bounds::<_>(
1239 &mut self.context,
1240 self.masm,
1241 &heap,
1242 ptr_size,
1243 )?;
1244
1245 let index_reg = index.as_typed_reg().reg;
1246 let index_offset_and_access_size = self.context.any_gpr(self.masm)?;
1250
1251 self.masm.mov(
1263 writable!(index_offset_and_access_size),
1264 index_reg.into(),
1265 heap.index_type().try_into()?,
1266 )?;
1267 self.masm.checked_uadd(
1278 writable!(index_offset_and_access_size),
1279 index_offset_and_access_size,
1280 RegImm::i64(offset_with_access_size as i64),
1281 ptr_size,
1282 TrapCode::HEAP_OUT_OF_BOUNDS,
1283 )?;
1284
1285 let addr = bounds::load_heap_addr_checked(
1286 self.masm,
1287 &mut self.context,
1288 ptr_size,
1289 &heap,
1290 enable_spectre_mitigation,
1291 bounds,
1292 index,
1293 offset,
1294 |masm, bounds, _| {
1295 let bounds_reg = bounds.as_typed_reg().reg;
1296 masm.cmp(
1297 index_offset_and_access_size,
1298 bounds_reg.into(),
1299 ptr_size,
1303 )?;
1304 Ok(IntCmpKind::GtU)
1305 },
1306 )?;
1307 self.context.free_reg(bounds.as_typed_reg().reg);
1308 self.context.free_reg(index_offset_and_access_size);
1309 Some(addr)
1310 };
1311
1312 self.context.free_reg(index.as_typed_reg().reg);
1313 Ok(addr)
1314 }
1315
1316 fn emit_check_align(
1319 &mut self,
1320 heap: &HeapData,
1321 memarg: &MemArg,
1322 access_size: OperandSize,
1323 ) -> Result<()> {
1324 if access_size.bytes() > 1 {
1325 let heap_ty_size: OperandSize = heap.index_type().try_into()?;
1326 let addr = *self
1327 .context
1328 .stack
1329 .peek()
1330 .ok_or_else(|| CodeGenError::missing_values_in_stack())?;
1331 let tmp = self.context.any_gpr(self.masm)?;
1332 self.context.move_val_to_reg(&addr, tmp, self.masm)?;
1333
1334 if memarg.offset != 0 {
1335 self.masm.add(
1336 writable!(tmp),
1337 tmp,
1338 RegImm::Imm(Imm::I64(memarg.offset)),
1339 heap_ty_size,
1340 )?;
1341 }
1342
1343 self.masm.and(
1344 writable!(tmp),
1345 tmp,
1346 RegImm::Imm(Imm::I32(access_size.bytes() - 1)),
1347 heap_ty_size,
1348 )?;
1349
1350 self.masm.cmp(tmp, RegImm::Imm(Imm::i64(0)), heap_ty_size)?;
1351 self.masm.trapif(IntCmpKind::Ne, TRAP_HEAP_MISALIGNED)?;
1352 self.context.free_reg(tmp);
1353 }
1354
1355 Ok(())
1356 }
1357
1358 pub fn emit_compute_heap_address_align_checked(
1359 &mut self,
1360 heap: &HeapData,
1361 memarg: &MemArg,
1362 access_size: OperandSize,
1363 ) -> Result<Option<Reg>> {
1364 self.emit_check_align(heap, memarg, access_size)?;
1365 self.emit_compute_heap_address(heap, memarg, access_size)
1366 }
1367
1368 pub fn emit_wasm_load(
1370 &mut self,
1371 arg: &MemArg,
1372 target_type: WasmValType,
1373 kind: LoadKind,
1374 ) -> Result<()> {
1375 let emit_load = |this: &mut Self, dst, addr, kind| -> Result<()> {
1376 let src = this.masm.address_at_reg(addr, 0)?;
1377 this.masm.wasm_load(src, writable!(dst), kind)?;
1378 this.context
1379 .stack
1380 .push(TypedReg::new(target_type, dst).into());
1381 this.context.free_reg(addr);
1382 Ok(())
1383 };
1384
1385 let memory_index = MemoryIndex::from_u32(arg.memory);
1386 let heap = self.env.resolve_heap(memory_index);
1387
1388 match kind {
1391 LoadKind::VectorLane(_) => {
1392 let dst = self.context.pop_to_reg(self.masm, None)?;
1396 let addr =
1397 self.emit_compute_heap_address(&heap, &arg, kind.derive_operand_size())?;
1398 if let Some(addr) = addr {
1399 emit_load(self, dst.reg, addr, kind)?;
1400 } else {
1401 self.context.free_reg(dst);
1402 }
1403 }
1404 _ => {
1405 let maybe_addr = match kind {
1406 LoadKind::Atomic(_, _) => self.emit_compute_heap_address_align_checked(
1407 &heap,
1408 &arg,
1409 kind.derive_operand_size(),
1410 )?,
1411 _ => self.emit_compute_heap_address(&heap, &arg, kind.derive_operand_size())?,
1412 };
1413
1414 if let Some(addr) = maybe_addr {
1415 let dst = match target_type {
1416 WasmValType::I32 | WasmValType::I64 => self.context.any_gpr(self.masm)?,
1417 WasmValType::F32 | WasmValType::F64 => self.context.any_fpr(self.masm)?,
1418 WasmValType::V128 => self.context.reg_for_type(target_type, self.masm)?,
1419 _ => bail!(CodeGenError::unsupported_wasm_type()),
1420 };
1421
1422 emit_load(self, dst, addr, kind)?;
1423 }
1424 }
1425 }
1426
1427 Ok(())
1428 }
1429
1430 pub fn emit_wasm_store(&mut self, arg: &MemArg, kind: StoreKind) -> Result<()> {
1432 let memory_index = MemoryIndex::from_u32(arg.memory);
1433 let heap = self.env.resolve_heap(memory_index);
1434 let src = self.context.pop_to_reg(self.masm, None)?;
1435
1436 let maybe_addr = match kind {
1437 StoreKind::Atomic(size) => {
1438 self.emit_compute_heap_address_align_checked(&heap, &arg, size)?
1439 }
1440 StoreKind::Operand(size) | StoreKind::VectorLane(LaneSelector { size, .. }) => {
1441 self.emit_compute_heap_address(&heap, &arg, size)?
1442 }
1443 };
1444
1445 if let Some(addr) = maybe_addr {
1446 self.masm
1447 .wasm_store(src.reg, self.masm.address_at_reg(addr, 0)?, kind)?;
1448
1449 self.context.free_reg(addr);
1450 }
1451 self.context.free_reg(src);
1452
1453 Ok(())
1454 }
1455
1456 pub fn emit_compute_table_elem_addr(
1459 &mut self,
1460 index: Reg,
1461 base: Reg,
1462 table_data: &TableData,
1463 ) -> Result<M::Address> {
1464 let bound = self.context.any_gpr(self.masm)?;
1465 let tmp = self.context.any_gpr(self.masm)?;
1466 let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
1467
1468 if let Some(offset) = table_data.import_from {
1469 self.masm
1473 .load_ptr(self.masm.address_at_vmctx(offset)?, writable!(base))?;
1474 } else {
1475 self.masm.mov(writable!(base), vmctx!(M).into(), ptr_size)?;
1478 };
1479
1480 let bound_addr = self
1482 .masm
1483 .address_at_reg(base, table_data.current_elems_offset)?;
1484 let bound_size = table_data.current_elements_size;
1485 self.masm.load(bound_addr, writable!(bound), bound_size)?;
1486 self.masm.cmp(index, bound.into(), bound_size)?;
1487 self.masm
1488 .trapif(IntCmpKind::GeU, TRAP_TABLE_OUT_OF_BOUNDS)?;
1489
1490 self.masm.with_scratch::<IntScratch, _>(|masm, scratch| {
1495 masm.mov(scratch.writable(), index.into(), bound_size)?;
1496 masm.mul(
1497 scratch.writable(),
1498 scratch.inner(),
1499 RegImm::i32(table_data.element_size.bytes() as i32),
1500 table_data.element_size,
1501 )?;
1502 masm.load_ptr(
1503 masm.address_at_reg(base, table_data.offset)?,
1504 writable!(base),
1505 )?;
1506 masm.mov(writable!(tmp), base.into(), ptr_size)?;
1509 masm.add(writable!(base), base, scratch.inner().into(), ptr_size)
1511 })?;
1512 if self.env.table_access_spectre_mitigation() {
1513 self.masm.cmp(index, bound.into(), bound_size)?;
1516 self.masm
1517 .cmov(writable!(base), tmp, IntCmpKind::GeU, ptr_size)?;
1518 }
1519 self.context.free_reg(bound);
1520 self.context.free_reg(tmp);
1521 self.masm.address_at_reg(base, 0)
1522 }
1523
1524 pub fn emit_compute_table_size(&mut self, table_data: &TableData) -> Result<()> {
1526 let size = self.context.any_gpr(self.masm)?;
1527 let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
1528
1529 self.masm.with_scratch::<IntScratch, _>(|masm, scratch| {
1530 if let Some(offset) = table_data.import_from {
1531 masm.load_ptr(masm.address_at_vmctx(offset)?, scratch.writable())?;
1532 } else {
1533 masm.mov(scratch.writable(), vmctx!(M).into(), ptr_size)?;
1534 };
1535
1536 let size_addr =
1537 masm.address_at_reg(scratch.inner(), table_data.current_elems_offset)?;
1538 masm.load(size_addr, writable!(size), table_data.current_elements_size)
1539 })?;
1540
1541 let dst = TypedReg::new(table_data.index_type(), size);
1542 self.context.stack.push(dst.into());
1543 Ok(())
1544 }
1545
1546 fn load_memory_length(&mut self, heap_data: &HeapData, size_reg: Reg) -> Result<()> {
1548 self.masm.with_scratch::<IntScratch, _>(|masm, scratch| {
1549 let base = if let Some(offset) = heap_data.import_from {
1550 masm.load_ptr(masm.address_at_vmctx(offset)?, scratch.writable())?;
1551 scratch.inner()
1552 } else {
1553 vmctx!(M)
1554 };
1555
1556 let size_addr = masm.address_at_reg(base, heap_data.current_length_offset)?;
1557 masm.load_ptr(size_addr, writable!(size_reg))
1558 })?;
1559 Ok(())
1560 }
1561
1562 pub fn emit_compute_memory_size(&mut self, heap_data: &HeapData) -> Result<()> {
1564 let size_reg = self.context.any_gpr(self.masm)?;
1565 self.load_memory_length(heap_data, size_reg)?;
1566
1567 let dst = TypedReg::new(heap_data.index_type(), size_reg);
1569 let pow = heap_data.memory.page_size_log2;
1570 self.masm.shift_ir(
1571 writable!(dst.reg),
1572 Imm::i32(pow as i32),
1573 dst.into(),
1574 ShiftKind::ShrU,
1575 self.env.ptr_type().try_into()?,
1576 )?;
1577 self.context.stack.push(dst.into());
1578 Ok(())
1579 }
1580
1581 fn emit_bounds_check_and_compute_addr(
1584 &mut self,
1585 heap: &HeapData,
1586 dst: Reg,
1587 ptr: Reg,
1588 len: Reg,
1589 ) -> Result<()> {
1590 let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
1591 let idx_size: OperandSize = heap.index_type().try_into()?;
1592 match idx_size {
1596 OperandSize::S32 => {
1597 self.masm
1598 .extend(writable!(dst), ptr, Extend::<Zero>::I64Extend32.into())?;
1599 self.masm.add_uextend(
1600 writable!(dst),
1601 dst,
1602 len,
1603 OperandSize::S32,
1604 OperandSize::S64,
1605 )?;
1606 }
1607 OperandSize::S64 => {
1608 self.masm
1609 .mov(writable!(dst), ptr.into(), OperandSize::S64)?;
1610 self.masm.checked_uadd(
1611 writable!(dst),
1612 dst,
1613 len.into(),
1614 OperandSize::S64,
1615 TrapCode::HEAP_OUT_OF_BOUNDS,
1616 )?;
1617 }
1618 _ => unreachable!(),
1619 }
1620
1621 let size_in_bytes = self.context.any_gpr(self.masm)?;
1624 self.load_memory_length(&heap, size_in_bytes)?;
1625 assert!(ptr_size == OperandSize::S64);
1626 self.masm.cmp(dst, size_in_bytes.into(), ptr_size)?;
1627 self.masm
1628 .trapif(IntCmpKind::GtU, TrapCode::HEAP_OUT_OF_BOUNDS)?;
1629 self.context.free_reg(size_in_bytes);
1630
1631 bounds::load_heap_addr_unchecked(
1633 self.masm,
1634 &heap,
1635 Index::from_typed_reg(TypedReg::new(heap.index_type(), ptr)),
1636 ImmOffset::from_u32(0),
1637 dst,
1638 ptr_size,
1639 )?;
1640 Ok(())
1641 }
1642
1643 pub fn emit_memory_copy(&mut self, dst_mem: MemoryIndex, src_mem: MemoryIndex) -> Result<()> {
1645 let dst_heap = self.env.resolve_heap(dst_mem);
1646 let src_heap = self.env.resolve_heap(src_mem);
1647 let dst_idx_size: OperandSize = dst_heap.index_type().try_into()?;
1648 let src_idx_size: OperandSize = src_heap.index_type().try_into()?;
1649
1650 let len = self.context.pop_to_reg(self.masm, None)?;
1651 let src = self.context.pop_to_reg(self.masm, None)?;
1652 let dst = self.context.pop_to_reg(self.masm, None)?;
1653
1654 if dst_idx_size == OperandSize::S32 || src_idx_size == OperandSize::S32 {
1660 self.masm.extend(
1661 writable!(len.reg),
1662 len.reg,
1663 Extend::<Zero>::I64Extend32.into(),
1664 )?;
1665 }
1666
1667 let dst_raw_addr = self.context.any_gpr(self.masm)?;
1668 self.emit_bounds_check_and_compute_addr(&dst_heap, dst_raw_addr, dst.reg, len.reg)?;
1669 self.context.free_reg(dst);
1670
1671 let src_raw_addr = self.context.any_gpr(self.masm)?;
1672 self.emit_bounds_check_and_compute_addr(&src_heap, src_raw_addr, src.reg, len.reg)?;
1673 self.context.free_reg(src);
1674
1675 self.context
1676 .stack
1677 .push(TypedReg::new(self.env.ptr_type(), dst_raw_addr).into());
1678 self.context
1679 .stack
1680 .push(TypedReg::new(self.env.ptr_type(), src_raw_addr).into());
1681 self.context
1682 .stack
1683 .push(TypedReg::new(self.env.ptr_type(), len.reg).into());
1684
1685 let builtin = self.env.builtins.memory_copy::<M::ABI>()?;
1686 FnCall::emit::<M>(
1687 &mut self.env,
1688 self.masm,
1689 &mut self.context,
1690 Callee::Builtin(builtin),
1691 )?;
1692 Ok(())
1693 }
1694
1695 pub fn emit_memory_fill(&mut self, mem: MemoryIndex) -> Result<()> {
1697 let heap = self.env.resolve_heap(mem);
1698 let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
1699 let idx_size: OperandSize = heap.index_type().try_into()?;
1700
1701 let len = self.context.pop_to_reg(self.masm, None)?;
1703 let val = self.context.pop_to_reg(self.masm, None)?;
1704 let dst = self.context.pop_to_reg(self.masm, None)?;
1705
1706 let raw_addr = self.context.any_gpr(self.masm)?;
1707 self.emit_bounds_check_and_compute_addr(&heap, raw_addr, dst.reg, len.reg)?;
1708 self.context.free_reg(dst);
1709
1710 let len_reg = len.reg;
1713 if idx_size == OperandSize::S32 && ptr_size == OperandSize::S64 {
1714 self.masm.extend(
1715 writable!(len_reg),
1716 len_reg,
1717 Extend::<Zero>::I64Extend32.into(),
1718 )?;
1719 }
1720
1721 self.context
1723 .stack
1724 .push(TypedReg::new(self.env.ptr_type(), raw_addr).into());
1725 self.context.stack.push(val.into());
1726 self.context
1727 .stack
1728 .push(TypedReg::new(self.env.ptr_type(), len_reg).into());
1729
1730 let builtin = self.env.builtins.memory_fill::<M::ABI>()?;
1731 FnCall::emit::<M>(
1732 &mut self.env,
1733 self.masm,
1734 &mut self.context,
1735 Callee::Builtin(builtin),
1736 )?;
1737 Ok(())
1738 }
1739
1740 pub fn emit_memory_init(&mut self, segment: DataIndex, mem: MemoryIndex) -> Result<()> {
1742 let dst_heap = self.env.resolve_heap(mem);
1743
1744 let len = self.context.pop_to_reg(self.masm, None)?;
1745 let src = self.context.pop_to_reg(self.masm, None)?;
1746 let dst = self.context.pop_to_reg(self.masm, None)?;
1747
1748 self.masm.extend(
1754 writable!(len.reg),
1755 len.reg,
1756 Extend::<Zero>::I64Extend32.into(),
1757 )?;
1758
1759 let dst_raw_addr = self.context.any_gpr(self.masm)?;
1760 self.emit_bounds_check_and_compute_addr(&dst_heap, dst_raw_addr, dst.reg, len.reg)?;
1761 self.context.free_reg(dst);
1762
1763 let runtime_data_index = match self.env.translation.runtime_data_map[segment] {
1764 Some(i) => i,
1765
1766 None => {
1769 self.masm.cmp(src.reg, RegImm::i32(0), OperandSize::S32)?;
1770 self.masm
1771 .trapif(IntCmpKind::Ne, TrapCode::HEAP_OUT_OF_BOUNDS)?;
1772 self.masm.cmp(len.reg, RegImm::i32(0), OperandSize::S32)?;
1773 self.masm
1774 .trapif(IntCmpKind::Ne, TrapCode::HEAP_OUT_OF_BOUNDS)?;
1775 self.context.free_reg(dst_raw_addr);
1776 self.context.free_reg(src);
1777 self.context.free_reg(len);
1778 return Ok(());
1779 }
1780 };
1781
1782 let data_segment_length_offset = self
1786 .env
1787 .vmoffsets
1788 .vmctx_runtime_data_length(runtime_data_index);
1789 let tmp1 = self.context.any_gpr(self.masm)?;
1790 let tmp2 = self.context.any_gpr(self.masm)?;
1791 self.masm.load(
1792 self.masm.address_at_vmctx(data_segment_length_offset)?,
1793 writable!(tmp1),
1794 OperandSize::S32,
1795 )?;
1796 self.masm
1797 .mov(writable!(tmp2), src.reg.into(), OperandSize::S32)?;
1798 self.masm.checked_uadd(
1799 writable!(tmp2),
1800 tmp2,
1801 len.reg.into(),
1802 OperandSize::S32,
1803 TrapCode::HEAP_OUT_OF_BOUNDS,
1804 )?;
1805 self.masm.cmp(tmp2, tmp1.into(), OperandSize::S32)?;
1806 self.masm
1807 .trapif(IntCmpKind::GtU, TrapCode::HEAP_OUT_OF_BOUNDS)?;
1808 self.context.free_reg(tmp2);
1809
1810 let data_segment_base_offset = self
1813 .env
1814 .vmoffsets
1815 .vmctx_runtime_data_base(runtime_data_index);
1816 self.masm.load(
1817 self.masm.address_at_vmctx(data_segment_base_offset)?,
1818 writable!(tmp1),
1819 OperandSize::S64,
1820 )?;
1821 self.masm.add_uextend(
1822 writable!(tmp1),
1823 tmp1,
1824 src.reg,
1825 OperandSize::S32,
1826 OperandSize::S64,
1827 )?;
1828 self.context.free_reg(src);
1829
1830 self.context.stack.push(TypedReg::i64(dst_raw_addr).into());
1832 self.context.stack.push(TypedReg::i64(tmp1).into());
1833 self.context.stack.push(len.into());
1834 let builtin = self.env.builtins.memory_copy::<M::ABI>()?;
1835 FnCall::emit::<M>(
1836 &mut self.env,
1837 self.masm,
1838 &mut self.context,
1839 Callee::Builtin(builtin),
1840 )?;
1841 Ok(())
1842 }
1843
1844 pub fn emit_data_drop(&mut self, data_index: DataIndex) -> Result<()> {
1845 let runtime_data_index = match self.env.translation.runtime_data_map[data_index] {
1846 Some(idx) => idx,
1847 None => return Ok(()),
1849 };
1850 let data_segment_offset = self
1851 .env
1852 .vmoffsets
1853 .vmctx_runtime_data_length(runtime_data_index);
1854 let len_addr = self.masm.address_at_vmctx(data_segment_offset)?;
1855 self.masm.store(RegImm::i32(0), len_addr, OperandSize::S32)
1856 }
1857
1858 pub fn emit_table_init(
1860 &mut self,
1861 elem_index: ElemIndex,
1862 table_index: TableIndex,
1863 ) -> Result<()> {
1864 let builtin_base = self.env.builtins.passive_elem_segment_base::<M::ABI>()?;
1865 let builtin_len = self.env.builtins.passive_elem_segment_len::<M::ABI>()?;
1866
1867 match self.env.translation.passive_elem_map[elem_index] {
1869 Some(idx) => {
1870 self.context.stack.extend([idx.as_u32().try_into()?]);
1871 FnCall::emit::<M>(
1872 &mut self.env,
1873 self.masm,
1874 &mut self.context,
1875 Callee::Builtin(builtin_len),
1876 )?;
1877 self.context.stack.extend([idx.as_u32().try_into()?]);
1878 FnCall::emit::<M>(
1879 &mut self.env,
1880 self.masm,
1881 &mut self.context,
1882 Callee::Builtin(builtin_base),
1883 )?;
1884 }
1885 None => {
1887 let tmp = self.context.any_gpr(self.masm)?;
1888 self.masm
1889 .mov(writable!(tmp), RegImm::i64(0), OperandSize::S64)?;
1890 self.context
1891 .stack
1892 .push(TypedReg::new(WasmValType::I64, tmp).into());
1893
1894 let tmp = self.context.any_gpr(self.masm)?;
1895 self.masm
1896 .mov(writable!(tmp), RegImm::i64(0), OperandSize::S64)?;
1897 self.context
1898 .stack
1899 .push(TypedReg::new(WasmValType::I64, tmp).into());
1900 }
1901 };
1902
1903 let table_data = self.env.resolve_table_data(table_index);
1905 let idx_size = table_data.index_type().try_into()?;
1906 self.emit_compute_table_size(&table_data)?;
1907
1908 let table_size = self.context.pop_to_reg(self.masm, None)?;
1911 let segment_base = self.context.pop_to_reg(self.masm, None)?;
1912 let segment_len = self.context.pop_to_reg(self.masm, None)?;
1913 let len = self.context.pop_to_reg(self.masm, None)?;
1914 let segment_off = self.context.pop_to_reg(self.masm, None)?;
1915 let table_off = self.context.pop_to_reg(self.masm, None)?;
1916
1917 if len.ty == WasmValType::I32 {
1920 self.masm.extend(
1921 writable!(len.reg),
1922 len.reg,
1923 Extend::<Zero>::I64Extend32.into(),
1924 )?;
1925 }
1926
1927 let tmp = self.context.any_gpr(self.masm)?;
1929 {
1930 self.masm
1931 .mov(writable!(tmp), segment_off.reg.into(), OperandSize::S32)?;
1932 self.masm.checked_uadd(
1933 writable!(tmp),
1934 tmp,
1935 len.reg.into(),
1936 OperandSize::S32,
1937 TRAP_TABLE_OUT_OF_BOUNDS,
1938 )?;
1939 self.masm
1940 .cmp(tmp, segment_len.reg.into(), OperandSize::S32)?;
1941 self.masm
1942 .trapif(IntCmpKind::GtU, TRAP_TABLE_OUT_OF_BOUNDS)?;
1943 self.context.free_reg(segment_len);
1944 }
1945
1946 {
1948 self.masm
1949 .mov(writable!(tmp), table_off.reg.into(), idx_size)?;
1950 self.masm.checked_uadd(
1951 writable!(tmp),
1952 tmp,
1953 len.reg.into(),
1954 idx_size,
1955 TRAP_TABLE_OUT_OF_BOUNDS,
1956 )?;
1957 self.masm.cmp(tmp, table_size.reg.into(), idx_size)?;
1958 self.masm
1959 .trapif(IntCmpKind::GtU, TRAP_TABLE_OUT_OF_BOUNDS)?;
1960 self.context.free_reg(table_size);
1961 }
1962 self.context.free_reg(tmp);
1963
1964 {
1966 self.masm.extend(
1967 writable!(segment_off.reg),
1968 segment_off.reg,
1969 Extend::<Zero>::I64Extend32.into(),
1970 )?;
1971 self.masm.mul(
1972 writable!(segment_off.reg),
1973 segment_off.reg,
1974 RegImm::i64(16),
1975 OperandSize::S64,
1976 )?;
1977 self.masm.add(
1978 writable!(segment_base.reg),
1979 segment_base.reg,
1980 segment_off.reg.into(),
1981 OperandSize::S64,
1982 )?;
1983 self.context.free_reg(segment_off);
1984 }
1985
1986 let header = self.masm.get_label()?;
1989 let exit = self.masm.get_label()?;
1990
1991 self.masm.bind(header)?;
1992 {
1993 self.masm.branch(
1994 IntCmpKind::Eq,
1995 len.reg,
1996 RegImm::i64(0),
1997 exit,
1998 OperandSize::S64,
1999 )?;
2000
2001 let funcref = self.context.any_gpr(self.masm)?;
2004 self.masm.load_ptr(
2005 self.masm.address_at_reg(segment_base.reg, 0)?,
2006 writable!(funcref),
2007 )?;
2008 self.masm.add(
2009 writable!(segment_base.reg),
2010 segment_base.reg,
2011 RegImm::i64(16),
2012 OperandSize::S64,
2013 )?;
2014
2015 self.context.stack.push(segment_base.into());
2019 self.context.stack.push(len.into());
2020 let table_off_copy = self.context.any_gpr(self.masm)?;
2021 self.masm.mov(
2022 writable!(table_off_copy),
2023 table_off.reg.into(),
2024 table_off.ty.try_into()?,
2025 )?;
2026 self.context.stack.push(table_off.into());
2027 self.context
2028 .stack
2029 .push(TypedReg::new(table_off.ty, table_off_copy).into());
2030 self.context
2031 .stack
2032 .push(TypedReg::new(WasmValType::FUNCREF, funcref).into());
2033 self.emit_table_set(table_index)?;
2034
2035 self.context.pop_to_reg(self.masm, Some(table_off.reg))?;
2037 self.context.pop_to_reg(self.masm, Some(len.reg))?;
2038 self.context.pop_to_reg(self.masm, Some(segment_base.reg))?;
2039
2040 self.masm.add(
2042 writable!(table_off.reg),
2043 table_off.reg,
2044 RegImm::i64(1),
2045 table_off.ty.try_into()?,
2046 )?;
2047
2048 self.masm.sub(
2051 writable!(len.reg),
2052 len.reg,
2053 RegImm::i64(1),
2054 OperandSize::S64,
2055 )?;
2056 }
2057 self.masm.jmp(header)?;
2058
2059 self.masm.bind(exit)?;
2060
2061 self.context.free_reg(segment_base);
2062 self.context.free_reg(len);
2063 self.context.free_reg(table_off);
2064 Ok(())
2065 }
2066
2067 pub fn emit_elem_drop(&mut self, elem_index: ElemIndex) -> Result<()> {
2069 let passive_elem_index = match self.env.translation.passive_elem_map[elem_index] {
2070 Some(idx) => idx,
2071 None => return Ok(()),
2073 };
2074 let builtin = self.env.builtins.passive_elem_segment_drop::<M::ABI>()?;
2075 self.context
2076 .stack
2077 .extend([passive_elem_index.as_u32().try_into()?]);
2078 FnCall::emit::<M>(
2079 &mut self.env,
2080 self.masm,
2081 &mut self.context,
2082 Callee::Builtin(builtin),
2083 )?;
2084 self.context.pop_and_free(self.masm)
2085 }
2086
2087 pub fn maybe_emit_fuel_check(&mut self) -> Result<()> {
2091 if !self.tunables.consume_fuel {
2092 return Ok(());
2093 }
2094
2095 self.emit_fuel_increment()?;
2096 let out_of_fuel = self.env.builtins.out_of_gas::<M::ABI>()?;
2097 let fuel_reg = self.context.without::<Result<Reg>, M, _>(
2098 &out_of_fuel.sig().regs,
2099 self.masm,
2100 |cx, masm| cx.any_gpr(masm),
2101 )??;
2102
2103 self.emit_load_fuel_consumed(fuel_reg)?;
2104
2105 let continuation = self.masm.get_label()?;
2107
2108 self.context.spill(self.masm)?;
2111 self.masm.branch(
2114 IntCmpKind::LtS,
2115 fuel_reg,
2116 RegImm::i64(0),
2117 continuation,
2118 OperandSize::S64,
2119 )?;
2120 FnCall::emit::<M>(
2122 &mut self.env,
2123 self.masm,
2124 &mut self.context,
2125 Callee::Builtin(out_of_fuel.clone()),
2126 )?;
2127 self.context.pop_and_free(self.masm)?;
2128
2129 self.masm.bind(continuation)?;
2131 self.context.free_reg(fuel_reg);
2132
2133 Ok(())
2134 }
2135
2136 fn emit_load_fuel_consumed(&mut self, fuel_reg: Reg) -> Result<()> {
2139 let store_context_offset = self.env.vmoffsets.ptr.vmctx_store_context();
2140 let fuel_offset = self.env.vmoffsets.ptr.vm_store_context().fuel_consumed();
2141 self.masm.load_ptr(
2142 self.masm
2143 .address_at_vmctx(u32::from(store_context_offset))?,
2144 writable!(fuel_reg),
2145 )?;
2146
2147 self.masm.load(
2148 self.masm.address_at_reg(fuel_reg, u32::from(fuel_offset))?,
2149 writable!(fuel_reg),
2150 OperandSize::S64,
2152 )
2153 }
2154
2155 pub fn maybe_emit_epoch_check(&mut self) -> Result<()> {
2158 if !self.tunables.epoch_interruption {
2159 return Ok(());
2160 }
2161
2162 let cont = self.masm.get_label()?;
2165 let new_epoch = self.env.builtins.new_epoch::<M::ABI>()?;
2166
2167 let (epoch_deadline_reg, epoch_counter_reg) =
2175 self.context.without::<Result<(Reg, Reg)>, M, _>(
2176 &new_epoch.sig().regs,
2177 self.masm,
2178 |cx, masm| Ok((cx.any_gpr(masm)?, cx.any_gpr(masm)?)),
2179 )??;
2180
2181 self.emit_load_epoch_deadline_and_counter(epoch_deadline_reg, epoch_counter_reg)?;
2182
2183 self.context.spill(self.masm)?;
2186 self.masm.branch(
2187 IntCmpKind::LtU,
2188 epoch_counter_reg,
2189 RegImm::reg(epoch_deadline_reg),
2190 cont,
2191 OperandSize::S64,
2192 )?;
2193 FnCall::emit::<M>(
2195 &mut self.env,
2196 self.masm,
2197 &mut self.context,
2198 Callee::Builtin(new_epoch.clone()),
2199 )?;
2200 self.visit_drop()?;
2203
2204 self.masm.bind(cont)?;
2206
2207 self.context.free_reg(epoch_deadline_reg);
2208 self.context.free_reg(epoch_counter_reg);
2209 Ok(())
2210 }
2211
2212 fn emit_load_epoch_deadline_and_counter(
2213 &mut self,
2214 epoch_deadline_reg: Reg,
2215 epoch_counter_reg: Reg,
2216 ) -> Result<()> {
2217 let epoch_ptr_offset = self.env.vmoffsets.ptr.vmctx_epoch_ptr();
2218 let store_context_offset = self.env.vmoffsets.ptr.vmctx_store_context();
2219 let epoch_deadline_offset = self.env.vmoffsets.ptr.vm_store_context().epoch_deadline();
2220
2221 self.masm.load_ptr(
2223 self.masm.address_at_vmctx(u32::from(epoch_ptr_offset))?,
2224 writable!(epoch_counter_reg),
2225 )?;
2226
2227 self.masm.load(
2230 self.masm.address_at_reg(epoch_counter_reg, 0)?,
2231 writable!(epoch_counter_reg),
2232 OperandSize::S64,
2233 )?;
2234
2235 self.masm.load_ptr(
2237 self.masm
2238 .address_at_vmctx(u32::from(store_context_offset))?,
2239 writable!(epoch_deadline_reg),
2240 )?;
2241
2242 self.masm.load(
2243 self.masm
2244 .address_at_reg(epoch_deadline_reg, u32::from(epoch_deadline_offset))?,
2245 writable!(epoch_deadline_reg),
2246 OperandSize::S64,
2248 )
2249 }
2250
2251 fn emit_fuel_increment(&mut self) -> Result<()> {
2254 let fuel_at_point = std::mem::replace(&mut self.fuel_consumed, 0);
2255 if fuel_at_point == 0 {
2256 return Ok(());
2257 }
2258
2259 let store_context_offset = self.env.vmoffsets.ptr.vmctx_store_context();
2260 let fuel_offset = self.env.vmoffsets.ptr.vm_store_context().fuel_consumed();
2261 let limits_reg = self.context.any_gpr(self.masm)?;
2262
2263 self.masm.load_ptr(
2265 self.masm
2266 .address_at_vmctx(u32::from(store_context_offset))?,
2267 writable!(limits_reg),
2268 )?;
2269
2270 self.masm.with_scratch::<IntScratch, _>(|masm, scratch| {
2271 masm.load(
2273 masm.address_at_reg(limits_reg, u32::from(fuel_offset))?,
2274 scratch.writable(),
2275 OperandSize::S64,
2276 )?;
2277
2278 masm.add(
2281 scratch.writable(),
2282 scratch.inner(),
2283 RegImm::i64(fuel_at_point),
2284 OperandSize::S64,
2285 )?;
2286
2287 masm.store(
2289 scratch.inner().into(),
2290 masm.address_at_reg(limits_reg, u32::from(fuel_offset))?,
2291 OperandSize::S64,
2292 )
2293 })?;
2294
2295 self.context.free_reg(limits_reg);
2296
2297 Ok(())
2298 }
2299
2300 fn fuel_before_visit_op(&mut self, op: &Operator) -> Result<()> {
2302 if !self.context.reachable {
2303 ensure!(self.fuel_consumed == 0, CodeGenError::illegal_fuel_state())
2306 }
2307
2308 self.fuel_consumed += self.tunables.operator_cost.cost(op);
2325
2326 match op {
2327 Operator::Unreachable
2328 | Operator::Loop { .. }
2329 | Operator::If { .. }
2330 | Operator::Else { .. }
2331 | Operator::Br { .. }
2332 | Operator::BrIf { .. }
2333 | Operator::BrTable { .. }
2334 | Operator::End
2335 | Operator::Return
2336 | Operator::CallIndirect { .. }
2337 | Operator::Call { .. }
2338 | Operator::ReturnCall { .. }
2339 | Operator::ReturnCallIndirect { .. } => self.emit_fuel_increment(),
2340 _ => Ok(()),
2341 }
2342 }
2343
2344 fn source_location_before_visit_op(&mut self, offset: usize) -> Result<()> {
2346 let loc = SourceLoc::new(offset as u32);
2347 let rel = self.source_loc_from(loc);
2348 self.source_location.current = self.masm.start_source_loc(rel)?;
2349 Ok(())
2350 }
2351
2352 fn source_location_after_visit_op(&mut self) -> Result<()> {
2354 if self.masm.current_code_offset()? >= self.source_location.current.0 {
2361 self.masm.end_source_loc()?;
2362 }
2363
2364 Ok(())
2365 }
2366
2367 pub(crate) fn emit_atomic_rmw(
2368 &mut self,
2369 arg: &MemArg,
2370 op: RmwOp,
2371 size: OperandSize,
2372 extend: Option<Extend<Zero>>,
2373 ) -> Result<()> {
2374 let memory_index = MemoryIndex::from_u32(arg.memory);
2375 let heap = self.env.resolve_heap(memory_index);
2376 let operand = self.context.pop_to_reg(self.masm, None)?;
2385 if let Some(addr) = self.emit_compute_heap_address_align_checked(&heap, arg, size)? {
2386 self.context
2387 .stack
2388 .push(TypedReg::new(self.env.ptr_type(), addr).into());
2389 self.context.stack.push(operand.into());
2390 self.masm
2391 .atomic_rmw(&mut self.context, size, op, UNTRUSTED_FLAGS, extend)?;
2392 } else {
2393 self.context.free_reg(operand);
2396 }
2397
2398 Ok(())
2399 }
2400
2401 pub(crate) fn emit_atomic_cmpxchg(
2402 &mut self,
2403 arg: &MemArg,
2404 size: OperandSize,
2405 extend: Option<Extend<Zero>>,
2406 ) -> Result<()> {
2407 let replacement = self.context.pop_to_reg(self.masm, None)?;
2430 let expected = self.context.pop_to_reg(self.masm, None)?;
2431
2432 let memory_index = MemoryIndex::from_u32(arg.memory);
2433 let heap = self.env.resolve_heap(memory_index);
2434 if let Some(addr) = self.emit_compute_heap_address_align_checked(&heap, arg, size)? {
2435 self.context
2436 .stack
2437 .push(TypedReg::new(self.env.ptr_type(), addr).into());
2438 self.context.stack.push(expected.into());
2439 self.context.stack.push(replacement.into());
2440
2441 self.masm
2442 .atomic_cas(&mut self.context, size, UNTRUSTED_FLAGS, extend)?;
2443 } else {
2444 self.context.free_reg(expected);
2447 self.context.free_reg(replacement);
2448 }
2449 Ok(())
2450 }
2451
2452 pub fn emit_atomic_wait(&mut self, arg: &MemArg, kind: AtomicWaitKind) -> Result<()> {
2454 let timeout = self.context.pop_to_reg(self.masm, None)?;
2467 let expected = self.context.pop_to_reg(self.masm, None)?;
2468 let addr = self.context.pop_to_reg(self.masm, None)?;
2469
2470 let stack_len = self.context.stack.len();
2472 let builtin = match kind {
2473 AtomicWaitKind::Wait32 => self.env.builtins.memory_atomic_wait32::<M::ABI>()?,
2474 AtomicWaitKind::Wait64 => self.env.builtins.memory_atomic_wait64::<M::ABI>()?,
2475 };
2476 let builtin = self.prepare_builtin_defined_memory_arg(
2477 MemoryIndex::from_u32(arg.memory),
2478 stack_len,
2479 builtin,
2480 )?;
2481
2482 if arg.offset != 0 {
2483 self.masm.checked_uadd(
2484 writable!(addr.reg),
2485 addr.reg,
2486 RegImm::i64(arg.offset as i64),
2487 OperandSize::S64,
2488 TrapCode::HEAP_OUT_OF_BOUNDS,
2489 )?;
2490 }
2491
2492 self.context
2493 .stack
2494 .push(TypedReg::new(WasmValType::I64, addr.reg).into());
2495 self.context.stack.push(expected.into());
2496 self.context.stack.push(timeout.into());
2497
2498 FnCall::emit::<M>(&mut self.env, self.masm, &mut self.context, builtin)?;
2499
2500 Ok(())
2501 }
2502
2503 pub fn emit_atomic_notify(&mut self, arg: &MemArg) -> Result<()> {
2504 let count = self.context.pop_to_reg(self.masm, None)?;
2516 let addr = self.context.pop_to_reg(self.masm, None)?;
2517
2518 let builtin = self.env.builtins.memory_atomic_notify::<M::ABI>()?;
2520 let stack_len = self.context.stack.len();
2521 let builtin = self.prepare_builtin_defined_memory_arg(
2522 MemoryIndex::from_u32(arg.memory),
2523 stack_len,
2524 builtin,
2525 )?;
2526
2527 if arg.offset != 0 {
2528 self.masm.checked_uadd(
2529 writable!(addr.reg),
2530 addr.reg,
2531 RegImm::i64(arg.offset as i64),
2532 OperandSize::S64,
2533 TrapCode::HEAP_OUT_OF_BOUNDS,
2534 )?;
2535 }
2536
2537 self.context
2539 .stack
2540 .push(TypedReg::new(WasmValType::I64, addr.reg).into());
2541 self.context.stack.push(count.into());
2542
2543 FnCall::emit::<M>(&mut self.env, self.masm, &mut self.context, builtin)?;
2544
2545 Ok(())
2546 }
2547
2548 pub fn prepare_builtin_defined_memory_arg(
2549 &mut self,
2550 mem: MemoryIndex,
2551 defined_index_at: usize,
2552 builtin: BuiltinFunction,
2553 ) -> Result<Callee> {
2554 match self.env.translation.module.defined_memory_index(mem) {
2555 Some(defined) => {
2558 self.context
2559 .stack
2560 .insert_many(defined_index_at, &[defined.as_u32().try_into()?]);
2561 Ok(Callee::Builtin(builtin))
2562 }
2563
2564 None => {
2568 let vmimport = self.env.vmoffsets.vmctx_vmmemory_import(mem);
2569 let vmctx_offset =
2570 vmimport + u32::from(self.env.vmoffsets.ptr.vm_memory_import().vmctx());
2571 let index_offset =
2572 vmimport + u32::from(self.env.vmoffsets.ptr.vm_memory_import().index());
2573 let index_addr = self.masm.address_at_vmctx(index_offset)?;
2574 let index_dst = self.context.reg_for_class(RegClass::Int, self.masm)?;
2575 self.masm
2576 .load(index_addr, writable!(index_dst), OperandSize::S32)?;
2577 self.context
2578 .stack
2579 .insert_many(defined_index_at, &[Val::reg(index_dst, WasmValType::I32)]);
2580 Ok(Callee::BuiltinWithDifferentVmctx(builtin, vmctx_offset))
2581 }
2582 }
2583 }
2584
2585 pub fn prepare_builtin_defined_table_arg(
2587 &mut self,
2588 table: TableIndex,
2589 defined_index_at: usize,
2590 builtin: BuiltinFunction,
2591 ) -> Result<Callee> {
2592 match self.env.translation.module.defined_table_index(table) {
2593 Some(defined) => {
2594 self.context
2595 .stack
2596 .insert_many(defined_index_at, &[defined.as_u32().try_into()?]);
2597 Ok(Callee::Builtin(builtin))
2598 }
2599 None => {
2600 let vmimport = self.env.vmoffsets.vmctx_vmtable_import(table);
2601 let vmctx_offset =
2602 vmimport + u32::from(self.env.vmoffsets.ptr.vm_table_import().vmctx());
2603 let index_offset =
2604 vmimport + u32::from(self.env.vmoffsets.ptr.vm_table_import().index());
2605 let index_addr = self.masm.address_at_vmctx(index_offset)?;
2606 let index_dst = self.context.reg_for_class(RegClass::Int, self.masm)?;
2607 self.masm
2608 .load(index_addr, writable!(index_dst), OperandSize::S32)?;
2609 self.context
2610 .stack
2611 .insert_many(defined_index_at, &[Val::reg(index_dst, WasmValType::I32)]);
2612 Ok(Callee::BuiltinWithDifferentVmctx(builtin, vmctx_offset))
2613 }
2614 }
2615 }
2616}
2617
2618pub fn control_index(depth: u32, control_length: usize) -> Result<usize> {
2621 (control_length - 1)
2622 .checked_sub(depth as usize)
2623 .ok_or_else(|| format_err!(CodeGenError::control_frame_expected()))
2624}