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, WasmFeatures,
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, wasm_unsupported,
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;
43mod drc;
44mod exceptions;
45pub(crate) use exceptions::{CatchInfo, TryTableInfo};
46mod gc;
47use gc::GcCodegenConfig;
48
49use bounds::{Bounds, ImmOffset, Index};
50
51mod phase;
52pub(crate) use phase::*;
53
54mod error;
55pub(crate) use error::*;
56
57pub(crate) trait BranchState {
60 fn unreachable_state_after_emission() -> bool;
63}
64
65pub(crate) struct ConditionalBranch;
67
68impl BranchState for ConditionalBranch {
69 fn unreachable_state_after_emission() -> bool {
70 false
71 }
72}
73
74pub(crate) struct UnconditionalBranch;
76
77impl BranchState for UnconditionalBranch {
78 fn unreachable_state_after_emission() -> bool {
79 true
80 }
81}
82
83#[derive(Default)]
87pub(crate) struct SourceLocation {
88 pub base: Option<SourceLoc>,
90 pub current: (CodeOffset, RelSourceLoc),
93}
94
95pub(crate) struct CodeGen<'a, 'translation: 'a, 'data: 'translation, M, P>
97where
98 M: MacroAssembler,
99 P: CodeGenPhase,
100{
101 pub sig: ABISig,
103
104 pub context: CodeGenContext<'a, P>,
106
107 pub env: FuncEnv<'a, 'translation, 'data, M::Ptr>,
109
110 pub masm: &'a mut M,
112
113 pub control_frames: SmallVec<[ControlStackFrame; 64]>,
117
118 pub source_location: SourceLocation,
120
121 pub tunables: &'a Tunables,
123
124 pub fuel_consumed: i64,
126
127 pub needs_gc_heap: bool,
129
130 gc_codegen_config: Option<GcCodegenConfig>,
132 phase: PhantomData<P>,
133}
134
135impl<'a, 'translation, 'data, M> CodeGen<'a, 'translation, 'data, M, Prologue>
136where
137 M: MacroAssembler,
138{
139 pub fn new(
140 tunables: &'a Tunables,
141 masm: &'a mut M,
142 context: CodeGenContext<'a, Prologue>,
143 env: FuncEnv<'a, 'translation, 'data, M::Ptr>,
144 sig: ABISig,
145 wasm_features: &WasmFeatures,
146 ) -> Result<CodeGen<'a, 'translation, 'data, M, Prologue>> {
147 let gc_codegen_config = match tunables.collector {
148 Some(collector) => Some(GcCodegenConfig::new(collector)),
149 None if wasm_features.contains(WasmFeatures::EXCEPTIONS) => {
150 return Err(format_err!(wasm_unsupported!(
151 "support for GC types disabled at configuration time"
152 )));
153 }
154 None => None,
155 };
156
157 Ok(Self {
158 sig,
159 context,
160 masm,
161 env,
162 tunables,
163 source_location: Default::default(),
164 control_frames: Default::default(),
165 fuel_consumed: 1,
167 needs_gc_heap: false,
168 gc_codegen_config,
169 phase: PhantomData,
170 })
171 }
172
173 pub fn emit_prologue(mut self) -> Result<CodeGen<'a, 'translation, 'data, M, Emission>> {
175 let vmctx = self
176 .sig
177 .params()
178 .first()
179 .ok_or_else(|| format_err!(CodeGenError::vmcontext_arg_expected()))?
180 .unwrap_reg();
181
182 self.masm.start_source_loc(Default::default())?;
183 self.masm.prologue(vmctx)?;
185
186 self.masm.mov(
188 writable!(vmctx!(M)),
189 vmctx.into(),
190 self.env.ptr_type().try_into()?,
191 )?;
192
193 self.masm.reserve_stack(self.context.frame.locals_size)?;
194 self.spill_register_arguments()?;
195 self.copy_stack_gc_refs_to_frame()?;
196
197 let defined_locals_range = &self.context.frame.defined_locals_range;
198 self.masm.zero_mem_range(defined_locals_range.as_range())?;
199
200 if self.sig.params.has_retptr() {
203 match self.sig.params.unwrap_results_area_operand() {
204 ABIOperand::Reg { ty, reg, .. } => {
205 let results_base_slot = self.context.frame.results_base_slot.as_ref().unwrap();
206 ensure!(
207 results_base_slot.addressed_from_sp(),
208 CodeGenError::sp_addressing_expected(),
209 );
210 let addr = self.masm.local_address(results_base_slot)?;
211 self.masm.store((*reg).into(), addr, (*ty).try_into()?)?;
212 }
213 _ => {}
216 }
217 }
218
219 self.masm.end_source_loc()?;
220
221 Ok(CodeGen {
222 sig: self.sig,
223 context: self.context.for_emission(),
224 masm: self.masm,
225 env: self.env,
226 tunables: self.tunables,
227 source_location: self.source_location,
228 control_frames: self.control_frames,
229 fuel_consumed: self.fuel_consumed,
230 needs_gc_heap: self.needs_gc_heap,
231 gc_codegen_config: self.gc_codegen_config,
232 phase: PhantomData,
233 })
234 }
235
236 fn spill_register_arguments(&mut self) -> Result<()> {
237 use WasmValType::*;
238 for (operand, slot) in self
239 .sig
240 .params_without_retptr()
241 .iter()
242 .zip(self.context.frame.locals())
243 {
244 match (operand, slot) {
245 (ABIOperand::Reg { ty, reg, .. }, slot) => {
246 let addr = self.masm.local_address(slot)?;
247 match &ty {
248 I32 | I64 | F32 | F64 | V128 => {
249 self.masm.store((*reg).into(), addr, (*ty).try_into()?)?;
250 }
251 Ref(rt) => match rt.heap_type {
252 WasmHeapType::Func => {
253 self.masm.store_ptr(*reg, addr)?;
254 }
255 WasmHeapType::Extern
256 | WasmHeapType::Exn
257 | WasmHeapType::ConcreteExn(_)
258 | WasmHeapType::NoExn => {
259 self.masm.store((*reg).into(), addr, (*ty).try_into()?)?;
260 }
261 _ => bail!(CodeGenError::unsupported_wasm_type()),
262 },
263 }
264 }
265 _ => {}
267 }
268 }
269 Ok(())
270 }
271
272 fn copy_stack_gc_refs_to_frame(&mut self) -> Result<()> {
277 for (operand, slot) in self
278 .sig
279 .params_without_retptr()
280 .iter()
281 .zip(self.context.frame.locals())
282 {
283 match (operand, slot) {
284 (ABIOperand::Stack { ty, offset, .. }, slot)
285 if ty.is_vmgcref_type_and_not_i31() =>
286 {
287 ensure!(
288 slot.addressed_from_sp(),
289 CodeGenError::sp_addressing_expected(),
290 );
291 let arg_base = u32::from(<M::ABI as ABI>::arg_base_offset());
292 let src = LocalSlot::stack_arg(*ty, offset + arg_base);
293 let src_addr = self.masm.local_address(&src)?;
294 let dst_addr = self.masm.local_address(slot)?;
295 self.masm.with_scratch::<IntScratch, _>(|masm, scratch| {
296 masm.load(src_addr, scratch.writable(), (*ty).try_into()?)?;
297 masm.store(scratch.inner().into(), dst_addr, (*ty).try_into()?)
298 })?;
299 }
300 _ => {}
301 }
302 }
303 Ok(())
304 }
305}
306
307impl<'a, 'translation, 'data, M> CodeGen<'a, 'translation, 'data, M, Emission>
308where
309 M: MacroAssembler,
310{
311 pub fn emit(
313 &mut self,
314 body: BinaryReader<'a>,
315 validator: &mut FuncValidator<ValidatorResources>,
316 ) -> Result<()> {
317 self.emit_body(body, validator)
318 .and_then(|_| self.emit_end())?;
319
320 Ok(())
321 }
322
323 pub fn pop_control_frame(&mut self) -> Result<ControlStackFrame> {
325 let frame = self
326 .control_frames
327 .pop()
328 .ok_or_else(|| format_err!(CodeGenError::control_frame_expected()))?;
329 if let Some(info) = frame.try_table_info() {
330 self.context
331 .exception_handlers
332 .restore_checkpoint(info.checkpoint);
333 }
334
335 Ok(frame)
336 }
337
338 pub fn source_loc_from(&mut self, loc: SourceLoc) -> RelSourceLoc {
340 if self.source_location.base.is_none() && !loc.is_default() {
341 self.source_location.base = Some(loc);
342 }
343
344 RelSourceLoc::from_base_offset(self.source_location.base.unwrap_or_default(), loc)
345 }
346
347 pub fn handle_unreachable_else(&mut self) -> Result<()> {
355 let frame = self
356 .control_frames
357 .last_mut()
358 .ok_or_else(|| CodeGenError::control_frame_expected())?;
359 ensure!(frame.is_if(), CodeGenError::if_control_frame_expected());
360 if frame.is_next_sequence_reachable() {
361 self.context.reachable = true;
365 frame.ensure_stack_state(self.masm, &mut self.context)?;
366 frame.bind_else(self.masm, &mut self.context)?;
367 }
368 Ok(())
369 }
370
371 pub fn handle_unreachable_end(&mut self) -> Result<()> {
372 let mut frame = self.pop_control_frame()?;
373 if let Some(info) = frame.take_try_table_info() {
374 return self.emit_try_table_end(frame, info);
375 }
376 let is_outermost = self.control_frames.len() == 0;
378
379 if frame.is_next_sequence_reachable() {
380 self.context.reachable = true;
381 frame.ensure_stack_state(self.masm, &mut self.context)?;
382 frame.bind_end(self.masm, &mut self.context)
383 } else if is_outermost {
384 frame.ensure_stack_state(self.masm, &mut self.context)
389 } else {
390 Ok(())
391 }
392 }
393
394 fn emit_body(
395 &mut self,
396 body: BinaryReader<'a>,
397 validator: &mut FuncValidator<ValidatorResources>,
398 ) -> Result<()> {
399 self.maybe_emit_fuel_check()?;
400
401 self.maybe_emit_epoch_check()?;
402
403 self.control_frames.push(ControlStackFrame::block(
406 BlockSig::from_sig(self.sig.clone()),
407 self.masm,
408 &mut self.context,
409 )?);
410
411 if self.sig.params.has_retptr() {
416 self.sig
417 .results
418 .set_ret_area(RetArea::slot(self.context.frame.results_base_slot.unwrap()));
419 }
420
421 let mut ops = OperatorsReader::new(body);
422 while !ops.eof() {
423 let offset = ops.original_position();
424 ops.visit_operator(&mut ValidateThenVisit(
425 validator.simd_visitor(offset),
426 self,
427 offset,
428 ))??;
429 }
430 ops.finish()?;
431 return Ok(());
432
433 struct ValidateThenVisit<'a, T, U>(T, &'a mut U, u64);
434
435 macro_rules! validate_then_visit {
436 ($( @$proposal:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident $ann:tt)*) => {
437 $(
438 fn $visit(&mut self $($(,$arg: $argty)*)?) -> Self::Output {
439 self.0.$visit($($($arg.clone()),*)?)?;
440 let op = Operator::$op $({ $($arg: $arg.clone()),* })?;
441 if self.1.visit(&op) {
442 self.1.before_visit_op(&op, self.2)?;
443 let res = self.1.$visit($($($arg),*)?)?;
444 self.1.after_visit_op()?;
445 Ok(res)
446 } else {
447 Ok(())
448 }
449 }
450 )*
451 };
452 }
453
454 fn visit_op_when_unreachable(op: &Operator) -> bool {
455 use Operator::*;
456 match op {
457 If { .. } | Block { .. } | TryTable { .. } | Loop { .. } | Else | End => true,
458 _ => false,
459 }
460 }
461
462 trait VisitorHooks {
465 fn before_visit_op(&mut self, operator: &Operator, offset: u64) -> Result<()>;
467 fn after_visit_op(&mut self) -> Result<()>;
469
470 fn visit(&self, op: &Operator) -> bool;
479 }
480
481 impl<'a, 'translation, 'data, M: MacroAssembler> VisitorHooks
482 for CodeGen<'a, 'translation, 'data, M, Emission>
483 {
484 fn visit(&self, op: &Operator) -> bool {
485 self.context.reachable || visit_op_when_unreachable(op)
486 }
487
488 fn before_visit_op(&mut self, operator: &Operator, offset: u64) -> Result<()> {
489 self.source_location_before_visit_op(offset)?;
491
492 if self.tunables.consume_fuel {
494 self.fuel_before_visit_op(operator)?;
495 }
496 Ok(())
497 }
498
499 fn after_visit_op(&mut self) -> Result<()> {
500 self.source_location_after_visit_op()
502 }
503 }
504
505 impl<'a, T, U> VisitOperator<'a> for ValidateThenVisit<'_, T, U>
506 where
507 T: VisitSimdOperator<'a, Output = wasmparser::Result<()>>,
508 U: VisitSimdOperator<'a, Output = Result<()>> + VisitorHooks,
509 {
510 type Output = U::Output;
511
512 fn simd_visitor(
513 &mut self,
514 ) -> Option<&mut dyn VisitSimdOperator<'a, Output = Self::Output>>
515 where
516 T:,
517 {
518 Some(self)
519 }
520
521 wasmparser::for_each_visit_operator!(validate_then_visit);
522 }
523
524 impl<'a, T, U> VisitSimdOperator<'a> for ValidateThenVisit<'_, T, U>
525 where
526 T: VisitSimdOperator<'a, Output = wasmparser::Result<()>>,
527 U: VisitSimdOperator<'a, Output = Result<()>> + VisitorHooks,
528 {
529 wasmparser::for_each_visit_simd_operator!(validate_then_visit);
530 }
531 }
532
533 pub fn gc_barrier_needed(&self, ty: &WasmValType) -> bool {
536 ty.is_vmgcref_type_and_not_i31()
537 && self.tunables.collector
538 == Some(wasmtime_environ::Collector::DeferredReferenceCounting)
539 }
540
541 pub fn emit_typecheck_funcref(
543 &mut self,
544 funcref_ptr: Reg,
545 type_index: TypeIndex,
546 ) -> Result<()> {
547 let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
548 let sig_index_bytes = self.env.vmoffsets.size_of_vmshared_type_index();
549 let sig_size = OperandSize::from_bytes(sig_index_bytes);
550 let sig_index = self.env.translation.module.types[type_index].unwrap_module_type_index();
551 let sig_offset = self.env.shared_type_index_offset(sig_index);
552 let signatures_base_offset = self.env.vmoffsets.ptr.vmctx().type_ids();
553 let funcref_sig_offset = self.env.vmoffsets.ptr.vm_func_ref().type_index();
554 let caller_id = self.context.any_gpr(self.masm)?;
556
557 self.masm.with_scratch::<IntScratch, _>(|masm, scratch| {
558 masm.load(
560 masm.address_at_vmctx(signatures_base_offset.into())?,
561 scratch.writable(),
562 ptr_size,
563 )?;
564
565 masm.load(
566 masm.address_at_reg(scratch.inner(), sig_offset)?,
567 writable!(caller_id),
568 sig_size,
569 )
570 })?;
571
572 let callee_id = self.context.any_gpr(self.masm)?;
573 self.masm.load(
574 self.masm
575 .address_at_reg(funcref_ptr, funcref_sig_offset.into())?,
576 writable!(callee_id),
577 sig_size,
578 )?;
579
580 self.masm
582 .cmp(caller_id, callee_id.into(), OperandSize::S32)?;
583 self.masm.trapif(IntCmpKind::Ne, TRAP_BAD_SIGNATURE)?;
584 self.context.free_reg(callee_id);
585 self.context.free_reg(caller_id);
586 wasmtime_environ::error::Ok(())
587 }
588
589 fn emit_end(&mut self) -> Result<()> {
591 let base = SPOffset::from_u32(self.context.frame.locals_size);
595 self.masm.start_source_loc(Default::default())?;
596 if self.context.reachable {
597 ControlStackFrame::pop_abi_results_impl(
598 &mut self.sig.results,
599 &mut self.context,
600 self.masm,
601 |results, _, _| Ok(results.ret_area().copied()),
602 )?;
603 } else {
604 self.context.truncate_stack_to(0)?;
608 self.masm.reset_stack_pointer(base)?;
609 }
610 ensure!(
611 self.context.stack.len() == 0,
612 CodeGenError::unexpected_value_in_value_stack()
613 );
614 let stack_args_size = if self.sig.call_conv.is_default() {
615 crate::abi::align_to(
616 self.sig.params_stack_size(),
617 u32::from(M::ABI::call_stack_align()),
618 )
619 } else {
620 0
621 };
622 self.masm
623 .epilogue(self.context.frame.locals_size, stack_args_size)?;
624 self.masm.end_source_loc()?;
625 Ok(())
626 }
627
628 pub fn emit_select(&mut self, ty: Option<WasmValType>) -> Result<()> {
630 let cond = self.context.pop_to_reg(self.masm, None)?;
631 let val2 = self.context.pop_to_reg(self.masm, None)?;
632 let val1 = self.context.pop_to_reg(self.masm, None)?;
633 let ty = ty.unwrap_or(val1.ty);
634 self.masm.cmp(cond.reg, RegImm::i32(0), OperandSize::S32)?;
635 self.masm.cmov(
637 writable!(val2.into()),
638 val1.into(),
639 IntCmpKind::Ne,
640 ty.try_into()?,
641 )?;
642 self.context.stack.push(TypedReg::new(ty, val2.reg).into());
645 self.context.free_reg(val1.reg);
646 self.context.free_reg(cond);
647
648 Ok(())
649 }
650
651 pub fn emit_set_local(&mut self, index: u32) -> Result<TypedReg> {
655 if self.context.stack.contains_latent_local(index) {
658 self.context.spill(self.masm)?;
659 }
660 let src = self.context.pop_to_reg(self.masm, None)?;
661 let (ty, addr) = self.context.frame.get_local_address(index, self.masm)?;
665 self.masm
666 .store(RegImm::reg(src.reg), addr, ty.try_into()?)?;
667
668 Ok(src)
669 }
670
671 pub fn emit_get_global_addr(&mut self, index: GlobalIndex) -> Result<(WasmValType, Reg, u32)> {
673 let data = self.env.resolve_global(index);
674
675 if data.imported {
676 let global_base = self.masm.address_at_reg(vmctx!(M), data.offset)?;
677 let dst = self.context.any_gpr(self.masm)?;
678 self.masm.load_ptr(global_base, writable!(dst))?;
679 Ok((data.ty, dst, 0))
680 } else {
681 Ok((data.ty, vmctx!(M), data.offset))
682 }
683 }
684
685 pub fn emit_table_get(&mut self, table_index: TableIndex) -> Result<()> {
686 let table = self.env.table(table_index);
687 let heap_type = table.ref_type.heap_type;
688 ensure!(
689 heap_type == WasmHeapType::Func,
690 CodeGenError::unsupported_wasm_type()
691 );
692 ensure!(
693 self.tunables.table_lazy_init,
694 CodeGenError::unsupported_table_eager_init()
695 );
696 let table_data = self.env.resolve_table_data(table_index);
697 let ptr_type = self.env.ptr_type();
698 let builtin = self.env.builtins.table_get_lazy_init_func_ref::<M::ABI>()?;
699
700 self.context.spill(self.masm)?;
705 let elem_value: Reg = self.context.reg(
706 builtin.sig().results.unwrap_singleton().unwrap_reg(),
707 self.masm,
708 )?;
709
710 let index = self.context.pop_to_reg(self.masm, None)?;
711 let base = self.context.any_gpr(self.masm)?;
712
713 let elem_addr = self.emit_compute_table_elem_addr(index.into(), base, &table_data)?;
714 self.masm.load_ptr(elem_addr, writable!(elem_value))?;
715 self.context.free_reg(base);
718
719 let (defined, cont) = (self.masm.get_label()?, self.masm.get_label()?);
720
721 self.context
723 .stack
724 .extend([table_index.as_u32().try_into().unwrap(), index.into()]);
725
726 self.masm.branch(
727 IntCmpKind::Ne,
728 elem_value,
729 elem_value.into(),
730 defined,
731 ptr_type.try_into()?,
732 )?;
733 self.context.free_reg(elem_value);
737 FnCall::emit::<M>(
738 &mut self.env,
739 self.masm,
740 &mut self.context,
741 Callee::Builtin(builtin.clone()),
742 )?;
743
744 let top = self
747 .context
748 .stack
749 .peek()
750 .ok_or_else(|| CodeGenError::missing_values_in_stack())?;
751 let top = top.unwrap_reg();
752 ensure!(
753 top.reg == elem_value,
754 CodeGenError::table_element_value_expected()
755 );
756 self.masm.jmp(cont)?;
757
758 self.masm.bind(defined)?;
765 assert_eq!(FUNCREF_MASK as isize, -2);
766 let imm = RegImm::i64(-2);
767 let dst = top.into();
768 self.masm
769 .and(writable!(dst), dst, imm, top.ty.try_into()?)?;
770
771 self.masm.bind(cont)
772 }
773
774 pub fn emit_table_set(&mut self, table_index: TableIndex) -> Result<()> {
779 let table = self.env.table(table_index);
780 ensure!(
781 table.ref_type.heap_type == WasmHeapType::Func,
782 CodeGenError::unsupported_wasm_type()
783 );
784 ensure!(
785 self.tunables.table_lazy_init,
786 CodeGenError::unsupported_table_eager_init()
787 );
788 let ptr_type = self.env.ptr_type();
789 let table_data = self.env.resolve_table_data(table_index);
790 let value = self.context.pop_to_reg(self.masm, None)?;
791 let index = self.context.pop_to_reg(self.masm, None)?;
792 let base = self.context.any_gpr(self.masm)?;
793 let elem_addr = self.emit_compute_table_elem_addr(index.into(), base, &table_data)?;
794 self.masm.or(
796 writable!(value.into()),
797 value.into(),
798 RegImm::i64(FUNCREF_INIT_BIT as i64),
799 ptr_type.try_into()?,
800 )?;
801
802 self.masm.store_ptr(value.into(), elem_addr)?;
803
804 self.context.free_reg(value);
805 self.context.free_reg(index);
806 self.context.free_reg(base);
807 Ok(())
808 }
809
810 pub fn emit_table_grow(&mut self, table_index: TableIndex) -> Result<()> {
812 let ptr_type = self.env.ptr_type();
813 let idx_type = self.env.table(table_index).idx_type;
814
815 let delta = self.context.pop_to_reg(self.masm, None)?;
818 let tmp = self.context.any_gpr(self.masm)?;
819 self.masm
820 .mov(writable!(tmp), delta.reg.into(), delta.ty.try_into()?)?;
821 self.context.stack.push(TypedReg::new(delta.ty, tmp).into());
822 self.context.stack.push(delta.into());
823
824 let at = self.context.stack.ensure_index_at(1)?;
827 let builtin = self.env.builtins.table_grow::<M::ABI>()?;
828 let builtin = self.prepare_builtin_defined_table_arg(table_index, at, builtin)?;
829 FnCall::emit::<M>(&mut self.env, self.masm, &mut self.context, builtin)?;
830
831 let result = self.context.pop_to_reg(self.masm, None)?;
835 let len = self.context.pop_to_reg(self.masm, None)?;
836 let init = self.context.pop_to_reg(self.masm, None)?;
837
838 let tmp_result = self.context.any_gpr(self.masm)?;
841 self.masm.mov(
842 writable!(tmp_result),
843 result.reg.into(),
844 result.ty.try_into()?,
845 )?;
846 self.context
847 .stack
848 .push(TypedReg::new(result.ty, tmp_result).into());
849
850 let done = self.masm.get_label()?;
853 self.masm.branch(
854 IntCmpKind::Eq,
855 result.reg,
856 RegImm::i64(-1),
857 done,
858 OperandSize::S64,
859 )?;
860
861 self.context.stack.push(result.into());
864 self.context.stack.push(init.into());
865 self.context.stack.push(len.into());
866 self.emit_table_fill(table_index)?;
867
868 self.masm.bind(done)?;
869
870 match (ptr_type, idx_type) {
874 (WasmValType::I64, IndexType::I64) => Ok(()),
875 (WasmValType::I64, IndexType::I32) => {
876 let top: Reg = self.context.pop_to_reg(self.masm, None)?.into();
877 self.masm.wrap(writable!(top), top)?;
878 self.context.stack.push(TypedReg::i32(top).into());
879 Ok(())
880 }
881
882 _ => Err(format_err!(CodeGenError::unsupported_32_bit_platform())),
883 }
884 }
885
886 pub fn emit_table_fill(&mut self, table_index: TableIndex) -> Result<()> {
888 let len = self.context.pop_to_reg(self.masm, None)?;
890 let init = self.context.pop_to_reg(self.masm, None)?;
891 let offset = self.context.pop_to_reg(self.masm, None)?;
892
893 let table_data = self.env.resolve_table_data(table_index);
895 self.emit_compute_table_size(&table_data)?;
896 let table_size = self.context.pop_to_reg(self.masm, None)?;
897 let tmp = self.context.any_gpr(self.masm)?;
898 let idx_size = table_data.index_type().try_into()?;
899 self.masm.mov(writable!(tmp), offset.reg.into(), idx_size)?;
900 self.masm.checked_uadd(
901 writable!(tmp),
902 tmp,
903 len.reg.into(),
904 idx_size,
905 TRAP_TABLE_OUT_OF_BOUNDS,
906 )?;
907 self.masm.cmp(tmp, table_size.reg.into(), idx_size)?;
908 self.masm
909 .trapif(IntCmpKind::GtU, TRAP_TABLE_OUT_OF_BOUNDS)?;
910 self.context.free_reg(tmp);
911 self.context.free_reg(table_size);
912
913 let header = self.masm.get_label()?;
914 let exit = self.masm.get_label()?;
915
916 self.masm.bind(header)?;
917
918 self.masm.branch(
920 IntCmpKind::Eq,
921 len.reg,
922 RegImm::i64(0),
923 exit,
924 OperandSize::S64,
925 )?;
926
927 let tmp_index = self.context.any_gpr(self.masm)?;
931 let tmp_init = self.context.any_gpr(self.masm)?;
932 self.masm
933 .mov(writable!(tmp_index), offset.reg.into(), OperandSize::S64)?;
934 self.masm
935 .mov(writable!(tmp_init), init.reg.into(), OperandSize::S64)?;
936
937 self.context.stack.push(TypedReg::i64(len.reg).into());
939 self.context.stack.push(TypedReg::i64(init.reg).into());
940 self.context.stack.push(TypedReg::i64(offset.reg).into());
941
942 self.context.stack.push(TypedReg::i64(tmp_index).into());
944 self.context.stack.push(TypedReg::i64(tmp_init).into());
945 self.emit_table_set(table_index)?;
946
947 self.context.pop_to_reg(self.masm, Some(offset.reg))?;
950 self.context.pop_to_reg(self.masm, Some(init.reg))?;
951 self.context.pop_to_reg(self.masm, Some(len.reg))?;
952
953 self.masm.add(
956 writable!(offset.reg),
957 offset.reg,
958 RegImm::i64(1),
959 OperandSize::S64,
960 )?;
961 self.masm.sub(
962 writable!(len.reg),
963 len.reg,
964 RegImm::i64(1),
965 OperandSize::S64,
966 )?;
967 self.masm.jmp(header)?;
968
969 self.masm.bind(exit)?;
970
971 self.context.free_reg(offset);
972 self.context.free_reg(init);
973 self.context.free_reg(len);
974 Ok(())
975 }
976
977 fn emit_table_range_bounds_check(
983 &mut self,
984 table_data: &TableData,
985 idx: Reg,
986 len: Reg,
987 ) -> Result<()> {
988 self.emit_compute_table_size(table_data)?;
989 let size = self.context.pop_to_reg(self.masm, None)?;
990
991 let end = self.context.any_gpr(self.masm)?;
994 self.masm
995 .mov(writable!(end), idx.into(), OperandSize::S64)?;
996 self.masm.checked_uadd(
997 writable!(end),
998 end,
999 len.into(),
1000 OperandSize::S64,
1001 TRAP_TABLE_OUT_OF_BOUNDS,
1002 )?;
1003 self.masm.cmp(end, size.reg.into(), OperandSize::S64)?;
1004 self.masm
1005 .trapif(IntCmpKind::GtU, TRAP_TABLE_OUT_OF_BOUNDS)?;
1006
1007 self.context.free_reg(size);
1008 self.context.free_reg(end);
1009 Ok(())
1010 }
1011
1012 pub fn emit_table_copy(&mut self, dst_table: TableIndex, src_table: TableIndex) -> Result<()> {
1014 let dst_data = self.env.resolve_table_data(dst_table);
1015 let src_data = self.env.resolve_table_data(src_table);
1016
1017 let len = self.context.pop_to_reg(self.masm, None)?;
1019 let src = self.context.pop_to_reg(self.masm, None)?;
1020 let dst = self.context.pop_to_reg(self.masm, None)?;
1021
1022 for op in [&len, &src, &dst] {
1026 if op.ty == WasmValType::I32 {
1027 self.masm.extend(
1028 writable!(op.reg),
1029 op.reg,
1030 Extend::<Zero>::I64Extend32.into(),
1031 )?;
1032 }
1033 }
1034
1035 self.emit_table_range_bounds_check(&src_data, src.reg, len.reg)?;
1038 self.emit_table_range_bounds_check(&dst_data, dst.reg, len.reg)?;
1039
1040 let step = self.context.any_gpr(self.masm)?;
1043 let forward = self.masm.get_label()?;
1044 let setup_done = self.masm.get_label()?;
1045 self.masm.branch(
1046 IntCmpKind::LeU,
1047 dst.reg,
1048 src.reg.into(),
1049 forward,
1050 OperandSize::S64,
1051 )?;
1052 {
1054 self.masm
1055 .mov(writable!(step), RegImm::i64(-1), OperandSize::S64)?;
1056 self.masm.add(
1057 writable!(src.reg),
1058 src.reg,
1059 len.reg.into(),
1060 OperandSize::S64,
1061 )?;
1062 self.masm.sub(
1063 writable!(src.reg),
1064 src.reg,
1065 RegImm::i64(1),
1066 OperandSize::S64,
1067 )?;
1068 self.masm.add(
1069 writable!(dst.reg),
1070 dst.reg,
1071 len.reg.into(),
1072 OperandSize::S64,
1073 )?;
1074 self.masm.sub(
1075 writable!(dst.reg),
1076 dst.reg,
1077 RegImm::i64(1),
1078 OperandSize::S64,
1079 )?;
1080 }
1081 self.masm.jmp(setup_done)?;
1082 self.masm.bind(forward)?;
1084 {
1085 self.masm
1086 .mov(writable!(step), RegImm::i64(1), OperandSize::S64)?;
1087 }
1088
1089 self.masm.bind(setup_done)?;
1090
1091 let header = self.masm.get_label()?;
1092 let exit = self.masm.get_label()?;
1093
1094 self.masm.bind(header)?;
1095
1096 self.masm.branch(
1098 IntCmpKind::Eq,
1099 len.reg,
1100 RegImm::i64(0),
1101 exit,
1102 OperandSize::S64,
1103 )?;
1104
1105 self.context.stack.push(TypedReg::i64(step).into());
1109 self.context.stack.push(TypedReg::i64(len.reg).into());
1110 self.context.stack.push(TypedReg::i64(dst.reg).into());
1111 self.context.stack.push(TypedReg::i64(src.reg).into());
1112
1113 {
1122 let tmp_src = self.context.pop_to_reg(self.masm, None)?;
1123 let s = self.context.any_gpr(self.masm)?;
1124 self.masm
1125 .mov(writable!(s), tmp_src.reg.into(), OperandSize::S64)?;
1126 self.context.stack.push(tmp_src.into());
1127 self.context.stack.push(TypedReg::i64(s).into());
1128 self.emit_table_get(src_table)?;
1129 let funcref = self.context.pop_to_reg(self.masm, None)?;
1130
1131 let tmp_src = self.context.pop_to_reg(self.masm, None)?;
1132 let tmp_dst = self.context.pop_to_reg(self.masm, None)?;
1133
1134 let d = self.context.any_gpr(self.masm)?;
1135 self.masm
1136 .mov(writable!(d), tmp_dst.reg.into(), OperandSize::S64)?;
1137 self.context.stack.push(tmp_dst.into());
1138 self.context.stack.push(tmp_src.into());
1139 self.context.stack.push(TypedReg::i64(d).into());
1140 self.context.stack.push(funcref.into());
1141 self.emit_table_set(dst_table)?;
1142 }
1143
1144 self.context.pop_to_reg(self.masm, Some(src.reg))?;
1147 self.context.pop_to_reg(self.masm, Some(dst.reg))?;
1148 self.context.pop_to_reg(self.masm, Some(len.reg))?;
1149 self.context.pop_to_reg(self.masm, Some(step))?;
1150
1151 self.masm
1153 .add(writable!(dst.reg), dst.reg, step.into(), OperandSize::S64)?;
1154 self.masm
1155 .add(writable!(src.reg), src.reg, step.into(), OperandSize::S64)?;
1156 self.masm.sub(
1157 writable!(len.reg),
1158 len.reg,
1159 RegImm::i64(1),
1160 OperandSize::S64,
1161 )?;
1162
1163 self.masm.jmp(header)?;
1164
1165 self.masm.bind(exit)?;
1166
1167 self.context.free_reg(src);
1168 self.context.free_reg(dst);
1169 self.context.free_reg(len);
1170 self.context.free_reg(step);
1171 Ok(())
1172 }
1173
1174 pub fn emit_compute_heap_address(
1195 &mut self,
1196 heap: &HeapData,
1197 memarg: &MemArg,
1198 access_size: OperandSize,
1199 ) -> Result<Option<Reg>> {
1200 let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
1201 let enable_spectre_mitigation = self.env.heap_access_spectre_mitigation();
1202 let add_offset_and_access_size = |offset: ImmOffset, access_size: OperandSize| {
1203 (access_size.bytes() as u64) + (offset.as_u32() as u64)
1204 };
1205
1206 let index = Index::from_typed_reg(self.context.pop_to_reg(self.masm, None)?);
1207
1208 let offset = bounds::ensure_index_and_offset(
1209 self.masm,
1210 index,
1211 memarg.offset,
1212 heap.index_type().try_into()?,
1213 )?;
1214 let offset_with_access_size = add_offset_and_access_size(offset, access_size);
1215
1216 let memory_tunables = MemoryTunables::new(self.tunables, MemoryKind::LinearMemory);
1217 let can_elide_bounds_check = heap
1218 .memory
1219 .can_elide_bounds_check(&memory_tunables, self.env.page_size_log2);
1220
1221 let addr = if offset_with_access_size > heap.memory.maximum_byte_size().unwrap_or(u64::MAX)
1222 || (!self.tunables.memory_may_move
1223 && offset_with_access_size > self.tunables.memory_reservation)
1224 {
1225 self.emit_fuel_increment()?;
1232 self.masm.trap(TrapCode::HEAP_OUT_OF_BOUNDS)?;
1233 self.context.reachable = false;
1234 None
1235
1236 } else if can_elide_bounds_check
1263 && u64::from(u32::MAX)
1264 <= self.tunables.memory_reservation + self.tunables.memory_guard_size
1265 - offset_with_access_size
1266 {
1267 assert!(can_elide_bounds_check);
1268 assert!(heap.index_type() == WasmValType::I32);
1269 let addr = self.context.any_gpr(self.masm)?;
1270 bounds::load_heap_addr_unchecked(self.masm, &heap, index, offset, addr, ptr_size)?;
1271 Some(addr)
1272
1273 } else if let Some(static_size) = heap.memory.static_heap_size() {
1281 let bounds = Bounds::from_u64(static_size);
1282 let addr = bounds::load_heap_addr_checked(
1283 self.masm,
1284 &mut self.context,
1285 ptr_size,
1286 &heap,
1287 enable_spectre_mitigation,
1288 bounds,
1289 index,
1290 offset,
1291 |masm, bounds, index| {
1292 let adjusted_bounds = bounds.as_u64() - offset_with_access_size;
1293 let index_reg = index.as_typed_reg().reg;
1294 masm.cmp(
1295 index_reg,
1296 RegImm::i64(adjusted_bounds as i64),
1297 ptr_size,
1303 )?;
1304 Ok(IntCmpKind::GtU)
1305 },
1306 )?;
1307 Some(addr)
1308 } else {
1309 let bounds = bounds::load_dynamic_heap_bounds::<_>(
1315 &mut self.context,
1316 self.masm,
1317 &heap,
1318 ptr_size,
1319 )?;
1320
1321 let index_reg = index.as_typed_reg().reg;
1322 let index_offset_and_access_size = self.context.any_gpr(self.masm)?;
1326
1327 self.masm.mov(
1339 writable!(index_offset_and_access_size),
1340 index_reg.into(),
1341 heap.index_type().try_into()?,
1342 )?;
1343 self.masm.checked_uadd(
1354 writable!(index_offset_and_access_size),
1355 index_offset_and_access_size,
1356 RegImm::i64(offset_with_access_size as i64),
1357 ptr_size,
1358 TrapCode::HEAP_OUT_OF_BOUNDS,
1359 )?;
1360
1361 let addr = bounds::load_heap_addr_checked(
1362 self.masm,
1363 &mut self.context,
1364 ptr_size,
1365 &heap,
1366 enable_spectre_mitigation,
1367 bounds,
1368 index,
1369 offset,
1370 |masm, bounds, _| {
1371 let bounds_reg = bounds.as_typed_reg().reg;
1372 masm.cmp(
1373 index_offset_and_access_size,
1374 bounds_reg.into(),
1375 ptr_size,
1379 )?;
1380 Ok(IntCmpKind::GtU)
1381 },
1382 )?;
1383 self.context.free_reg(bounds.as_typed_reg().reg);
1384 self.context.free_reg(index_offset_and_access_size);
1385 Some(addr)
1386 };
1387
1388 self.context.free_reg(index.as_typed_reg().reg);
1389 Ok(addr)
1390 }
1391
1392 fn emit_check_align(
1395 &mut self,
1396 heap: &HeapData,
1397 memarg: &MemArg,
1398 access_size: OperandSize,
1399 ) -> Result<()> {
1400 if access_size.bytes() > 1 {
1401 let heap_ty_size: OperandSize = heap.index_type().try_into()?;
1402 let addr = *self
1403 .context
1404 .stack
1405 .peek()
1406 .ok_or_else(|| CodeGenError::missing_values_in_stack())?;
1407 let tmp = self.context.any_gpr(self.masm)?;
1408 self.context.move_val_to_reg(&addr, tmp, self.masm)?;
1409
1410 if memarg.offset != 0 {
1411 self.masm.add(
1412 writable!(tmp),
1413 tmp,
1414 RegImm::Imm(Imm::I64(memarg.offset)),
1415 heap_ty_size,
1416 )?;
1417 }
1418
1419 self.masm.and(
1420 writable!(tmp),
1421 tmp,
1422 RegImm::Imm(Imm::I32(access_size.bytes() - 1)),
1423 heap_ty_size,
1424 )?;
1425
1426 self.masm.cmp(tmp, RegImm::Imm(Imm::i64(0)), heap_ty_size)?;
1427 self.masm.trapif(IntCmpKind::Ne, TRAP_HEAP_MISALIGNED)?;
1428 self.context.free_reg(tmp);
1429 }
1430
1431 Ok(())
1432 }
1433
1434 pub fn emit_compute_heap_address_align_checked(
1435 &mut self,
1436 heap: &HeapData,
1437 memarg: &MemArg,
1438 access_size: OperandSize,
1439 ) -> Result<Option<Reg>> {
1440 self.emit_check_align(heap, memarg, access_size)?;
1441 self.emit_compute_heap_address(heap, memarg, access_size)
1442 }
1443
1444 pub fn emit_wasm_load(
1446 &mut self,
1447 arg: &MemArg,
1448 target_type: WasmValType,
1449 kind: LoadKind,
1450 ) -> Result<()> {
1451 let emit_load = |this: &mut Self, dst, addr, kind| -> Result<()> {
1452 let src = this.masm.address_at_reg(addr, 0)?;
1453 this.masm.wasm_load(src, writable!(dst), kind)?;
1454 this.context
1455 .stack
1456 .push(TypedReg::new(target_type, dst).into());
1457 this.context.free_reg(addr);
1458 Ok(())
1459 };
1460
1461 let memory_index = MemoryIndex::from_u32(arg.memory);
1462 let heap = self.env.resolve_heap(memory_index);
1463
1464 match kind {
1467 LoadKind::VectorLane(_) => {
1468 let dst = self.context.pop_to_reg(self.masm, None)?;
1472 let addr =
1473 self.emit_compute_heap_address(&heap, &arg, kind.derive_operand_size())?;
1474 if let Some(addr) = addr {
1475 emit_load(self, dst.reg, addr, kind)?;
1476 } else {
1477 self.context.free_reg(dst);
1478 }
1479 }
1480 _ => {
1481 let maybe_addr = match kind {
1482 LoadKind::Atomic(_, _) => self.emit_compute_heap_address_align_checked(
1483 &heap,
1484 &arg,
1485 kind.derive_operand_size(),
1486 )?,
1487 _ => self.emit_compute_heap_address(&heap, &arg, kind.derive_operand_size())?,
1488 };
1489
1490 if let Some(addr) = maybe_addr {
1491 let dst = match target_type {
1492 WasmValType::I32 | WasmValType::I64 => self.context.any_gpr(self.masm)?,
1493 WasmValType::F32 | WasmValType::F64 => self.context.any_fpr(self.masm)?,
1494 WasmValType::V128 => self.context.reg_for_type(target_type, self.masm)?,
1495 _ => bail!(CodeGenError::unsupported_wasm_type()),
1496 };
1497
1498 emit_load(self, dst, addr, kind)?;
1499 }
1500 }
1501 }
1502
1503 Ok(())
1504 }
1505
1506 pub fn emit_wasm_store(&mut self, arg: &MemArg, kind: StoreKind) -> Result<()> {
1508 let memory_index = MemoryIndex::from_u32(arg.memory);
1509 let heap = self.env.resolve_heap(memory_index);
1510 let src = self.context.pop_to_reg(self.masm, None)?;
1511
1512 let maybe_addr = match kind {
1513 StoreKind::Atomic(size) => {
1514 self.emit_compute_heap_address_align_checked(&heap, &arg, size)?
1515 }
1516 StoreKind::Operand(size) | StoreKind::VectorLane(LaneSelector { size, .. }) => {
1517 self.emit_compute_heap_address(&heap, &arg, size)?
1518 }
1519 };
1520
1521 if let Some(addr) = maybe_addr {
1522 self.masm
1523 .wasm_store(src.reg, self.masm.address_at_reg(addr, 0)?, kind)?;
1524
1525 self.context.free_reg(addr);
1526 }
1527 self.context.free_reg(src);
1528
1529 Ok(())
1530 }
1531
1532 pub fn emit_compute_table_elem_addr(
1535 &mut self,
1536 index: Reg,
1537 base: Reg,
1538 table_data: &TableData,
1539 ) -> Result<M::Address> {
1540 let bound = self.context.any_gpr(self.masm)?;
1541 let tmp = self.context.any_gpr(self.masm)?;
1542 let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
1543
1544 if let Some(offset) = table_data.import_from {
1545 self.masm
1549 .load_ptr(self.masm.address_at_vmctx(offset)?, writable!(base))?;
1550 } else {
1551 self.masm.mov(writable!(base), vmctx!(M).into(), ptr_size)?;
1554 };
1555
1556 let bound_addr = self
1558 .masm
1559 .address_at_reg(base, table_data.current_elems_offset)?;
1560 let bound_size = table_data.current_elements_size;
1561 self.masm.load(bound_addr, writable!(bound), bound_size)?;
1562 self.masm.cmp(index, bound.into(), bound_size)?;
1563 self.masm
1564 .trapif(IntCmpKind::GeU, TRAP_TABLE_OUT_OF_BOUNDS)?;
1565
1566 self.masm.with_scratch::<IntScratch, _>(|masm, scratch| {
1571 masm.mov(scratch.writable(), index.into(), bound_size)?;
1572 masm.mul(
1573 scratch.writable(),
1574 scratch.inner(),
1575 RegImm::i32(table_data.element_size.bytes() as i32),
1576 table_data.element_size,
1577 )?;
1578 masm.load_ptr(
1579 masm.address_at_reg(base, table_data.offset)?,
1580 writable!(base),
1581 )?;
1582 masm.mov(writable!(tmp), base.into(), ptr_size)?;
1585 masm.add(writable!(base), base, scratch.inner().into(), ptr_size)
1587 })?;
1588 if self.env.table_access_spectre_mitigation() {
1589 self.masm.cmp(index, bound.into(), bound_size)?;
1592 self.masm
1593 .cmov(writable!(base), tmp, IntCmpKind::GeU, ptr_size)?;
1594 }
1595 self.context.free_reg(bound);
1596 self.context.free_reg(tmp);
1597 self.masm.address_at_reg(base, 0)
1598 }
1599
1600 pub fn emit_compute_table_size(&mut self, table_data: &TableData) -> Result<()> {
1602 let size = self.context.any_gpr(self.masm)?;
1603 let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
1604
1605 self.masm.with_scratch::<IntScratch, _>(|masm, scratch| {
1606 if let Some(offset) = table_data.import_from {
1607 masm.load_ptr(masm.address_at_vmctx(offset)?, scratch.writable())?;
1608 } else {
1609 masm.mov(scratch.writable(), vmctx!(M).into(), ptr_size)?;
1610 };
1611
1612 let size_addr =
1613 masm.address_at_reg(scratch.inner(), table_data.current_elems_offset)?;
1614 masm.load(size_addr, writable!(size), table_data.current_elements_size)
1615 })?;
1616
1617 let dst = TypedReg::new(table_data.index_type(), size);
1618 self.context.stack.push(dst.into());
1619 Ok(())
1620 }
1621
1622 fn load_memory_length(&mut self, heap_data: &HeapData, size_reg: Reg) -> Result<()> {
1624 self.masm.with_scratch::<IntScratch, _>(|masm, scratch| {
1625 let base = if let Some(offset) = heap_data.import_from {
1626 masm.load_ptr(masm.address_at_vmctx(offset)?, scratch.writable())?;
1627 scratch.inner()
1628 } else {
1629 vmctx!(M)
1630 };
1631
1632 let size_addr = masm.address_at_reg(base, heap_data.current_length_offset)?;
1633 masm.load_ptr(size_addr, writable!(size_reg))
1634 })?;
1635 Ok(())
1636 }
1637
1638 pub fn emit_compute_memory_size(&mut self, heap_data: &HeapData) -> Result<()> {
1640 let size_reg = self.context.any_gpr(self.masm)?;
1641 self.load_memory_length(heap_data, size_reg)?;
1642
1643 let dst = TypedReg::new(heap_data.index_type(), size_reg);
1645 let pow = heap_data.memory.page_size_log2;
1646 self.masm.shift_ir(
1647 writable!(dst.reg),
1648 Imm::i32(pow as i32),
1649 dst.into(),
1650 ShiftKind::ShrU,
1651 self.env.ptr_type().try_into()?,
1652 )?;
1653 self.context.stack.push(dst.into());
1654 Ok(())
1655 }
1656
1657 fn emit_bounds_check_and_compute_addr(
1660 &mut self,
1661 heap: &HeapData,
1662 dst: Reg,
1663 ptr: Reg,
1664 len: Reg,
1665 ) -> Result<()> {
1666 let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
1667 let idx_size: OperandSize = heap.index_type().try_into()?;
1668 match idx_size {
1672 OperandSize::S32 => {
1673 self.masm
1674 .extend(writable!(dst), ptr, Extend::<Zero>::I64Extend32.into())?;
1675 self.masm.add_uextend(
1676 writable!(dst),
1677 dst,
1678 len,
1679 OperandSize::S32,
1680 OperandSize::S64,
1681 )?;
1682 }
1683 OperandSize::S64 => {
1684 self.masm
1685 .mov(writable!(dst), ptr.into(), OperandSize::S64)?;
1686 self.masm.checked_uadd(
1687 writable!(dst),
1688 dst,
1689 len.into(),
1690 OperandSize::S64,
1691 TrapCode::HEAP_OUT_OF_BOUNDS,
1692 )?;
1693 }
1694 _ => unreachable!(),
1695 }
1696
1697 let size_in_bytes = self.context.any_gpr(self.masm)?;
1700 self.load_memory_length(&heap, size_in_bytes)?;
1701 assert!(ptr_size == OperandSize::S64);
1702 self.masm.cmp(dst, size_in_bytes.into(), ptr_size)?;
1703 self.masm
1704 .trapif(IntCmpKind::GtU, TrapCode::HEAP_OUT_OF_BOUNDS)?;
1705 self.context.free_reg(size_in_bytes);
1706
1707 bounds::load_heap_addr_unchecked(
1709 self.masm,
1710 &heap,
1711 Index::from_typed_reg(TypedReg::new(heap.index_type(), ptr)),
1712 ImmOffset::from_u32(0),
1713 dst,
1714 ptr_size,
1715 )?;
1716 Ok(())
1717 }
1718
1719 pub fn emit_memory_copy(&mut self, dst_mem: MemoryIndex, src_mem: MemoryIndex) -> Result<()> {
1721 let dst_heap = self.env.resolve_heap(dst_mem);
1722 let src_heap = self.env.resolve_heap(src_mem);
1723 let dst_idx_size: OperandSize = dst_heap.index_type().try_into()?;
1724 let src_idx_size: OperandSize = src_heap.index_type().try_into()?;
1725
1726 let len = self.context.pop_to_reg(self.masm, None)?;
1727 let src = self.context.pop_to_reg(self.masm, None)?;
1728 let dst = self.context.pop_to_reg(self.masm, None)?;
1729
1730 if dst_idx_size == OperandSize::S32 || src_idx_size == OperandSize::S32 {
1736 self.masm.extend(
1737 writable!(len.reg),
1738 len.reg,
1739 Extend::<Zero>::I64Extend32.into(),
1740 )?;
1741 }
1742
1743 let dst_raw_addr = self.context.any_gpr(self.masm)?;
1744 self.emit_bounds_check_and_compute_addr(&dst_heap, dst_raw_addr, dst.reg, len.reg)?;
1745 self.context.free_reg(dst);
1746
1747 let src_raw_addr = self.context.any_gpr(self.masm)?;
1748 self.emit_bounds_check_and_compute_addr(&src_heap, src_raw_addr, src.reg, len.reg)?;
1749 self.context.free_reg(src);
1750
1751 self.context
1752 .stack
1753 .push(TypedReg::new(self.env.ptr_type(), dst_raw_addr).into());
1754 self.context
1755 .stack
1756 .push(TypedReg::new(self.env.ptr_type(), src_raw_addr).into());
1757 self.context
1758 .stack
1759 .push(TypedReg::new(self.env.ptr_type(), len.reg).into());
1760
1761 let builtin = self.env.builtins.memory_copy::<M::ABI>()?;
1762 FnCall::emit::<M>(
1763 &mut self.env,
1764 self.masm,
1765 &mut self.context,
1766 Callee::Builtin(builtin),
1767 )?;
1768 Ok(())
1769 }
1770
1771 pub fn emit_memory_fill(&mut self, mem: MemoryIndex) -> Result<()> {
1773 let heap = self.env.resolve_heap(mem);
1774 let ptr_size: OperandSize = self.env.ptr_type().try_into()?;
1775 let idx_size: OperandSize = heap.index_type().try_into()?;
1776
1777 let len = self.context.pop_to_reg(self.masm, None)?;
1779 let val = self.context.pop_to_reg(self.masm, None)?;
1780 let dst = self.context.pop_to_reg(self.masm, None)?;
1781
1782 let raw_addr = self.context.any_gpr(self.masm)?;
1783 self.emit_bounds_check_and_compute_addr(&heap, raw_addr, dst.reg, len.reg)?;
1784 self.context.free_reg(dst);
1785
1786 let len_reg = len.reg;
1789 if idx_size == OperandSize::S32 && ptr_size == OperandSize::S64 {
1790 self.masm.extend(
1791 writable!(len_reg),
1792 len_reg,
1793 Extend::<Zero>::I64Extend32.into(),
1794 )?;
1795 }
1796
1797 self.context
1799 .stack
1800 .push(TypedReg::new(self.env.ptr_type(), raw_addr).into());
1801 self.context.stack.push(val.into());
1802 self.context
1803 .stack
1804 .push(TypedReg::new(self.env.ptr_type(), len_reg).into());
1805
1806 let builtin = self.env.builtins.memory_fill::<M::ABI>()?;
1807 FnCall::emit::<M>(
1808 &mut self.env,
1809 self.masm,
1810 &mut self.context,
1811 Callee::Builtin(builtin),
1812 )?;
1813 Ok(())
1814 }
1815
1816 pub fn emit_memory_init(&mut self, segment: DataIndex, mem: MemoryIndex) -> Result<()> {
1818 let dst_heap = self.env.resolve_heap(mem);
1819
1820 let len = self.context.pop_to_reg(self.masm, None)?;
1821 let src = self.context.pop_to_reg(self.masm, None)?;
1822 let dst = self.context.pop_to_reg(self.masm, None)?;
1823
1824 self.masm.extend(
1830 writable!(len.reg),
1831 len.reg,
1832 Extend::<Zero>::I64Extend32.into(),
1833 )?;
1834
1835 let dst_raw_addr = self.context.any_gpr(self.masm)?;
1836 self.emit_bounds_check_and_compute_addr(&dst_heap, dst_raw_addr, dst.reg, len.reg)?;
1837 self.context.free_reg(dst);
1838
1839 let runtime_data_index = match self.env.translation.runtime_data_map[segment] {
1840 Some(i) => i,
1841
1842 None => {
1845 self.masm.cmp(src.reg, RegImm::i32(0), OperandSize::S32)?;
1846 self.masm
1847 .trapif(IntCmpKind::Ne, TrapCode::HEAP_OUT_OF_BOUNDS)?;
1848 self.masm.cmp(len.reg, RegImm::i32(0), OperandSize::S32)?;
1849 self.masm
1850 .trapif(IntCmpKind::Ne, TrapCode::HEAP_OUT_OF_BOUNDS)?;
1851 self.context.free_reg(dst_raw_addr);
1852 self.context.free_reg(src);
1853 self.context.free_reg(len);
1854 return Ok(());
1855 }
1856 };
1857
1858 let data_segment_length_offset = self
1862 .env
1863 .vmoffsets
1864 .runtime_data_lengths()
1865 .at(runtime_data_index);
1866 let tmp1 = self.context.any_gpr(self.masm)?;
1867 let tmp2 = self.context.any_gpr(self.masm)?;
1868 self.masm.load(
1869 self.masm.address_at_vmctx(data_segment_length_offset)?,
1870 writable!(tmp1),
1871 OperandSize::S32,
1872 )?;
1873 self.masm
1874 .mov(writable!(tmp2), src.reg.into(), OperandSize::S32)?;
1875 self.masm.checked_uadd(
1876 writable!(tmp2),
1877 tmp2,
1878 len.reg.into(),
1879 OperandSize::S32,
1880 TrapCode::HEAP_OUT_OF_BOUNDS,
1881 )?;
1882 self.masm.cmp(tmp2, tmp1.into(), OperandSize::S32)?;
1883 self.masm
1884 .trapif(IntCmpKind::GtU, TrapCode::HEAP_OUT_OF_BOUNDS)?;
1885 self.context.free_reg(tmp2);
1886
1887 let data_segment_base_offset = self
1890 .env
1891 .vmoffsets
1892 .runtime_data_bases()
1893 .at(runtime_data_index);
1894 self.masm.load(
1895 self.masm.address_at_vmctx(data_segment_base_offset)?,
1896 writable!(tmp1),
1897 OperandSize::S64,
1898 )?;
1899 self.masm.add_uextend(
1900 writable!(tmp1),
1901 tmp1,
1902 src.reg,
1903 OperandSize::S32,
1904 OperandSize::S64,
1905 )?;
1906 self.context.free_reg(src);
1907
1908 self.context.stack.push(TypedReg::i64(dst_raw_addr).into());
1910 self.context.stack.push(TypedReg::i64(tmp1).into());
1911 self.context.stack.push(len.into());
1912 let builtin = self.env.builtins.memory_copy::<M::ABI>()?;
1913 FnCall::emit::<M>(
1914 &mut self.env,
1915 self.masm,
1916 &mut self.context,
1917 Callee::Builtin(builtin),
1918 )?;
1919 Ok(())
1920 }
1921
1922 pub fn emit_data_drop(&mut self, data_index: DataIndex) -> Result<()> {
1923 let runtime_data_index = match self.env.translation.runtime_data_map[data_index] {
1924 Some(idx) => idx,
1925 None => return Ok(()),
1927 };
1928 let data_segment_offset = self
1929 .env
1930 .vmoffsets
1931 .runtime_data_lengths()
1932 .at(runtime_data_index);
1933 let len_addr = self.masm.address_at_vmctx(data_segment_offset)?;
1934 self.masm.store(RegImm::i32(0), len_addr, OperandSize::S32)
1935 }
1936
1937 pub fn emit_table_init(
1939 &mut self,
1940 elem_index: ElemIndex,
1941 table_index: TableIndex,
1942 ) -> Result<()> {
1943 let builtin_base = self.env.builtins.passive_elem_segment_base::<M::ABI>()?;
1944 let builtin_len = self.env.builtins.passive_elem_segment_len::<M::ABI>()?;
1945
1946 match self.env.translation.passive_elem_map[elem_index] {
1948 Some(idx) => {
1949 self.context.stack.extend([idx.as_u32().try_into()?]);
1950 FnCall::emit::<M>(
1951 &mut self.env,
1952 self.masm,
1953 &mut self.context,
1954 Callee::Builtin(builtin_len),
1955 )?;
1956 self.context.stack.extend([idx.as_u32().try_into()?]);
1957 FnCall::emit::<M>(
1958 &mut self.env,
1959 self.masm,
1960 &mut self.context,
1961 Callee::Builtin(builtin_base),
1962 )?;
1963 }
1964 None => {
1966 let tmp = self.context.any_gpr(self.masm)?;
1967 self.masm
1968 .mov(writable!(tmp), RegImm::i64(0), OperandSize::S64)?;
1969 self.context
1970 .stack
1971 .push(TypedReg::new(WasmValType::I64, tmp).into());
1972
1973 let tmp = self.context.any_gpr(self.masm)?;
1974 self.masm
1975 .mov(writable!(tmp), RegImm::i64(0), OperandSize::S64)?;
1976 self.context
1977 .stack
1978 .push(TypedReg::new(WasmValType::I64, tmp).into());
1979 }
1980 };
1981
1982 let table_data = self.env.resolve_table_data(table_index);
1984 let idx_size = table_data.index_type().try_into()?;
1985 self.emit_compute_table_size(&table_data)?;
1986
1987 let table_size = self.context.pop_to_reg(self.masm, None)?;
1990 let segment_base = self.context.pop_to_reg(self.masm, None)?;
1991 let segment_len = self.context.pop_to_reg(self.masm, None)?;
1992 let len = self.context.pop_to_reg(self.masm, None)?;
1993 let segment_off = self.context.pop_to_reg(self.masm, None)?;
1994 let table_off = self.context.pop_to_reg(self.masm, None)?;
1995
1996 if len.ty == WasmValType::I32 {
1999 self.masm.extend(
2000 writable!(len.reg),
2001 len.reg,
2002 Extend::<Zero>::I64Extend32.into(),
2003 )?;
2004 }
2005
2006 let tmp = self.context.any_gpr(self.masm)?;
2008 {
2009 self.masm
2010 .mov(writable!(tmp), segment_off.reg.into(), OperandSize::S32)?;
2011 self.masm.checked_uadd(
2012 writable!(tmp),
2013 tmp,
2014 len.reg.into(),
2015 OperandSize::S32,
2016 TRAP_TABLE_OUT_OF_BOUNDS,
2017 )?;
2018 self.masm
2019 .cmp(tmp, segment_len.reg.into(), OperandSize::S32)?;
2020 self.masm
2021 .trapif(IntCmpKind::GtU, TRAP_TABLE_OUT_OF_BOUNDS)?;
2022 self.context.free_reg(segment_len);
2023 }
2024
2025 {
2027 self.masm
2028 .mov(writable!(tmp), table_off.reg.into(), idx_size)?;
2029 self.masm.checked_uadd(
2030 writable!(tmp),
2031 tmp,
2032 len.reg.into(),
2033 idx_size,
2034 TRAP_TABLE_OUT_OF_BOUNDS,
2035 )?;
2036 self.masm.cmp(tmp, table_size.reg.into(), idx_size)?;
2037 self.masm
2038 .trapif(IntCmpKind::GtU, TRAP_TABLE_OUT_OF_BOUNDS)?;
2039 self.context.free_reg(table_size);
2040 }
2041 self.context.free_reg(tmp);
2042
2043 {
2045 self.masm.extend(
2046 writable!(segment_off.reg),
2047 segment_off.reg,
2048 Extend::<Zero>::I64Extend32.into(),
2049 )?;
2050 self.masm.mul(
2051 writable!(segment_off.reg),
2052 segment_off.reg,
2053 RegImm::i64(16),
2054 OperandSize::S64,
2055 )?;
2056 self.masm.add(
2057 writable!(segment_base.reg),
2058 segment_base.reg,
2059 segment_off.reg.into(),
2060 OperandSize::S64,
2061 )?;
2062 self.context.free_reg(segment_off);
2063 }
2064
2065 let header = self.masm.get_label()?;
2068 let exit = self.masm.get_label()?;
2069
2070 self.masm.bind(header)?;
2071 {
2072 self.masm.branch(
2073 IntCmpKind::Eq,
2074 len.reg,
2075 RegImm::i64(0),
2076 exit,
2077 OperandSize::S64,
2078 )?;
2079
2080 let funcref = self.context.any_gpr(self.masm)?;
2083 self.masm.load_ptr(
2084 self.masm.address_at_reg(segment_base.reg, 0)?,
2085 writable!(funcref),
2086 )?;
2087 self.masm.add(
2088 writable!(segment_base.reg),
2089 segment_base.reg,
2090 RegImm::i64(16),
2091 OperandSize::S64,
2092 )?;
2093
2094 self.context.stack.push(segment_base.into());
2098 self.context.stack.push(len.into());
2099 let table_off_copy = self.context.any_gpr(self.masm)?;
2100 self.masm.mov(
2101 writable!(table_off_copy),
2102 table_off.reg.into(),
2103 table_off.ty.try_into()?,
2104 )?;
2105 self.context.stack.push(table_off.into());
2106 self.context
2107 .stack
2108 .push(TypedReg::new(table_off.ty, table_off_copy).into());
2109 self.context
2110 .stack
2111 .push(TypedReg::new(WasmValType::FUNCREF, funcref).into());
2112 self.emit_table_set(table_index)?;
2113
2114 self.context.pop_to_reg(self.masm, Some(table_off.reg))?;
2116 self.context.pop_to_reg(self.masm, Some(len.reg))?;
2117 self.context.pop_to_reg(self.masm, Some(segment_base.reg))?;
2118
2119 self.masm.add(
2121 writable!(table_off.reg),
2122 table_off.reg,
2123 RegImm::i64(1),
2124 table_off.ty.try_into()?,
2125 )?;
2126
2127 self.masm.sub(
2130 writable!(len.reg),
2131 len.reg,
2132 RegImm::i64(1),
2133 OperandSize::S64,
2134 )?;
2135 }
2136 self.masm.jmp(header)?;
2137
2138 self.masm.bind(exit)?;
2139
2140 self.context.free_reg(segment_base);
2141 self.context.free_reg(len);
2142 self.context.free_reg(table_off);
2143 Ok(())
2144 }
2145
2146 pub fn emit_elem_drop(&mut self, elem_index: ElemIndex) -> Result<()> {
2148 let passive_elem_index = match self.env.translation.passive_elem_map[elem_index] {
2149 Some(idx) => idx,
2150 None => return Ok(()),
2152 };
2153 let builtin = self.env.builtins.passive_elem_segment_drop::<M::ABI>()?;
2154 self.context
2155 .stack
2156 .extend([passive_elem_index.as_u32().try_into()?]);
2157 FnCall::emit::<M>(
2158 &mut self.env,
2159 self.masm,
2160 &mut self.context,
2161 Callee::Builtin(builtin),
2162 )?;
2163 self.context.pop_and_free(self.masm)
2164 }
2165
2166 pub fn maybe_emit_fuel_check(&mut self) -> Result<()> {
2170 if !self.tunables.consume_fuel {
2171 return Ok(());
2172 }
2173
2174 self.emit_fuel_increment()?;
2175 let out_of_fuel = self.env.builtins.out_of_gas::<M::ABI>()?;
2176 let fuel_reg = self.context.without::<Result<Reg>, M, _>(
2177 &out_of_fuel.sig().regs,
2178 self.masm,
2179 |cx, masm| cx.any_gpr(masm),
2180 )??;
2181
2182 self.emit_load_fuel_consumed(fuel_reg)?;
2183
2184 let continuation = self.masm.get_label()?;
2186
2187 self.context.spill(self.masm)?;
2190 self.masm.branch(
2193 IntCmpKind::LtS,
2194 fuel_reg,
2195 RegImm::i64(0),
2196 continuation,
2197 OperandSize::S64,
2198 )?;
2199 FnCall::emit::<M>(
2201 &mut self.env,
2202 self.masm,
2203 &mut self.context,
2204 Callee::Builtin(out_of_fuel.clone()),
2205 )?;
2206 self.context.pop_and_free(self.masm)?;
2207
2208 self.masm.bind(continuation)?;
2210 self.context.free_reg(fuel_reg);
2211
2212 Ok(())
2213 }
2214
2215 fn emit_load_fuel_consumed(&mut self, fuel_reg: Reg) -> Result<()> {
2218 let store_context_offset = self.env.vmoffsets.ptr.vmctx().store_context();
2219 let fuel_offset = self.env.vmoffsets.ptr.vm_store_context().fuel_consumed();
2220 self.masm.load_ptr(
2221 self.masm
2222 .address_at_vmctx(u32::from(store_context_offset))?,
2223 writable!(fuel_reg),
2224 )?;
2225
2226 self.masm.load(
2227 self.masm.address_at_reg(fuel_reg, u32::from(fuel_offset))?,
2228 writable!(fuel_reg),
2229 OperandSize::S64,
2231 )
2232 }
2233
2234 pub fn maybe_emit_epoch_check(&mut self) -> Result<()> {
2237 if !self.tunables.epoch_interruption {
2238 return Ok(());
2239 }
2240
2241 let cont = self.masm.get_label()?;
2244 let new_epoch = self.env.builtins.new_epoch::<M::ABI>()?;
2245
2246 let (epoch_deadline_reg, epoch_counter_reg) =
2254 self.context.without::<Result<(Reg, Reg)>, M, _>(
2255 &new_epoch.sig().regs,
2256 self.masm,
2257 |cx, masm| Ok((cx.any_gpr(masm)?, cx.any_gpr(masm)?)),
2258 )??;
2259
2260 self.emit_load_epoch_deadline_and_counter(epoch_deadline_reg, epoch_counter_reg)?;
2261
2262 self.context.spill(self.masm)?;
2265 self.masm.branch(
2266 IntCmpKind::LtU,
2267 epoch_counter_reg,
2268 RegImm::reg(epoch_deadline_reg),
2269 cont,
2270 OperandSize::S64,
2271 )?;
2272 FnCall::emit::<M>(
2274 &mut self.env,
2275 self.masm,
2276 &mut self.context,
2277 Callee::Builtin(new_epoch.clone()),
2278 )?;
2279 self.visit_drop()?;
2282
2283 self.masm.bind(cont)?;
2285
2286 self.context.free_reg(epoch_deadline_reg);
2287 self.context.free_reg(epoch_counter_reg);
2288 Ok(())
2289 }
2290
2291 fn emit_load_epoch_deadline_and_counter(
2292 &mut self,
2293 epoch_deadline_reg: Reg,
2294 epoch_counter_reg: Reg,
2295 ) -> Result<()> {
2296 let epoch_ptr_offset = self.env.vmoffsets.ptr.vmctx().epoch_ptr();
2297 let store_context_offset = self.env.vmoffsets.ptr.vmctx().store_context();
2298 let epoch_deadline_offset = self.env.vmoffsets.ptr.vm_store_context().epoch_deadline();
2299
2300 self.masm.load_ptr(
2302 self.masm.address_at_vmctx(u32::from(epoch_ptr_offset))?,
2303 writable!(epoch_counter_reg),
2304 )?;
2305
2306 self.masm.load(
2309 self.masm.address_at_reg(epoch_counter_reg, 0)?,
2310 writable!(epoch_counter_reg),
2311 OperandSize::S64,
2312 )?;
2313
2314 self.masm.load_ptr(
2316 self.masm
2317 .address_at_vmctx(u32::from(store_context_offset))?,
2318 writable!(epoch_deadline_reg),
2319 )?;
2320
2321 self.masm.load(
2322 self.masm
2323 .address_at_reg(epoch_deadline_reg, u32::from(epoch_deadline_offset))?,
2324 writable!(epoch_deadline_reg),
2325 OperandSize::S64,
2327 )
2328 }
2329
2330 fn emit_fuel_increment(&mut self) -> Result<()> {
2333 let fuel_at_point = std::mem::replace(&mut self.fuel_consumed, 0);
2334 if fuel_at_point == 0 {
2335 return Ok(());
2336 }
2337
2338 let store_context_offset = self.env.vmoffsets.ptr.vmctx().store_context();
2339 let fuel_offset = self.env.vmoffsets.ptr.vm_store_context().fuel_consumed();
2340 let limits_reg = self.context.any_gpr(self.masm)?;
2341
2342 self.masm.load_ptr(
2344 self.masm
2345 .address_at_vmctx(u32::from(store_context_offset))?,
2346 writable!(limits_reg),
2347 )?;
2348
2349 self.masm.with_scratch::<IntScratch, _>(|masm, scratch| {
2350 masm.load(
2352 masm.address_at_reg(limits_reg, u32::from(fuel_offset))?,
2353 scratch.writable(),
2354 OperandSize::S64,
2355 )?;
2356
2357 masm.add(
2360 scratch.writable(),
2361 scratch.inner(),
2362 RegImm::i64(fuel_at_point),
2363 OperandSize::S64,
2364 )?;
2365
2366 masm.store(
2368 scratch.inner().into(),
2369 masm.address_at_reg(limits_reg, u32::from(fuel_offset))?,
2370 OperandSize::S64,
2371 )
2372 })?;
2373
2374 self.context.free_reg(limits_reg);
2375
2376 Ok(())
2377 }
2378
2379 fn fuel_before_visit_op(&mut self, op: &Operator) -> Result<()> {
2381 if !self.context.reachable {
2382 ensure!(self.fuel_consumed == 0, CodeGenError::illegal_fuel_state());
2385 return Ok(());
2388 }
2389
2390 self.fuel_consumed += self.tunables.operator_cost.cost(op);
2407
2408 match op {
2409 Operator::Unreachable
2410 | Operator::Loop { .. }
2411 | Operator::If { .. }
2412 | Operator::Else { .. }
2413 | Operator::Br { .. }
2414 | Operator::BrIf { .. }
2415 | Operator::BrTable { .. }
2416 | Operator::End
2417 | Operator::Return
2418 | Operator::CallIndirect { .. }
2419 | Operator::Call { .. }
2420 | Operator::ReturnCall { .. }
2421 | Operator::ReturnCallIndirect { .. }
2422 | Operator::Throw { .. }
2423 | Operator::ThrowRef => self.emit_fuel_increment(),
2424 _ => Ok(()),
2425 }
2426 }
2427
2428 fn source_location_before_visit_op(&mut self, offset: u64) -> Result<()> {
2430 let loc = SourceLoc::new(offset as u32);
2431 let rel = self.source_loc_from(loc);
2432 self.source_location.current = self.masm.start_source_loc(rel)?;
2433 Ok(())
2434 }
2435
2436 fn source_location_after_visit_op(&mut self) -> Result<()> {
2438 if self.masm.current_code_offset()? >= self.source_location.current.0 {
2445 self.masm.end_source_loc()?;
2446 }
2447
2448 Ok(())
2449 }
2450
2451 pub(crate) fn emit_atomic_rmw(
2452 &mut self,
2453 arg: &MemArg,
2454 op: RmwOp,
2455 size: OperandSize,
2456 extend: Option<Extend<Zero>>,
2457 ) -> Result<()> {
2458 let memory_index = MemoryIndex::from_u32(arg.memory);
2459 let heap = self.env.resolve_heap(memory_index);
2460 let operand = self.context.pop_to_reg(self.masm, None)?;
2469 if let Some(addr) = self.emit_compute_heap_address_align_checked(&heap, arg, size)? {
2470 self.context
2471 .stack
2472 .push(TypedReg::new(self.env.ptr_type(), addr).into());
2473 self.context.stack.push(operand.into());
2474 self.masm
2475 .atomic_rmw(&mut self.context, size, op, UNTRUSTED_FLAGS, extend)?;
2476 } else {
2477 self.context.free_reg(operand);
2480 }
2481
2482 Ok(())
2483 }
2484
2485 pub(crate) fn emit_atomic_cmpxchg(
2486 &mut self,
2487 arg: &MemArg,
2488 size: OperandSize,
2489 extend: Option<Extend<Zero>>,
2490 ) -> Result<()> {
2491 let replacement = self.context.pop_to_reg(self.masm, None)?;
2514 let expected = self.context.pop_to_reg(self.masm, None)?;
2515
2516 let memory_index = MemoryIndex::from_u32(arg.memory);
2517 let heap = self.env.resolve_heap(memory_index);
2518 if let Some(addr) = self.emit_compute_heap_address_align_checked(&heap, arg, size)? {
2519 self.context
2520 .stack
2521 .push(TypedReg::new(self.env.ptr_type(), addr).into());
2522 self.context.stack.push(expected.into());
2523 self.context.stack.push(replacement.into());
2524
2525 self.masm
2526 .atomic_cas(&mut self.context, size, UNTRUSTED_FLAGS, extend)?;
2527 } else {
2528 self.context.free_reg(expected);
2531 self.context.free_reg(replacement);
2532 }
2533 Ok(())
2534 }
2535
2536 pub fn emit_atomic_wait(&mut self, arg: &MemArg, kind: AtomicWaitKind) -> Result<()> {
2538 let timeout = self.context.pop_to_reg(self.masm, None)?;
2551 let expected = self.context.pop_to_reg(self.masm, None)?;
2552 let addr = self.context.pop_to_reg(self.masm, None)?;
2553
2554 let stack_len = self.context.stack.len();
2556 let builtin = match kind {
2557 AtomicWaitKind::Wait32 => self.env.builtins.memory_atomic_wait32::<M::ABI>()?,
2558 AtomicWaitKind::Wait64 => self.env.builtins.memory_atomic_wait64::<M::ABI>()?,
2559 };
2560 let builtin = self.prepare_builtin_defined_memory_arg(
2561 MemoryIndex::from_u32(arg.memory),
2562 stack_len,
2563 builtin,
2564 )?;
2565
2566 if arg.offset != 0 {
2567 self.masm.checked_uadd(
2568 writable!(addr.reg),
2569 addr.reg,
2570 RegImm::i64(arg.offset as i64),
2571 OperandSize::S64,
2572 TrapCode::HEAP_OUT_OF_BOUNDS,
2573 )?;
2574 }
2575
2576 self.context
2577 .stack
2578 .push(TypedReg::new(WasmValType::I64, addr.reg).into());
2579 self.context.stack.push(expected.into());
2580 self.context.stack.push(timeout.into());
2581
2582 FnCall::emit::<M>(&mut self.env, self.masm, &mut self.context, builtin)?;
2583
2584 Ok(())
2585 }
2586
2587 pub fn emit_atomic_notify(&mut self, arg: &MemArg) -> Result<()> {
2588 let count = self.context.pop_to_reg(self.masm, None)?;
2600 let addr = self.context.pop_to_reg(self.masm, None)?;
2601
2602 let builtin = self.env.builtins.memory_atomic_notify::<M::ABI>()?;
2604 let stack_len = self.context.stack.len();
2605 let builtin = self.prepare_builtin_defined_memory_arg(
2606 MemoryIndex::from_u32(arg.memory),
2607 stack_len,
2608 builtin,
2609 )?;
2610
2611 if arg.offset != 0 {
2612 self.masm.checked_uadd(
2613 writable!(addr.reg),
2614 addr.reg,
2615 RegImm::i64(arg.offset as i64),
2616 OperandSize::S64,
2617 TrapCode::HEAP_OUT_OF_BOUNDS,
2618 )?;
2619 }
2620
2621 self.context
2623 .stack
2624 .push(TypedReg::new(WasmValType::I64, addr.reg).into());
2625 self.context.stack.push(count.into());
2626
2627 FnCall::emit::<M>(&mut self.env, self.masm, &mut self.context, builtin)?;
2628
2629 Ok(())
2630 }
2631
2632 pub fn prepare_builtin_defined_memory_arg(
2633 &mut self,
2634 mem: MemoryIndex,
2635 defined_index_at: usize,
2636 builtin: BuiltinFunction,
2637 ) -> Result<Callee> {
2638 match self.env.translation.module.defined_memory_index(mem) {
2639 Some(defined) => {
2642 self.context
2643 .stack
2644 .insert_many(defined_index_at, &[defined.as_u32().try_into()?]);
2645 Ok(Callee::Builtin(builtin))
2646 }
2647
2648 None => {
2652 let vmimport = self.env.vmoffsets.imported_memories().at(mem);
2653 let vmctx_offset =
2654 vmimport + u32::from(self.env.vmoffsets.ptr.vm_memory_import().vmctx());
2655 let index_offset =
2656 vmimport + u32::from(self.env.vmoffsets.ptr.vm_memory_import().index());
2657 let index_addr = self.masm.address_at_vmctx(index_offset)?;
2658 let index_dst = self.context.reg_for_class(RegClass::Int, self.masm)?;
2659 self.masm
2660 .load(index_addr, writable!(index_dst), OperandSize::S32)?;
2661 self.context
2662 .stack
2663 .insert_many(defined_index_at, &[Val::reg(index_dst, WasmValType::I32)]);
2664 Ok(Callee::BuiltinWithDifferentVmctx(builtin, vmctx_offset))
2665 }
2666 }
2667 }
2668
2669 pub fn prepare_builtin_defined_table_arg(
2671 &mut self,
2672 table: TableIndex,
2673 defined_index_at: usize,
2674 builtin: BuiltinFunction,
2675 ) -> Result<Callee> {
2676 match self.env.translation.module.defined_table_index(table) {
2677 Some(defined) => {
2678 self.context
2679 .stack
2680 .insert_many(defined_index_at, &[defined.as_u32().try_into()?]);
2681 Ok(Callee::Builtin(builtin))
2682 }
2683 None => {
2684 let vmimport = self.env.vmoffsets.imported_tables().at(table);
2685 let vmctx_offset =
2686 vmimport + u32::from(self.env.vmoffsets.ptr.vm_table_import().vmctx());
2687 let index_offset =
2688 vmimport + u32::from(self.env.vmoffsets.ptr.vm_table_import().index());
2689 let index_addr = self.masm.address_at_vmctx(index_offset)?;
2690 let index_dst = self.context.reg_for_class(RegClass::Int, self.masm)?;
2691 self.masm
2692 .load(index_addr, writable!(index_dst), OperandSize::S32)?;
2693 self.context
2694 .stack
2695 .insert_many(defined_index_at, &[Val::reg(index_dst, WasmValType::I32)]);
2696 Ok(Callee::BuiltinWithDifferentVmctx(builtin, vmctx_offset))
2697 }
2698 }
2699 }
2700}
2701
2702pub fn control_index(depth: u32, control_length: usize) -> Result<usize> {
2705 (control_length - 1)
2706 .checked_sub(depth as usize)
2707 .ok_or_else(|| format_err!(CodeGenError::control_frame_expected()))
2708}